mirror of https://github.com/BOINC/boinc.git
*** empty log message ***
svn path=/trunk/boinc/; revision=3688
This commit is contained in:
parent
cb3fbf95ee
commit
57f2aec66d
|
@ -14049,3 +14049,35 @@ David 21 June 2004
|
|||
|
||||
html/inc/
|
||||
result.inc
|
||||
|
||||
David 22 June 2004
|
||||
- client: handle a "down for maintenance" RPC reply as a failure,
|
||||
i.e. do a backoff on the project.
|
||||
This should hopefully fix a problem where if
|
||||
a project is down, and a client has a result to report,
|
||||
it will do an RPC every second, creating an overload on the server.
|
||||
Do this by adding a new optional <project_is_down/> element
|
||||
to scheduler replies.
|
||||
- If get a <request_delay> from scheduler,
|
||||
don't let that override the current min_rpc_time;
|
||||
instead, take the max of the two.
|
||||
- Change the implementation of trickle messages.
|
||||
Instead of using dedicated trickle_up and trickle_down DB tables,
|
||||
use more general msg_from_host and msg_to_host tables.
|
||||
These will be used also for data management,
|
||||
i.e. to send upoad or download requests to specific hosts.
|
||||
|
||||
client/
|
||||
cs_scheduler.C
|
||||
scheduler_op.C,h
|
||||
db/
|
||||
boinc_db.C,h
|
||||
schema.sql
|
||||
lib/
|
||||
error_numbers.h
|
||||
sched/
|
||||
assimilator.C
|
||||
handle_request.C
|
||||
main.C
|
||||
server_types.C,h
|
||||
trickle_handler.C
|
||||
|
|
|
@ -489,14 +489,23 @@ int CLIENT_STATE::handle_scheduler_reply(
|
|||
project->user_total_credit = sr.user_total_credit;
|
||||
project->user_expavg_credit = sr.user_expavg_credit;
|
||||
project->user_create_time = sr.user_create_time;
|
||||
|
||||
if (sr.request_delay) {
|
||||
time_t x = time(0) + sr.request_delay;
|
||||
if (x > project->min_rpc_time) project->min_rpc_time = x;
|
||||
}
|
||||
|
||||
if (strlen(sr.message)) {
|
||||
sprintf(buf, "Message from server: %s", sr.message);
|
||||
int prio = (!strcmp(sr.message_priority, "high"))?MSG_ERROR:MSG_INFO;
|
||||
show_message(project, buf, prio);
|
||||
}
|
||||
|
||||
if (sr.request_delay) {
|
||||
project->min_rpc_time = time(0) + sr.request_delay;
|
||||
// if project is down, return error (so that we back off)
|
||||
// and don't do anything else
|
||||
//
|
||||
if (sr.project_is_down) {
|
||||
return ERR_PROJECT_DOWN;
|
||||
}
|
||||
|
||||
project->host_total_credit = sr.host_total_credit;
|
||||
|
|
|
@ -477,9 +477,8 @@ bool SCHEDULER_OP::poll() {
|
|||
project->write_account_file();
|
||||
}
|
||||
} else {
|
||||
if (retval) {
|
||||
backoff(project, "Can't parse scheduler reply");
|
||||
} else {
|
||||
switch (retval) {
|
||||
case 0:
|
||||
// if we asked for work and didn't get any,
|
||||
// back off this project
|
||||
//
|
||||
|
@ -489,6 +488,13 @@ bool SCHEDULER_OP::poll() {
|
|||
project->nrpc_failures = 0;
|
||||
project->min_rpc_time = 0;
|
||||
}
|
||||
break;
|
||||
case ERR_PROJECT_DOWN:
|
||||
backoff(project, "Project is down");
|
||||
break;
|
||||
default:
|
||||
backoff(project, "Can't parse scheduler reply");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -576,6 +582,7 @@ int SCHEDULER_REPLY::parse(FILE* in, PROJECT* project) {
|
|||
code_sign_key = 0;
|
||||
code_sign_key_signature = 0;
|
||||
trickle_up_ack = false;
|
||||
project_is_down = false;
|
||||
|
||||
p = fgets(buf, 256, in);
|
||||
if (!p) {
|
||||
|
@ -670,6 +677,8 @@ int SCHEDULER_REPLY::parse(FILE* in, PROJECT* project) {
|
|||
continue;
|
||||
} else if (match_tag(buf, "<trickle_up_ack/>")) {
|
||||
trickle_up_ack = true;
|
||||
} else if (match_tag(buf, "<project_is_down/>")) {
|
||||
project_is_down = true;
|
||||
} else if (parse_str(buf, "<email_hash>", project->email_hash, sizeof(project->email_hash))) {
|
||||
continue;
|
||||
} else if (parse_str(buf, "<cross_project_id>", project->cross_project_id, sizeof(project->cross_project_id))) {
|
||||
|
|
|
@ -112,6 +112,7 @@ struct SCHEDULER_REPLY {
|
|||
char* code_sign_key;
|
||||
char* code_sign_key_signature;
|
||||
bool trickle_up_ack;
|
||||
bool project_is_down;
|
||||
|
||||
SCHEDULER_REPLY();
|
||||
~SCHEDULER_REPLY();
|
||||
|
|
|
@ -59,8 +59,8 @@ void TEAM::clear() {memset(this, 0, sizeof(*this));}
|
|||
void HOST::clear() {memset(this, 0, sizeof(*this));}
|
||||
void RESULT::clear() {memset(this, 0, sizeof(*this));}
|
||||
void WORKUNIT::clear() {memset(this, 0, sizeof(*this));}
|
||||
void TRICKLE_UP::clear() {memset(this, 0, sizeof(*this));}
|
||||
void TRICKLE_DOWN::clear() {memset(this, 0, sizeof(*this));}
|
||||
void MSG_FROM_HOST::clear() {memset(this, 0, sizeof(*this));}
|
||||
void MSG_TO_HOST::clear() {memset(this, 0, sizeof(*this));}
|
||||
|
||||
DB_PLATFORM::DB_PLATFORM() : DB_BASE(boinc_db, "platform"){}
|
||||
DB_CORE_VERSION::DB_CORE_VERSION() : DB_BASE(boinc_db, "core_version"){}
|
||||
|
@ -71,8 +71,8 @@ DB_TEAM::DB_TEAM() : DB_BASE(boinc_db, "team"){}
|
|||
DB_HOST::DB_HOST() : DB_BASE(boinc_db, "host"){}
|
||||
DB_WORKUNIT::DB_WORKUNIT() : DB_BASE(boinc_db, "workunit"){}
|
||||
DB_RESULT::DB_RESULT() : DB_BASE(boinc_db, "result"){}
|
||||
DB_TRICKLE_UP::DB_TRICKLE_UP() : DB_BASE(boinc_db, "trickle_up"){}
|
||||
DB_TRICKLE_DOWN::DB_TRICKLE_DOWN() : DB_BASE(boinc_db, "trickle_down"){}
|
||||
DB_MSG_FROM_HOST::DB_MSG_FROM_HOST() : DB_BASE(boinc_db, "msg_from_host"){}
|
||||
DB_MSG_TO_HOST::DB_MSG_TO_HOST() : DB_BASE(boinc_db, "msg_to_host"){}
|
||||
|
||||
int DB_PLATFORM::get_id() {return id;}
|
||||
int DB_CORE_VERSION::get_id() {return id;}
|
||||
|
@ -83,8 +83,8 @@ int DB_TEAM::get_id() {return id;}
|
|||
int DB_HOST::get_id() {return id;}
|
||||
int DB_WORKUNIT::get_id() {return id;}
|
||||
int DB_RESULT::get_id() {return id;}
|
||||
int DB_TRICKLE_UP::get_id() {return id;}
|
||||
int DB_TRICKLE_DOWN::get_id() {return id;}
|
||||
int DB_MSG_FROM_HOST::get_id() {return id;}
|
||||
int DB_MSG_TO_HOST::get_id() {return id;}
|
||||
|
||||
void DB_PLATFORM::db_print(char* buf){
|
||||
sprintf(buf,
|
||||
|
@ -533,52 +533,51 @@ int DB_RESULT::insert() {
|
|||
return DB_BASE::insert();
|
||||
}
|
||||
|
||||
void DB_TRICKLE_UP::db_print(char* buf) {
|
||||
void DB_MSG_FROM_HOST::db_print(char* buf) {
|
||||
ESCAPE(xml);
|
||||
sprintf(buf,
|
||||
"create_time=%d, send_time=%d, "
|
||||
"resultid=%d, appid=%d, hostid=%d, "
|
||||
"hostid=%d, variety=%d, "
|
||||
"handled=%d, xml='%s'",
|
||||
create_time, send_time,
|
||||
resultid, appid, hostid,
|
||||
hostid, variety,
|
||||
handled, xml
|
||||
);
|
||||
UNESCAPE(xml);
|
||||
}
|
||||
|
||||
void DB_TRICKLE_UP::db_parse(MYSQL_ROW& r) {
|
||||
void DB_MSG_FROM_HOST::db_parse(MYSQL_ROW& r) {
|
||||
int i=0;
|
||||
clear();
|
||||
id = atol(r[i++]);
|
||||
create_time = atol(r[i++]);
|
||||
send_time = atol(r[i++]);
|
||||
resultid = atol(r[i++]);
|
||||
appid = atol(r[i++]);
|
||||
hostid = atol(r[i++]);
|
||||
variety = atol(r[i++]);
|
||||
handled = atoi(r[i++]);
|
||||
strcpy2(xml, r[i++]);
|
||||
}
|
||||
|
||||
void DB_TRICKLE_DOWN::db_print(char* buf) {
|
||||
void DB_MSG_TO_HOST::db_print(char* buf) {
|
||||
ESCAPE(xml);
|
||||
sprintf(buf,
|
||||
"create_time=%d, "
|
||||
"resultid=%d, hostid=%d, "
|
||||
"hostid=%d, variety=%d, "
|
||||
"handled=%d, xml='%s'",
|
||||
create_time,
|
||||
resultid, hostid,
|
||||
hostid, variety,
|
||||
handled, xml
|
||||
);
|
||||
UNESCAPE(xml);
|
||||
}
|
||||
|
||||
void DB_TRICKLE_DOWN::db_parse(MYSQL_ROW& r) {
|
||||
void DB_MSG_TO_HOST::db_parse(MYSQL_ROW& r) {
|
||||
int i=0;
|
||||
clear();
|
||||
id = atol(r[i++]);
|
||||
create_time = atol(r[i++]);
|
||||
resultid = atol(r[i++]);
|
||||
hostid = atol(r[i++]);
|
||||
variety = atol(r[i++]);
|
||||
handled = atol(r[i++]);
|
||||
strcpy2(xml, r[i++]);
|
||||
}
|
||||
|
|
|
@ -406,23 +406,22 @@ struct RESULT {
|
|||
void clear();
|
||||
};
|
||||
|
||||
struct TRICKLE_UP {
|
||||
struct MSG_FROM_HOST {
|
||||
int id;
|
||||
int create_time;
|
||||
int send_time; // when API call was made
|
||||
int resultid;
|
||||
int appid;
|
||||
int hostid;
|
||||
int variety; // project-defined; generally app ID
|
||||
bool handled; // trickle handler has processed this
|
||||
char xml[LARGE_BLOB_SIZE];
|
||||
void clear();
|
||||
};
|
||||
|
||||
struct TRICKLE_DOWN {
|
||||
struct MSG_TO_HOST {
|
||||
int id;
|
||||
int create_time;
|
||||
int resultid;
|
||||
int hostid;
|
||||
int variety; // project-defined; generally app ID
|
||||
bool handled; // scheduler has sent this
|
||||
char xml[LARGE_BLOB_SIZE];
|
||||
void clear();
|
||||
|
@ -522,17 +521,17 @@ public:
|
|||
void operator=(WORKUNIT& w) {WORKUNIT::operator=(w);}
|
||||
};
|
||||
|
||||
class DB_TRICKLE_UP : public DB_BASE, public TRICKLE_UP {
|
||||
class DB_MSG_FROM_HOST : public DB_BASE, public MSG_FROM_HOST {
|
||||
public:
|
||||
DB_TRICKLE_UP();
|
||||
DB_MSG_FROM_HOST();
|
||||
int get_id();
|
||||
void db_print(char*);
|
||||
void db_parse(MYSQL_ROW &row);
|
||||
};
|
||||
|
||||
class DB_TRICKLE_DOWN : public DB_BASE, public TRICKLE_DOWN {
|
||||
class DB_MSG_TO_HOST : public DB_BASE, public MSG_TO_HOST {
|
||||
public:
|
||||
DB_TRICKLE_DOWN();
|
||||
DB_MSG_TO_HOST();
|
||||
int get_id();
|
||||
void db_print(char*);
|
||||
void db_parse(MYSQL_ROW &row);
|
||||
|
|
|
@ -220,23 +220,22 @@ create table result (
|
|||
primary key (id)
|
||||
);
|
||||
|
||||
create table trickle_up (
|
||||
create table msg_from_host (
|
||||
id integer not null auto_increment,
|
||||
create_time integer not null,
|
||||
send_time integer not null,
|
||||
resultid integer not null,
|
||||
appid integer not null,
|
||||
hostid integer not null,
|
||||
variety integer not null,
|
||||
handled smallint not null,
|
||||
xml text,
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table trickle_down (
|
||||
create table msg_to_host (
|
||||
id integer not null auto_increment,
|
||||
create_time integer not null,
|
||||
resultid integer not null,
|
||||
hostid integer not null,
|
||||
variety integer not null,
|
||||
handled smallint not null,
|
||||
xml text,
|
||||
primary key (id)
|
||||
|
|
|
@ -2,28 +2,42 @@
|
|||
require_once("docutil.php");
|
||||
page_head("Result scheduling");
|
||||
echo "
|
||||
<h2>Goals and motivation</h2>
|
||||
|
||||
<p>The BOINC client result computation scheduling aims to achieve the
|
||||
following goals:</p>
|
||||
This document describes BOINC's policies for the following:
|
||||
|
||||
<ul>
|
||||
<li> CPU scheduling policy: what result to run when.
|
||||
<li> Work fetch policy: when to contact scheduling servers,
|
||||
and which one(s) to contact.
|
||||
</ul>
|
||||
|
||||
<h2>CPU scheduling</h2>
|
||||
|
||||
<p>CPU scheduling aims to achieve the following goals
|
||||
(decreasing priority):</p>
|
||||
|
||||
<ol>
|
||||
|
||||
<li><b>Efficiently and effectively use system resources.</b><br>This
|
||||
is clearly desirable.</li>
|
||||
<li>
|
||||
<b>Maximize CPU utilization</b>
|
||||
|
||||
<li><b>Maintain a sense of minimum variety of projects for which
|
||||
results are computed in a given amount of time.</b><br>A user
|
||||
participating in multiple projects can get bored seeing his computer
|
||||
work only on one project for a long time.</li>
|
||||
<li>
|
||||
<b>Respect the resource share allocation for each project.</b>
|
||||
A project's resource share represents how much computing resources
|
||||
(CPU time, network bandwith, storage space) a user wants to allocate
|
||||
to the project relative to the resources allocated to all of the other
|
||||
projects in which he is participating. The client should respect this
|
||||
allocation to be faithful to the user. In the case of CPU time, the
|
||||
result computation scheduling should achieve the expected time shares
|
||||
over a reasonable time period.
|
||||
|
||||
<li><b>Respect the resource share allocation for each
|
||||
project.</b><br>The user specifies the resource shares and thus
|
||||
expects them to be honored.</li>
|
||||
<li>
|
||||
<b>Satisfy result deadlines if possible.</b>
|
||||
|
||||
</ol>
|
||||
|
||||
<p>The motivation for the second goal stems from the potential
|
||||
<li>
|
||||
<b>Given a 'minimum variety' parameter MV (seconds),
|
||||
reschedule CPUs at least once every MV seconds.</b>
|
||||
The motivation for this goal stems from the potential
|
||||
orders-of-magnitude differences in expected completion time for
|
||||
results from different projects. Some projects will have results that
|
||||
complete in hours, while other projects may have results that take
|
||||
|
@ -33,334 +47,272 @@ short-running result computations stuck behind projects with
|
|||
long-running result computations. A participant in multiple projects
|
||||
will expect to see his computer work on each of these projects in a
|
||||
reasonable time period, not just the project with the long-running
|
||||
result computations.</p>
|
||||
result computations.
|
||||
|
||||
<p>A project's resource share represents how much computing resources
|
||||
(CPU time, network bandwith, storage space) a user wants to allocate
|
||||
to the project relative to the resources allocated to all of the other
|
||||
projects in which he is participating. The client should respect this
|
||||
allocation to be faithful to the user. In the case of CPU time, the
|
||||
result computation scheduling should achieve the expected time shares
|
||||
over a reasonable time period.</p>
|
||||
|
||||
<p>At the same time, the scheduler RPC policy needs to complement the
|
||||
result scheduling. We have the following goals for this policy:</p>
|
||||
|
||||
<ol>
|
||||
|
||||
<li><b>Have enough work to keep all CPUs busy</b></li>
|
||||
|
||||
<li><b>Have enough work to provide for minimum variety of
|
||||
projects</b></li>
|
||||
|
||||
<li><b>Respect work_buf_min and work_buf_max</b></li>
|
||||
|
||||
</ol>
|
||||
|
||||
<h2>BOINC client result computation scheduling</h2>
|
||||
|
||||
<p>We address the goals using result preemption. After a given time
|
||||
period, the client decides on a new set of projects for which results
|
||||
will be computed in the next time period. This decision will consider
|
||||
the projects' resource shares by tracking the debt owed to a project.
|
||||
The debt to a project accrues according to the project's resource
|
||||
share, and is paid off when CPU time is devoted to the project.</p>
|
||||
|
||||
<p>A consequence of result preemption is that projects can have
|
||||
multiple active result computations at a given time. For example,
|
||||
consider a two processor system participating in two projects, A and
|
||||
B, with resource shares 75% and 25%, respectively. Ideally, one
|
||||
processor will run a result computation for A, while the other
|
||||
processor will switch between running result computations for A and B.
|
||||
Thus, A will have two active result computations. This consequence
|
||||
implies a desirable property of the result preemption scheme: <b>that
|
||||
the number of active result computations for a project be
|
||||
minimized.</b> For example, it's better to have one result from
|
||||
<li>
|
||||
<b>Minimize mean time to completion for results.</b>
|
||||
This means that the number of active result computations for a project should be minimized.
|
||||
For example, it's better to have one result from
|
||||
project P complete in time T than to have two results from project P
|
||||
simultaneously complete in time 2T. Maintaining more active result
|
||||
computations than necessary increases the mean-time-to-completion if
|
||||
the client switches between these active result computations.</p>
|
||||
simultaneously complete in time 2T.
|
||||
|
||||
<p>We will attempt to minimize the number of active result
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
A result is 'active' if there is a slot directory for it.
|
||||
A consequence of result preemption is that there can
|
||||
be more active results than CPUs.
|
||||
|
||||
|
||||
<h3>Debt</h3>
|
||||
|
||||
<p>
|
||||
The notion of 'debt' is used to respect the resource share allocation
|
||||
for each project.
|
||||
The debt to a project represents the amount of work
|
||||
(in CPU time) we owe it.
|
||||
Debt is decreased when CPU time is devoted to a project.
|
||||
We increase the debt to a project according to the
|
||||
total amount of work done in a time period scaled by the project's
|
||||
resource share.
|
||||
|
||||
<p>
|
||||
For example, consider a system participating in two projects, A and B,
|
||||
with resource shares 75% and 25%, respectively.
|
||||
Suppose in some time period, the system devotes 25 minutes of CPU time to project A
|
||||
and 15 minutes of CPU time to project B.
|
||||
We decrease the debt to A by 20 minutes and increase it by 30 minutes (75% of 25 + 15).
|
||||
So the debt increases overall.
|
||||
This makes sense because we expected to devote a
|
||||
larger percentage of the system resources to project A than it
|
||||
actually got.
|
||||
|
||||
<p>
|
||||
The choice of projects for which to start result computations
|
||||
can simply follow the debt ordering of the projects.
|
||||
The algorithm computes the 'anticipated debt' to a project
|
||||
(the debt we expect to owe after the time period expires)
|
||||
as it chooses result computations to run.
|
||||
|
||||
<h3>A sketch of the CPU scheduling algorithm</h3>
|
||||
|
||||
<p>
|
||||
This algorithm is run:
|
||||
<ul>
|
||||
<li> Whenever a CPU is free
|
||||
<li> Whenever a new result arrives (via scheduler RPC)
|
||||
<li> Whenever it hasn't run for MV seconds
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
We will attempt to minimize the number of active result
|
||||
computations for a project by dynamically choosing results to compute
|
||||
from a global pool. When we allocate CPU time to project, we will
|
||||
choose results to compute intelligently: choose already running tasks
|
||||
first, then preempted tasks, and only choose to start a new result
|
||||
computation in the last resort. This will not guarantee the above
|
||||
property, but we hope it will be close to achieving it.</p>
|
||||
|
||||
<h3>A sketch of the result preemption algorithm</h3>
|
||||
|
||||
<p>The algorithm requires that a time period length be defined (e.g.
|
||||
one hour). The result preemption algorithm is run at the beginning of
|
||||
each period. It proceeds as follows:</p>
|
||||
from a global pool.
|
||||
When we allocate CPU time to project,
|
||||
we will choose already running tasks first,
|
||||
then preempted tasks, and only choose to start a new result
|
||||
computation in the last resort.
|
||||
This will not guarantee the above
|
||||
property, but we hope it will be close to achieving it.
|
||||
|
||||
<ol>
|
||||
|
||||
<li>Pay off debts to projects according to the amount of work done for
|
||||
the projects in the last period.</li>
|
||||
<li>Decrease debts to projects according to the amount of work done for
|
||||
the projects in the last period.
|
||||
|
||||
<li>Accrue debts to projects according to the projects' resource
|
||||
shares.</li>
|
||||
<li>Increase debts to projects according to the projects' resource shares.
|
||||
|
||||
<li>Let the expected future debt for each project be initialized to
|
||||
its actual debt.</li>
|
||||
<li>Let the anticipated debt for each project be initialized to
|
||||
its current debt.
|
||||
|
||||
<li>Repeat until we decide on a result to compute for each
|
||||
processor:</li>
|
||||
<li>Repeat until we decide on a result to compute for each processor:
|
||||
|
||||
<ol>
|
||||
|
||||
<li>Choose the project that has the largest expected future debt and a
|
||||
ready-to-compute result.</li>
|
||||
<li>Choose the project that has the largest anticipated debt and a
|
||||
ready-to-compute result.
|
||||
|
||||
<li>Decrease the expected future debt of the project by the amount we
|
||||
expect to pay off, and return the project back into consideration for
|
||||
running on another processor.</li>
|
||||
<li>Decrease the anticipated debt of the project by the expected amount of CPU time.
|
||||
|
||||
</ol>
|
||||
|
||||
<li>Preempt the current result computations with the new ones.</li>
|
||||
<li>Preempt current result computations, and start new ones.
|
||||
|
||||
</ol>
|
||||
|
||||
<p>Because result computations may finish before the time period
|
||||
expires, we need to account for such a gap in a project's debt
|
||||
payment. So, we need to also keep track of the amount of work done
|
||||
during the current time period for each project as results finish.
|
||||
This accounting should be reset for each time period.</p>
|
||||
|
||||
<p>Finally, the starting of new result computations in the middle of a
|
||||
time period needs to use this accounting instead of the expected
|
||||
future debts that were estimated at the beginning of the time period.
|
||||
Otherwise, it will be similar to the decision of choosing which tasks
|
||||
to run at the beginning of a time period.</p>
|
||||
|
||||
<h3>Pseudocode</h3>
|
||||
|
||||
<p>We'll initialize <tt>total_work_done_this_period</tt> to
|
||||
<tt>num_cpus * period_length</tt>.</p>
|
||||
|
||||
<pre>
|
||||
preempt_apps(): // called after every period_length
|
||||
data structures:
|
||||
ACTIVE_TASK:
|
||||
double cpu_at_last_schedule_point
|
||||
double current_cpu_time
|
||||
scheduler_state:
|
||||
PREEMPTED
|
||||
RUNNING
|
||||
next_scheduler_state // temp
|
||||
PROJECT:
|
||||
double work_done_this_period // temp
|
||||
double debt
|
||||
double anticipated_debt // temp
|
||||
bool has_runnable_result
|
||||
|
||||
// finish accounting
|
||||
foreach T in running_tasks:
|
||||
T.project.work_done_this_period += T.work_done_this_period
|
||||
total_work_done_this_period += T.work_done_this_period
|
||||
schedule_cpus():
|
||||
|
||||
foreach project P
|
||||
P.work_done_this_period = 0
|
||||
|
||||
total_work_done_this_period = 0
|
||||
foreach task T that is RUNNING:
|
||||
x = current_cpu_time - T.cpu_at_last_schedule_point
|
||||
T.project.work_done_this_period += x
|
||||
total_work_done_this_period += x
|
||||
|
||||
// pay off and accrue debts
|
||||
foreach P in projects:
|
||||
P.debt += P.resource_share * total_work_done_this_period
|
||||
- P.work_done_this_period
|
||||
|
||||
// make preemption decisions
|
||||
expected_pay_off = total_work_done_this_period / num_cpus
|
||||
|
||||
foreach P in projects:
|
||||
P.expected_future_debt = P.debt
|
||||
to_preempt.addAll(running_tasks) // assume we'll preempt everything at first
|
||||
to_run = ()
|
||||
P.anticipated_debt = P.debt
|
||||
|
||||
foreach task T
|
||||
T.next_scheduler_state = PREEMPTED
|
||||
|
||||
do num_cpus times:
|
||||
found = false
|
||||
do projects.size times:
|
||||
// choose the project with the largest expected future debt
|
||||
P = argmax { P.expected_future_debt } over all P in projects
|
||||
if (some T in to_preempt is for P):
|
||||
// P has a task that ran last period, so just don't preempt it
|
||||
to_preempt.remove(T)
|
||||
T.expected_pay_off = expected_pay_off
|
||||
found = true
|
||||
break
|
||||
if (some T in preempted_tasks is for P):
|
||||
// P has a task that was preempted
|
||||
preempted_tasks.remove(T)
|
||||
to_run.add(T)
|
||||
T.expected_pay_off = expected_pay_off
|
||||
found = true
|
||||
break
|
||||
if (some R in results is for P, not active, and ready to run):
|
||||
T = new ACTIVE_TASK for R
|
||||
to_run.add(T)
|
||||
T.expected_pay_off = expected_pay_off
|
||||
found = true
|
||||
break
|
||||
remove P from consideration in the argmax
|
||||
if found:
|
||||
P.expected_future_debt -= expected_pay_off
|
||||
else:
|
||||
// choose the project with the largest anticipated debt
|
||||
P = argmax { P.anticipated_debt } over all P in projects with runnable result
|
||||
if none:
|
||||
break
|
||||
suspend tasks in to_preempt (reset T.expected_pay_off for each T in to_preempt)
|
||||
run or unsuspend tasks in to_run (and put in running_tasks)
|
||||
if (some T in P is RUNNING):
|
||||
t.next_scheduler_state = RUNNING
|
||||
P.anticipated_debt -= expected_pay_off
|
||||
continue
|
||||
if (some T in P is PREEMPTED):
|
||||
T.next_scheduler_state = RUNNING
|
||||
P.anticipated_debt -= expected_pay_off
|
||||
continue
|
||||
if (some R in results is for P, not active, and ready to run):
|
||||
T = new ACTIVE_TASK for R
|
||||
T.next_scheduler_state = RUNNING
|
||||
P.anticipated_debt -= expected_pay_off
|
||||
|
||||
// reset accounting
|
||||
foreach P in projects:
|
||||
P.work_done_this_period = 0
|
||||
total_work_done_this_period = 0
|
||||
|
||||
----------
|
||||
|
||||
start_apps(): // called at each iteration of the BOINC main loop
|
||||
|
||||
foreach P in projects:
|
||||
// expected_future_debt should account for any tasks that finished
|
||||
// and for tasks that are still running
|
||||
P.expected_future_debt = P.debt - P.work_done_this_period
|
||||
foreach T in running_tasks:
|
||||
T.project.expected_future_debt -= T.expected_pay_off
|
||||
|
||||
to_run = ()
|
||||
while running_tasks.size < num_cpus:
|
||||
do projects.size times:
|
||||
// choose the project with the largest expected future debt
|
||||
P = argmax { P.expected_future_debt } over all P in projects
|
||||
if (some T in preempted_tasks is for P):
|
||||
// P has a task that was preempted
|
||||
preempted_tasks.remove(T)
|
||||
to_run.add(T)
|
||||
T.expected_pay_off = fraction_of_period_left * expected_pay_off
|
||||
found = true
|
||||
break
|
||||
if (some R in results is for P, not active, and ready to run):
|
||||
T = new ACTIVE_TASK for R
|
||||
to_run.add(T)
|
||||
T.expected_pay_off = fraction_of_period_left * expected_pay_off
|
||||
found = true
|
||||
break
|
||||
remove P from consideration in the argmax
|
||||
if found:
|
||||
P.expected_future_debt -= fraction_of_period_left * expected_pay_off
|
||||
else:
|
||||
break
|
||||
run or unsuspend tasks in to_run
|
||||
|
||||
----------
|
||||
|
||||
handle_finished_apps(): // called at each iteration of the BOINC main loop
|
||||
|
||||
foreach T in running_tasks:
|
||||
if T finished:
|
||||
// do some accounting
|
||||
T.project.work_done_this_period += T.work_done_this_period
|
||||
total_work_done_this_period += T.work_done_this_period
|
||||
do other clean up stuff
|
||||
foreach task T
|
||||
if scheduler_state == PREEMPTED and next_scheduler_state = RUNNING
|
||||
unsuspend or run
|
||||
if scheduler_state == RUNNING and next_scheduler_state = PREEMPTED
|
||||
suspend (or kill)
|
||||
|
||||
foreach task T
|
||||
T.cpu_at_last_schedule_point = current_cpu_time
|
||||
</pre>
|
||||
|
||||
<h3>Debt computation</h3>
|
||||
<h2>Work fetch policy</h2>
|
||||
|
||||
<p>The notion of debt is used to respect the resource share allocation
|
||||
for each project. The debt to a project represents the amount of work
|
||||
(in CPU time) we owe to a project. Debt is paid off when CPU time is
|
||||
devoted to a project. We accrue the debt to a project according to the
|
||||
total amount of work done in a time period scaled by the project's
|
||||
resource share.</p>
|
||||
<p>
|
||||
The work fetch policy has the following goal:
|
||||
|
||||
<p>For example, consider a system participating in two projects, A and
|
||||
B, with resource shares 75% and 25%, respectively. Suppose in some
|
||||
time period, the system devotes 25 minutes of CPU time to project A
|
||||
and 15 minutes of CPU time to project B. We decrease the debt to A by
|
||||
20 minutes and accrue it by 30 minutes (75% of 25 + 15). So the debt
|
||||
increases overall. This makes sense because we expected to devote a
|
||||
larger percentage of the system resources to project A than it
|
||||
actually got.</p>
|
||||
<ul>
|
||||
<li>
|
||||
<b>Given a 'connection frequency' parameter 1/T (1/days), have enough
|
||||
work for each project to meet CPU scheduling needs for T days.</b>
|
||||
The client should expect to contact scheduling servers only every T
|
||||
days.
|
||||
So, it should try to maintain between T and 2T days worth of work.
|
||||
</ul>
|
||||
|
||||
<p>The choosing of projects for which to start result computations at
|
||||
the beginning of each time period can simply follow the debt ordering
|
||||
of the projects. The algorithm computes the expected future debt to a
|
||||
project (the debt we expect to owe after the time period expires) as
|
||||
it chooses result computations to run.</p>
|
||||
<h3>When to get work</h3>
|
||||
|
||||
<blockquote>expected future debt = debt - expected pay off * number of
|
||||
tasks to run this period</blockquote>
|
||||
<p>
|
||||
The CPU scheduler needs a minimum number of results from a project
|
||||
in order to respect the project's resource share.
|
||||
We effectively have too little work when the number of results for a
|
||||
project is less than this minimum number.
|
||||
|
||||
<p>However, choosing projects to run in the middle of a time period is
|
||||
a little different. The preemption algorithm expected each of the
|
||||
tasks it started to last for the entire time period. However, when a
|
||||
task finishes in the middle of a time period, the expected future debt
|
||||
to the respective project is an overestimate. We thus change the
|
||||
expected future debt to reflect what has taken place: it is the debt
|
||||
owed to the project at the beginning of the time period, minus the
|
||||
amount of work that has already been done this time period, and minus
|
||||
the amount of work we expect to complete by the end of the time
|
||||
period. When projects have results chosen to run, we decrease the
|
||||
expected future debt by the amount of work we expect to be done for
|
||||
the project in the remainder of the time period.</p>
|
||||
<blockquote>
|
||||
min_results(P) = ceil(ncpus * P.resource_share)
|
||||
</blockquote>
|
||||
|
||||
<blockquote>expected future debt = debt - (work completed + expected
|
||||
pay off of tasks already running this period + expected pay off *
|
||||
fraction of period left * number of new tasks for this
|
||||
period)</blockquote>
|
||||
<p>
|
||||
The client can estimate the amount of time that will elapse until we
|
||||
have too little work for a project.
|
||||
When this length of time is less than T, it is time to get more work.
|
||||
|
||||
<h2>Scheduler RPC policy</h2>
|
||||
|
||||
<p>The client should get more work when either of the following are
|
||||
true:</p>
|
||||
<h3>A sketch of the work fetch algorithm</h3>
|
||||
|
||||
<p>
|
||||
This algorithm determines if a project needs more work. If a project
|
||||
does need work, then the amount of work it needs is computed.
|
||||
It is called whenever the client can make a scheduler RPC.
|
||||
<p>
|
||||
<ol>
|
||||
|
||||
<li>The client will have no work in at most work_buf_min days.</li>
|
||||
|
||||
<li>The client will not have enough work for a project to get its fair
|
||||
share of computation time (according to its resource share)</li>
|
||||
|
||||
<li>The client will have fewer than num_cpus tasks</li>
|
||||
|
||||
<li>
|
||||
For each project
|
||||
<ol>
|
||||
<li>
|
||||
If the number of results for the project is too few
|
||||
<ol>
|
||||
<li>
|
||||
Set the project's work request to 2T
|
||||
<li>
|
||||
Return NEED WORK IMMEDIATELY
|
||||
</ol>
|
||||
<li>
|
||||
For all but the top (min_results - 1) results with the longest
|
||||
expected time to completion:
|
||||
<ol>
|
||||
<li>
|
||||
Sum the expected completion time of the result scaled by the work rate
|
||||
and the project's resource share
|
||||
</ol>
|
||||
<li>
|
||||
If the sum S is less than T
|
||||
<ol>
|
||||
<li>Set the project's work request to 2T - S
|
||||
<li>Return NEED WORK
|
||||
</ol>
|
||||
<li>
|
||||
Else, set the project's work request to 0 and return DON'T NEED WORK
|
||||
</ol>
|
||||
</ol>
|
||||
|
||||
<p>Ignoring the second case can cause long running result computations
|
||||
to monopolize the CPU, even with result preemption. For example,
|
||||
suppose a project has work units that finish on the order of months.
|
||||
Then, when work_buf_min is on the order of days, the client will never
|
||||
think it is out of work. However, projects with shorter result
|
||||
computations may run out of work. So, even with preemption, we cannot
|
||||
have minimum variety.</p>
|
||||
<p>
|
||||
The mechanism for actually getting work checks if a project has a
|
||||
non-zero work request and if so, makes the scheduler RPC call to
|
||||
request the work.
|
||||
|
||||
<h3>need_to_get_work()</h3>
|
||||
|
||||
<p>The second case (running out of work for one project) is addressed
|
||||
by capping the amount of work counted for a project. We cap it by the
|
||||
total amount of work that can be done in min_work_buf_secs, scaled by
|
||||
the project's resource share. Thus, the client will get more work when
|
||||
any one project has too little work.</p>
|
||||
|
||||
<p>The case of having fewer results than CPUs is addressed by
|
||||
\"packing\" results into CPU \"bins\".</p>
|
||||
<h3>Pseudocode</h3>
|
||||
|
||||
<pre>
|
||||
need_to_get_work():
|
||||
data structures:
|
||||
PROJECT:
|
||||
double work_request_days
|
||||
|
||||
num_cpus_busy = 0
|
||||
total_work_secs = 0
|
||||
work_secs_for_one_cpu = 0
|
||||
foreach P in projects:
|
||||
P.avail_work_secs = 0
|
||||
check_work_needed(Project P):
|
||||
|
||||
sort results in order of decreasing estimated_cpu_time
|
||||
if num_results(P) < min_results(P):
|
||||
P.work_request_days = 2T
|
||||
return NEED_WORK_IMMEDIATELY
|
||||
|
||||
// pack results into CPU bins
|
||||
foreach R in results:
|
||||
result_work_secs = estimated_cpu_time(R)
|
||||
work_secs_for_one_cpu += result_work_secs
|
||||
R.project.avail_work_secs += result_work_secs
|
||||
if work_secs_for_one_cpu >= min_work_buf_secs
|
||||
work_secs_for_one_cpu = 0
|
||||
num_cpus_busy += 1
|
||||
top_results = top (min_results(P) - 1) results of P by expected
|
||||
completion time
|
||||
|
||||
work_remaining = 0
|
||||
foreach result R of P that is not in top_results:
|
||||
work_remaining += R.expected_completion_time
|
||||
work_remaining *= P.resource_share * active_frac / ncpus
|
||||
|
||||
if work_remaining < T:
|
||||
P.work_request_days = 2T - work_remaining / seconds_per_day
|
||||
return NEED_WORK
|
||||
else:
|
||||
P.work_request_days = 0
|
||||
return DONT_NEED_WORK
|
||||
|
||||
// count total amount of work, but cap amount any one project contributes
|
||||
// to this total
|
||||
foreach P in projects:
|
||||
total_work_secs += min { P.avail_work_secs,
|
||||
P.resource_share * min_work_buf_secs * num_cpus }
|
||||
|
||||
return (num_cpus_busy < num_cpus)
|
||||
|| (total_work_secs < min_work_secs * num_cpus)
|
||||
</pre>
|
||||
|
||||
<p>XXX it will be useful to know what caused this predicate to return
|
||||
true, so maybe it should be split into separate predicates.</p>
|
||||
|
||||
<p>XXX also need to factor in if we are able to currently contact a
|
||||
project (according to min_rpc_time).</p>
|
||||
";
|
||||
page_tail();
|
||||
?>
|
||||
|
|
|
@ -97,6 +97,8 @@ void update_average(
|
|||
avg_time = now;
|
||||
}
|
||||
|
||||
#define CREDIT_HALF_LIFE (SECONDS_IN_DAY*7)
|
||||
|
||||
"),"
|
||||
</pre>
|
||||
<hr noshade size=1>
|
||||
|
|
183
doc/db_dump.php
183
doc/db_dump.php
|
@ -4,62 +4,94 @@ page_head("Downloadable statistics data");
|
|||
echo "
|
||||
|
||||
<p>
|
||||
BOINC projects can export data describing teams, users and hosts.
|
||||
This data is exported in downloadable XML files,
|
||||
and can be summarized and represented as Web pages.
|
||||
Some examples are listed at
|
||||
BOINC projects can export <b>statistics data</b>
|
||||
describing teams, users and hosts.
|
||||
This data can be imported and used to produce
|
||||
web sites that show statistics and leaderboards
|
||||
for one or more BOINC projects.
|
||||
Examples of such sites are listed at
|
||||
<a href=http://setiboinc.ssl.berkeley.edu/ap/stats.php>
|
||||
http://setiboinc.ssl.berkeley.edu/ap/stats.php</a>.
|
||||
|
||||
<p>
|
||||
The data is presented in several 'views':
|
||||
teams ordered by credit, teams ordered by ID, etc.
|
||||
Each view is available in two forms:
|
||||
<ul>
|
||||
<li> As a single file.
|
||||
<li>
|
||||
Divided into a number of files,
|
||||
each containing a limited number of records.
|
||||
This lets you get a single record or range of records efficiently.
|
||||
For views that are ordered by ID,
|
||||
each file contains a fixed-size segment of the ID range,
|
||||
not a fixed number of records.
|
||||
If the database ID allocation has gaps,
|
||||
files will have fewer than this number of records.
|
||||
</ul>
|
||||
Statistics data is exported in XML-format files.
|
||||
These files are contained in a download directory,
|
||||
linked to from the project's web site.
|
||||
A project can decide what data to export,
|
||||
and how it is divided into files.
|
||||
This is described by a file <b>db_dump.xml</b> of the following form:
|
||||
";
|
||||
echo html_text("
|
||||
<boinc_db_dump_spec>
|
||||
<enumeration>
|
||||
<table>x</table>
|
||||
<filename>x</filename>
|
||||
<sort>x</sort>
|
||||
<output>
|
||||
<recs_per_file>n</recs_per_file>
|
||||
<detail/>
|
||||
<compression>x</compression>
|
||||
</output>
|
||||
...
|
||||
</enumeration>
|
||||
...
|
||||
</boinc_db_dump_spec>
|
||||
");
|
||||
echo "
|
||||
An 'enumeration' is a listing of particular table.
|
||||
The fields are:
|
||||
";
|
||||
list_start();
|
||||
list_item("table", "'user', 'host' or 'team'");
|
||||
list_item("filename", "The base filename.");
|
||||
list_item("sort", "The sorting criterion:
|
||||
'total_credit', 'expavg_credit', or 'id'.
|
||||
'id' is the default."
|
||||
);
|
||||
list_end();
|
||||
echo
|
||||
"An 'output' is a file or set of files containing an enumeration.
|
||||
The fields are:";
|
||||
list_start();
|
||||
list_item("recs_per_file",
|
||||
"If present, the listing is divided into multiple files
|
||||
with the given number of records per file.
|
||||
The file names have the form xxx_N,
|
||||
where xxx is the base filename.
|
||||
For views that are ordered by ID,
|
||||
each file contains a fixed-size segment of the ID range,
|
||||
not a fixed number of records.
|
||||
If the database ID allocation has gaps,
|
||||
files will have fewer than this number of records.
|
||||
<p>
|
||||
If zero or absent,
|
||||
the listing is written to a single file."
|
||||
);
|
||||
list_item("detail",
|
||||
"If present, records are 'detailed':
|
||||
user records include a list of hosts,
|
||||
and team records include a list of users."
|
||||
);
|
||||
list_end();
|
||||
echo"
|
||||
<p>
|
||||
The entries in a given file are in either 'summary' or 'detail' form.
|
||||
For example, the summary of a team gives its ID, name, and credit,
|
||||
while the detailed form also contains a list of its members.
|
||||
<p>
|
||||
The files are as follows:
|
||||
|
||||
<p>
|
||||
<b>tables.xml</b>
|
||||
<p>
|
||||
For each entity type (team, user, and host) this gives
|
||||
<ul>
|
||||
<li> the total number of records
|
||||
<li> the number of records per file for summary files
|
||||
<li> the number of records per file for detail files
|
||||
</ul>
|
||||
The download directory contains the following files:
|
||||
";
|
||||
list_start();
|
||||
list_item("<b>tables.xml</b>",
|
||||
"This gives the total number of records
|
||||
for each entity type (team, user, and host).
|
||||
It also includes the UNIX time when the files were last generated,
|
||||
and a list of the project's applications,
|
||||
with counts of results in various states.
|
||||
<br>
|
||||
For example:
|
||||
<pre>",
|
||||
<pre>".
|
||||
htmlspecialchars("<tables>
|
||||
<update_time>1046220857</update_time>
|
||||
<nusers_total>127</nusers_total>
|
||||
<nusers_per_file_summary>1000</nusers_per_file_summary>
|
||||
<nusers_per_file_detail>100</nusers_per_file_detail>
|
||||
<nteams_total>14</nteams_total>
|
||||
<nteams_per_file_summary>1000</nteams_per_file_summary>
|
||||
<nteams_per_file_detail>100</nteams_per_file_detail>
|
||||
<nhosts_total>681</nhosts_total>
|
||||
<nhosts_per_file_summary>1000</nhosts_per_file_summary>
|
||||
<nhosts_per_file_detail>100</nhosts_per_file_detail>
|
||||
<applications>
|
||||
<application>
|
||||
<name>setiathome</name>
|
||||
|
@ -70,41 +102,32 @@ htmlspecialchars("<tables>
|
|||
...
|
||||
</applications>
|
||||
</tables>
|
||||
"),
|
||||
"</pre>
|
||||
Other files:
|
||||
";
|
||||
list_start();
|
||||
list_heading("File name", "Contents");
|
||||
list_item(
|
||||
"team_total_credit.xml<br> team_total_credit_N.xml",
|
||||
"Team summaries, ordered by decreasing <a href=credit.php>total credit</a>.
|
||||
The first file is the complete list;
|
||||
the remaining files (for N = 0, 1, ...) is the list
|
||||
in limited-size chunks."
|
||||
").
|
||||
"</pre>");
|
||||
list_item("<b>core_versions.xml</b>",
|
||||
"A list of versions of the core client in the project's database"
|
||||
);
|
||||
list_item("team_expavg_credit.xml<br> team_expavg_credit_N.xml",
|
||||
"Team summaries, ordered by decreasing <a href=credit.php>recent-average credit</a>.");
|
||||
list_item("team_id.xml<br> team_id_N.xml",
|
||||
"Team details, ordered by increasing ID.");
|
||||
list_item("user_total_credit.xml<br> user_total_credit_N.xml",
|
||||
"User summaries, ordered by decreasing total credit.");
|
||||
list_item("user_expavg_credit.xml<br> user_expavg_credit_N.xml",
|
||||
"User summaries, ordered by decreasing recent-average credit.");
|
||||
list_item("user_id.xml, user_id_N.xml",
|
||||
"User details, ordered by increasing ID.");
|
||||
list_item("host_total_credit.xml<br> host_total_credit_N.xml",
|
||||
"Host summaries, ordered by decreasing total credit.");
|
||||
list_item("host_expavg_credit.xml<br> host_expavg_credit_N.xml",
|
||||
"Host summaries, ordered by decreasing recent-average credit.");
|
||||
list_item("host_id.xml<br> host_id_N.xml",
|
||||
"Host details, ordered by increasing ID.");
|
||||
list_item("core_versions.xml", "A list of version of the core client
|
||||
in the project's database");
|
||||
list_end();
|
||||
echo "
|
||||
<p>
|
||||
The format of the various XML elements is as follows:
|
||||
The format of the various XML elements
|
||||
in the output files is as follows.
|
||||
Notes:
|
||||
<ul>
|
||||
<li>
|
||||
<cpid> ('<a href=cpid.php>cross-project identifier</a>')
|
||||
is a unique identifier across multiple projects.
|
||||
Accounts with the same email address on different projects
|
||||
will have the same cross-project identifier
|
||||
(as long as at least one computer is attached to both accounts).
|
||||
<li>
|
||||
All 'expavg_credit' values were computed at some point
|
||||
in the past (given by 'expavg_time').
|
||||
To compute their current values,
|
||||
they must be scaled according to the formula given
|
||||
<a href=credit.php>here</a>.
|
||||
</ul>
|
||||
<p>
|
||||
|
||||
<p>
|
||||
<b>Team summary</b>
|
||||
|
@ -115,6 +138,7 @@ htmlspecialchars("
|
|||
<name>Broadband Reports Team Starfire</name>
|
||||
<total_credit>153402.872429</total_credit>
|
||||
<expavg_credit>503030.483254</expavg_credit>
|
||||
<expavg_time>1087542007.701900</expavg_time>
|
||||
<nusers>14</nusers>
|
||||
</team>
|
||||
"),
|
||||
|
@ -128,6 +152,7 @@ htmlspecialchars("
|
|||
<name>Broadband Reports Team Starfire</name>
|
||||
<total_credit>153402.872429</total_credit>
|
||||
<expavg_credit>503030.483254</expavg_credit>
|
||||
<expavg_time>1087542007.701900</expavg_time>
|
||||
<nusers>14</nusers>
|
||||
<create_time>0</create_time>
|
||||
<name_html>%3Ca%20href%3D%27http%3A%2F%2Fbroadbandreports%2Ecom%2Fforum%2Fseti%2
|
||||
|
@ -139,6 +164,7 @@ g%27%3E</name_html>
|
|||
<name>John Keck</name>
|
||||
<total_credit>42698.813543</total_credit>
|
||||
<expavg_credit>117348.653646</expavg_credit>
|
||||
<expavg_time>1087542007.701900</expavg_time>
|
||||
<teamid>5</teamid>
|
||||
</user>
|
||||
<user>
|
||||
|
@ -146,6 +172,7 @@ g%27%3E</name_html>
|
|||
<name>Liontaur</name>
|
||||
<total_credit>46389.595430</total_credit>
|
||||
<expavg_credit>122936.372641</expavg_credit>
|
||||
<expavg_time>1087542007.701900</expavg_time>
|
||||
<teamid>5</teamid>
|
||||
</user>
|
||||
</team>
|
||||
|
@ -159,18 +186,13 @@ htmlspecialchars("
|
|||
<name>John Keck</name>
|
||||
<total_credit>42698.813543</total_credit>
|
||||
<expavg_credit>117348.653646</expavg_credit>
|
||||
<expavg_time>1087542007.701900</expavg_time>
|
||||
<cpid>283472938743489759837498347</cpid>
|
||||
[ <teamid>5</teamid> ]
|
||||
[ <has_profile/> ]
|
||||
</user>
|
||||
"), "</pre>
|
||||
<p>
|
||||
Note: <cpid> ('<a href=cpid.php>cross-project identifier</a>')
|
||||
is a unique identifier across multiple projects.
|
||||
Accounts with the same email address on different projects
|
||||
will have the same cross-project identifier
|
||||
(as long as at least one computer is attached to both accounts).
|
||||
<p>
|
||||
<b>User detail</b>
|
||||
<pre>",
|
||||
htmlspecialchars("
|
||||
|
@ -179,6 +201,7 @@ htmlspecialchars("
|
|||
<name>Eric Heien</name>
|
||||
<total_credit>4897.904591</total_credit>
|
||||
<expavg_credit>9820.631754</expavg_credit>
|
||||
<expavg_time>1087542007.701900</expavg_time>
|
||||
<country>United States</country>
|
||||
<create_time>1046220857</ncreate_time>
|
||||
[ <teamid>14</teamid> ]
|
||||
|
@ -187,6 +210,7 @@ htmlspecialchars("
|
|||
<id>27</id>
|
||||
<total_credit>0.000000</total_credit>
|
||||
<expavg_credit>0.000000</expavg_credit>
|
||||
<expavg_time>1087542007.701900</expavg_time>
|
||||
<p_vendor></p_vendor>
|
||||
<p_model></p_model>
|
||||
<os_name>Darwin</os_name>
|
||||
|
@ -196,6 +220,7 @@ htmlspecialchars("
|
|||
<id>266</id>
|
||||
<total_credit>0.000000</total_credit>
|
||||
<expavg_credit>0.000000</expavg_credit>
|
||||
<expavg_time>1087542007.701900</expavg_time>
|
||||
<p_vendor>GenuineIntel</p_vendor>
|
||||
<p_model>Intel(R)</p_model>
|
||||
<os_name>Linux</os_name>
|
||||
|
@ -211,6 +236,7 @@ htmlspecialchars("
|
|||
<id>266</id>
|
||||
<total_credit>0.000000</total_credit>
|
||||
<expavg_credit>0.000000</expavg_credit>
|
||||
<expavg_time>1087542007.701900</expavg_time>
|
||||
<p_vendor>GenuineIntel</p_vendor>
|
||||
<p_model>Intel(R)</p_model>
|
||||
<os_name>Linux</os_name>
|
||||
|
@ -226,6 +252,7 @@ htmlspecialchars("
|
|||
<userid>3</userid>
|
||||
<total_credit>0.000000</total_credit>
|
||||
<expavg_credit>0.000000</expavg_credit>
|
||||
<expavg_time>1087542007.701900</expavg_time>
|
||||
<p_vendor>GenuineIntel</p_vendor>
|
||||
<p_model>Pentium</p_model>
|
||||
<os_name>Windows XP</os_name>
|
||||
|
|
|
@ -25,7 +25,7 @@ a:active {
|
|||
|
||||
body , table , input , select {
|
||||
font-family: "Trebuchet MS", Verdana, Arial, Sans Serif;
|
||||
#font-size: small;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
body {
|
||||
|
|
|
@ -131,3 +131,4 @@
|
|||
#define ERR_BIND -180
|
||||
#define ERR_LISTEN -181
|
||||
#define ERR_TIMEOUT -182
|
||||
#define ERR_PROJECT_DOWN -183
|
||||
|
|
|
@ -50,7 +50,6 @@ bool do_pass(APP& app) {
|
|||
DB_RESULT canonical_result, result;
|
||||
bool did_something = false;
|
||||
char buf[256];
|
||||
int retval;
|
||||
|
||||
check_stop_daemons();
|
||||
|
||||
|
|
|
@ -547,7 +547,7 @@ inline static const char* get_remote_addr() {
|
|||
void handle_trickle_ups(SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply) {
|
||||
unsigned int i;
|
||||
DB_RESULT result;
|
||||
DB_TRICKLE_UP tup;
|
||||
DB_MSG_FROM_HOST mfh;
|
||||
int retval;
|
||||
char buf[256];
|
||||
|
||||
|
@ -577,15 +577,18 @@ void handle_trickle_ups(SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply) {
|
|||
);
|
||||
continue;
|
||||
}
|
||||
tup.clear();
|
||||
tup.create_time = time(0);
|
||||
tup.send_time = td.send_time;
|
||||
tup.resultid = result.id;
|
||||
tup.appid = result.appid;
|
||||
tup.hostid = reply.host.id;
|
||||
tup.handled = false;
|
||||
safe_strcpy(tup.xml, td.trickle_text.c_str());
|
||||
retval = tup.insert();
|
||||
mfh.clear();
|
||||
mfh.create_time = time(0);
|
||||
mfh.send_time = td.send_time;
|
||||
mfh.variety = result.appid;
|
||||
mfh.hostid = reply.host.id;
|
||||
mfh.handled = false;
|
||||
sprintf(buf, "<result_name>%s</result_name>\n", td.result_name);
|
||||
string foobar;
|
||||
foobar = buf;
|
||||
foobar += td.trickle_text;
|
||||
safe_strcpy(mfh.xml, foobar.c_str());
|
||||
retval = mfh.insert();
|
||||
if (retval) {
|
||||
log_messages.printf(SCHED_MSG_LOG::CRITICAL,
|
||||
"[HOST#%d] trickle insert failed: %d\n",
|
||||
|
@ -596,14 +599,14 @@ void handle_trickle_ups(SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply) {
|
|||
}
|
||||
|
||||
void handle_trickle_downs(SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply) {
|
||||
DB_TRICKLE_DOWN td;
|
||||
DB_MSG_TO_HOST mth;
|
||||
char buf[256];
|
||||
|
||||
sprintf(buf, "where hostid = %d", reply.host.id);
|
||||
while (!td.enumerate(buf)) {
|
||||
reply.trickle_downs.push_back(td);
|
||||
td.handled = true;
|
||||
td.update();
|
||||
while (!mth.enumerate(buf)) {
|
||||
reply.msgs_to_host.push_back(mth);
|
||||
mth.handled = true;
|
||||
mth.update();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@ void send_message(char* msg, int delay) {
|
|||
"<scheduler_reply>\n"
|
||||
" <message priority=\"low\">%s</message>\n"
|
||||
" <request_delay>%d</request_delay>\n"
|
||||
" <project_is_down/>\n"
|
||||
"</scheduler_reply>\n",
|
||||
msg, delay
|
||||
);
|
||||
|
|
|
@ -185,7 +185,6 @@ SCHEDULER_REPLY::~SCHEDULER_REPLY() {
|
|||
int SCHEDULER_REPLY::write(FILE* fout) {
|
||||
unsigned int i, j;
|
||||
string u1, u2, t1, t2;
|
||||
int retval;
|
||||
|
||||
fprintf(fout,
|
||||
"<scheduler_reply>\n"
|
||||
|
@ -317,25 +316,9 @@ int SCHEDULER_REPLY::write(FILE* fout) {
|
|||
if (send_trickle_up_ack) {
|
||||
fputs("<trickle_up_ack/>\n", fout);
|
||||
}
|
||||
for (i=0; i<trickle_downs.size(); i++) {
|
||||
TRICKLE_DOWN& td = trickle_downs[i];
|
||||
DB_RESULT result;
|
||||
retval = result.lookup_id(td.resultid);
|
||||
if (retval) {
|
||||
continue;
|
||||
}
|
||||
fprintf(fout,
|
||||
"<trickle_down>\n"
|
||||
" <result_name>%s</result_name>\n"
|
||||
" <send_time>%d</send_time>\n"
|
||||
" <text>\n"
|
||||
"%s\n"
|
||||
" </text>\n"
|
||||
"</trickle_down>\n",
|
||||
result.name,
|
||||
td.create_time,
|
||||
td.xml
|
||||
);
|
||||
for (i=0; i<msgs_to_host.size(); i++) {
|
||||
MSG_TO_HOST& mth = msgs_to_host[i];
|
||||
fprintf(fout, "%s", mth.xml);
|
||||
}
|
||||
if (config.non_cpu_intensive) {
|
||||
fprintf(fout, "<non_cpu_intensive/>\n");
|
||||
|
|
|
@ -104,7 +104,7 @@ struct SCHEDULER_REPLY {
|
|||
vector<WORKUNIT>wus;
|
||||
vector<RESULT>results;
|
||||
vector<RESULT>result_acks;
|
||||
vector<TRICKLE_DOWN>trickle_downs;
|
||||
vector<MSG_TO_HOST>msgs_to_host;
|
||||
char code_sign_key[4096];
|
||||
char code_sign_key_signature[4096];
|
||||
bool send_trickle_up_ack;
|
||||
|
|
|
@ -44,23 +44,23 @@ using namespace std;
|
|||
SCHED_CONFIG config;
|
||||
char app_name[256];
|
||||
|
||||
extern int handle_trickle(TRICKLE_UP&);
|
||||
extern int handle_trickle(MSG_FROM_HOST&);
|
||||
|
||||
int handle_trickle(TRICKLE_UP& tup) {
|
||||
int handle_trickle(MSG_FROM_HOST& mfh) {
|
||||
int retval;
|
||||
|
||||
printf(
|
||||
"got trickle-up \n%s\nfor result %d\n",
|
||||
tup.xml, tup.resultid
|
||||
"got trickle-up \n%s\n\n",
|
||||
mfh.xml
|
||||
);
|
||||
DB_TRICKLE_DOWN tdown;
|
||||
tdown.clear();
|
||||
tdown.create_time = time(0);
|
||||
tdown.resultid = tup.resultid;
|
||||
tdown.hostid = tup.hostid;
|
||||
tdown.handled = false;
|
||||
strcpy(tdown.xml, tup.xml);
|
||||
retval = tdown.insert();
|
||||
DB_MSG_TO_HOST mth;
|
||||
mth.clear();
|
||||
mth.create_time = time(0);
|
||||
mth.hostid = mfh.hostid;
|
||||
mth.variety = mfh.variety;
|
||||
mth.handled = false;
|
||||
strcpy(mth.xml, mfh.xml);
|
||||
retval = mth.insert();
|
||||
if (retval) {
|
||||
printf("insert failed %d\n", retval);
|
||||
}
|
||||
|
@ -71,17 +71,17 @@ int handle_trickle(TRICKLE_UP& tup) {
|
|||
// return true if there were any
|
||||
//
|
||||
bool do_trickle_scan(APP& app) {
|
||||
DB_TRICKLE_UP tup;
|
||||
DB_MSG_FROM_HOST mfh;
|
||||
char buf[256];
|
||||
bool found=false;
|
||||
int retval;
|
||||
|
||||
sprintf(buf, "where appid=%d and handled=0", app.id);
|
||||
while (!tup.enumerate(buf)) {
|
||||
retval = handle_trickle(tup);
|
||||
sprintf(buf, "where variety=%d and handled=0", app.id);
|
||||
while (!mfh.enumerate(buf)) {
|
||||
retval = handle_trickle(mfh);
|
||||
if (!retval) {
|
||||
tup.handled = true;
|
||||
tup.update();
|
||||
mfh.handled = true;
|
||||
mfh.update();
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue