server/web: add support for per-application credit

See http://boinc.berkeley.edu/trac/wiki/PerAppCredit
If enabled (by the <credit_by_app> config flag)
validators will maintain on a per-(app, user, credit type) basis,
and same for teams,
in new DB tables credit_user and credit_team.
This info is displayed in the web site, on user and team pages,
using project-supplied functions to generate the HTML.

Note: update_stats doesn't decay the recent-average values
for per-app credit; I'll add this if needed.
This commit is contained in:
David Anderson 2014-08-15 14:01:32 -07:00
parent 2128990681
commit dbd2d03a0d
20 changed files with 323 additions and 10 deletions

View File

@ -97,6 +97,8 @@ void VDA_CHUNK_HOST::clear() {memset(this, 0, sizeof(*this));}
void BADGE::clear() {memset(this, 0, sizeof(*this));}
void BADGE_USER::clear() {memset(this, 0, sizeof(*this));}
void BADGE_TEAM::clear() {memset(this, 0, sizeof(*this));}
void CREDIT_USER::clear() {memset(this, 0, sizeof(*this));}
void CREDIT_TEAM::clear() {memset(this, 0, sizeof(*this));}
DB_PLATFORM::DB_PLATFORM(DB_CONN* dc) :
DB_BASE("platform", dc?dc:&boinc_db){}
@ -170,6 +172,10 @@ DB_BADGE_USER::DB_BADGE_USER(DB_CONN* dc) :
DB_BASE("badge_user", dc?dc:&boinc_db){}
DB_BADGE_TEAM::DB_BADGE_TEAM(DB_CONN* dc) :
DB_BASE("badge_team", dc?dc:&boinc_db){}
DB_CREDIT_USER::DB_CREDIT_USER(DB_CONN* dc) :
DB_BASE("credit_user", dc?dc:&boinc_db){}
DB_CREDIT_TEAM::DB_CREDIT_TEAM(DB_CONN* dc) :
DB_BASE("credit_team", dc?dc:&boinc_db){}
int DB_PLATFORM::get_id() {return id;}
int DB_APP::get_id() {return id;}
@ -2654,4 +2660,66 @@ void DB_BADGE_TEAM::db_parse(MYSQL_ROW &r) {
reassign_time = atof(r[i++]);
}
void DB_CREDIT_USER::db_print(char* buf) {
sprintf(buf,
"userid=%d, "
"appid=%d, "
"njobs=%d, "
"total=%.15e, "
"expavg=%.15e, "
"expavg_time=%.15e, "
"credit_type=%d ",
userid,
appid,
njobs,
total,
expavg,
expavg_time,
credit_type
);
}
void DB_CREDIT_USER::db_parse(MYSQL_ROW &r) {
int i=0;
clear();
userid = atoi(r[i++]);
appid = atoi(r[i++]);
njobs = atoi(r[i++]);
total = atof(r[i++]);
expavg = atof(r[i++]);
expavg_time = atof(r[i++]);
credit_type = atoi(r[i++]);
}
void DB_CREDIT_TEAM::db_print(char* buf) {
sprintf(buf,
"teamid=%d, "
"appid=%d, "
"njobs=%d, "
"total=%.15e, "
"expavg=%.15e, "
"expavg_time=%.15e, "
"credit_type=%d ",
teamid,
appid,
njobs,
total,
expavg,
expavg_time,
credit_type
);
}
void DB_CREDIT_TEAM::db_parse(MYSQL_ROW &r) {
int i=0;
clear();
teamid = atoi(r[i++]);
appid = atoi(r[i++]);
njobs = atoi(r[i++]);
total = atof(r[i++]);
expavg = atof(r[i++]);
expavg_time = atof(r[i++]);
credit_type = atoi(r[i++]);
}
const char *BOINC_RCSID_ac374386c8 = "$Id$";

View File

@ -531,4 +531,16 @@ struct DB_BADGE_TEAM : public DB_BASE, public BADGE_TEAM {
void db_parse(MYSQL_ROW&);
};
struct DB_CREDIT_USER : public DB_BASE, public CREDIT_USER {
DB_CREDIT_USER(DB_CONN* p=0);
void db_print(char*);
void db_parse(MYSQL_ROW&);
};
struct DB_CREDIT_TEAM : public DB_BASE, public CREDIT_TEAM {
DB_CREDIT_TEAM(DB_CONN* p=0);
void db_print(char*);
void db_parse(MYSQL_ROW&);
};
#endif

View File

@ -799,4 +799,27 @@ struct BADGE_TEAM {
void clear();
};
struct CREDIT_USER {
int userid;
int appid;
// need not be an app ID
int njobs;
double total;
double expavg;
double expavg_time;
int credit_type;
void clear();
};
struct CREDIT_TEAM {
int teamid;
int appid;
int njobs;
double total;
double expavg;
double expavg_time;
int credit_type;
void clear();
};
#endif

View File

@ -731,3 +731,25 @@ create table badge_team (
create_time double not null,
reassign_time double not null
);
create table credit_user (
userid integer not null,
appid integer not null,
njobs integer not null,
total double not null,
expavg double not null,
expavg_time double not null,
credit_type integer not null,
primary key (userid, appid, credit_type)
) engine=InnoDB;
create table credit_team (
teamid integer not null,
appid integer not null,
njobs integer not null,
total double not null,
expavg double not null,
expavg_time double not null,
credit_type integer not null,
primary key (teamid, appid, credit_type)
) engine=InnoDB;

View File

@ -540,6 +540,16 @@ $math = array(
"",
"Study hash functions"
),
array(
"Moo! Wrapper",
"http://moowrap.net/",
"Private",
tra("Cryptography and combinatorics"),
"Run applications from distributed.net",
"",
"",
"Run applications from distributed.net"
),
array(
"Enigma@Home",
"http://www.enigmaathome.net/",

View File

@ -646,5 +646,18 @@ class BoincBadgeTeam {
}
}
class BoincCreditUser {
static function lookup($clause) {
$db = BoincDb::get();
return $db->lookup('credit_user', 'BoincCreditUser', $clause);
}
}
class BoincCreditTeam {
static function lookup($clause) {
$db = BoincDb::get();
return $db->lookup('credit_team', 'BoincCreditTeam', $clause);
}
}
?>

View File

@ -39,10 +39,6 @@
// Various functions are defined below for converting between these forms,
// and also to/from HTML form elements
error_reporting(E_ALL);
ini_set('display_errors', true);
ini_set('display_startup_errors', true);
include_once("../inc/prefs_util.inc");
$cpu_prefs = array(

View File

@ -42,10 +42,6 @@
// In addition there are some fields of the user table
// (send_email and show_hosts) that are treated as project preferences
error_reporting(E_ALL);
ini_set('display_errors', true);
ini_set('display_startup_errors', true);
include_once("../inc/prefs_util.inc");
include_once("../project/project_specific_prefs.inc");

View File

@ -104,6 +104,9 @@ function display_team_page($team, $user) {
if (!no_computing()) {
row2(tra('Total credit'), format_credit_large($team->total_credit));
row2(tra('Recent average credit'), format_credit_large($team->expavg_credit));
if (function_exists('project_team_credit')) {
project_team_credit($team);
}
show_badges_row(false, $team);
$x = "";

View File

@ -18,6 +18,10 @@
// Utility functions for BOINC web pages
error_reporting(E_ALL);
ini_set('display_errors', true);
ini_set('display_startup_errors', true);
require_once("../inc/util_basic.inc");
require_once("../project/project.inc");
require_once("../inc/countries.inc");

View File

@ -941,6 +941,35 @@ function update_6_5_2014() {
);
}
function update_8_15_2014() {
do_query(
"create table credit_user (
userid integer not null,
appid integer not null,
njobs integer not null,
total double not null,
expavg double not null,
expavg_time double not null,
credit_type integer not null,
primary key (userid, appid, credit_type)
) engine=InnoDB
"
);
do_query(
"create table credit_team (
teamid integer not null,
appid integer not null,
njobs integer not null,
total double not null,
expavg double not null,
expavg_time double not null,
credit_type integer not null,
primary key (teamid, appid, credit_type)
) engine=InnoDB
"
);
}
// Updates are done automatically if you use "upgrade".
//
// If you need to do updates manually,
@ -983,6 +1012,7 @@ $db_updates = array (
array(27008, "update_4_2_2014"),
array(27009, "update_5_3_2014"),
array(27010, "update_6_5_2014"),
array(27011, "update_8_15_2014"),
);
?>

View File

@ -134,8 +134,62 @@ function project_user_page_private($user){
// shown in the private account page
}
// show project-specific credit on user/team pages
//
function show_app_credit_user($user, $app_name, $appids) {
$t = 0;
$a = 0;
$n = 0;
foreach ($appids as $appid) {
$cu = BoincCreditUser::lookup(
"userid=$user->id and appid=$appid and credit_type=0"
);
if ($cu) {
$t += $cu->total;
$a += $cu->expavg;
$n += $cu->njobs;
}
}
row2("$app_name credit",
format_credit_large($t)." total, ".
format_credit($a)." average".
" ($n tasks)"
);
}
function show_app_credit_team($team, $app_name, $appids) {
$t = 0;
$a = 0;
$n = 0;
foreach ($appids as $appid) {
$ct = BoincCreditTeam::lookup(
"teamid=$team->id and appid=$appid and credit_type=0"
);
if ($ct) {
$t += $ct->total;
$a += $ct->expavg;
$n += $ct->njobs;
}
}
row2("$app_name credit",
format_credit_large($t)." total, ".
format_credit($a)." average".
" ($n tasks)"
);
}
function project_user_credit($user){
// shown in the the private account page, credit area
if (0) {
show_app_credit_user($user, "Remote Test", array(16));
show_app_credit_user($user, "Uppercase", array(1, 25));
}
}
function project_team_credit($team) {
if (0) {
show_app_credit_team($team, "Remote Test", array(16));
show_app_credit_team($team, "Uppercase", array(1, 25));
}
}
function project_forum_post_rules() {

View File

@ -213,6 +213,13 @@ enum BATTERY_STATE {
// input/output files can be deleted,
// result and workunit records can be purged.
// credit types
//
#define CREDIT_TYPE_FLOPS 0
#define CREDIT_TYPE_STORAGE 1
#define CREDIT_TYPE_NETWORK 2
#define CREDIT_TYPE_PROJECT 3
struct TIME_STATS {
double now;
// the client's current time of day

View File

@ -119,3 +119,7 @@ BATCH_STATE_IN_PROGRESS = 1
BATCH_STATE_COMPLETE = 2
BATCH_STATE_ABORTED = 3
BATCH_STATE_RETIRED = 4
CREDIT_TYPE_FLOPS = 0
CREDIT_TYPE_STORAGE = 1
CREDIT_TYPE_NETWORK = 2
CREDIT_TYPE_PROJECT = 3

View File

@ -124,6 +124,66 @@ int grant_credit(DB_HOST &host, double start_time, double credit) {
return 0;
}
int grant_credit_by_app(RESULT& result, double credit) {
DB_CREDIT_USER cu;
char clause1[1024], clause2[1024];
double now = dtime();
sprintf(clause1, "where userid=%d and appid=%d", result.userid, result.appid);
int retval = cu.lookup(clause1);
if (retval) {
cu.clear();
cu.userid = result.userid;
cu.appid = result.appid;
retval = cu.insert();
if (retval) return retval;
}
update_average(
now,
result.sent_time,
credit, CREDIT_HALF_LIFE,
cu.expavg, cu.expavg_time
);
sprintf(clause1,
"total=total+%.15e, expavg=%.15e, expavg_time=%.15e, njobs=njobs+1",
credit, cu.expavg, cu.expavg_time
);
sprintf(clause2,
"userid=%d and appid=%d", result.userid, result.appid
);
retval = cu.update_fields_noid(clause1, clause2);
if (retval) return retval;
// team. keep track of credit for zero (no team) also
//
DB_CREDIT_TEAM ct;
sprintf(clause1, "where teamid=%d and appid=%d", result.teamid, result.appid);
retval = ct.lookup(clause1);
if (retval) {
ct.clear();
ct.teamid = result.teamid;
ct.appid = result.appid;
retval = ct.insert();
if (retval) return retval;
}
update_average(
now,
result.sent_time,
credit, CREDIT_HALF_LIFE,
ct.expavg, ct.expavg_time
);
sprintf(clause1,
"total=total+%.15e, expavg=%.15e, expavg_time=%.15e, njobs=njobs+1",
credit, ct.expavg, ct.expavg_time
);
sprintf(clause2,
"teamid=%d and appid=%d", result.teamid, result.appid
);
retval = ct.update_fields_noid(clause1, clause2);
if (retval) return retval;
return 0;
}
///////////////////// V2 CREDIT STUFF STARTS HERE ///////////////////
// levels of confidence in a credit value

View File

@ -63,3 +63,5 @@ extern int hav_lookup(DB_HOST_APP_VERSION& hav, int hostid, int avid);
extern int write_modified_app_versions(
std::vector<DB_APP_VERSION_VAL>& app_versions
);
extern int grant_credit_by_app(RESULT& result, double credit);

View File

@ -296,6 +296,7 @@ int SCHED_CONFIG::parse(FILE* f) {
if (xp.parse_bool("prefer_primary_platform", prefer_primary_platform)) continue;
if (xp.parse_double("version_select_random_factor", version_select_random_factor)) continue;
if (xp.parse_double("maintenance_delay", maintenance_delay)) continue;
if (xp.parse_bool("credit_by_app", credit_by_app)) continue;
//////////// SCHEDULER LOG FLAGS /////////

View File

@ -177,6 +177,8 @@ struct SCHED_CONFIG {
bool estimate_flops_from_hav_pfc;
// Use host_app_version peak flop count rather than elapsed time
// to calculate projected_flops when choosing version.
bool credit_by_app;
// store per-app credit info in credit_user and credit_team
// time intervals
double maintenance_delay;

View File

@ -17,7 +17,7 @@
// update_stats:
// Update average credit for idle users, hosts and teams.
// These fields are updates as new credit is granted;
// These fields are updated as new credit is granted;
// the purpose of this program is to decay credit of entities
// that are inactive for long periods.
// Run it about once a day.

View File

@ -308,6 +308,9 @@ int handle_wu(
if (!no_credit) {
result.granted_credit = canonical_result.granted_credit;
grant_credit(host, result.sent_time, result.granted_credit);
if (config.credit_by_app) {
grant_credit_by_app(result, result.granted_credit);
}
}
break;
case VALIDATE_STATE_INVALID:
@ -534,6 +537,9 @@ int handle_wu(
result.id, result.name, result.granted_credit,
result.hostid
);
if (config.credit_by_app) {
grant_credit_by_app(result, credit);
}
}
break;
case VALIDATE_STATE_INVALID: