|
|
|
@ -119,7 +119,7 @@ static char *restorefrom;
|
|
|
|
|
* idcontrol: only userid reuse is critical and the user is added |
|
|
|
|
* immeditately to the DB before replying to the add message |
|
|
|
|
* |
|
|
|
|
* Tables that are/will be written straight to the DB, so ar OK: |
|
|
|
|
* Tables that are/will be written straight to the DB, so are OK: |
|
|
|
|
* users, useraccounts, paymentaddresses, payments, |
|
|
|
|
* accountadjustment, optioncontrol, miningpayouts, |
|
|
|
|
* eventlog |
|
|
|
@ -128,7 +128,7 @@ static char *restorefrom;
|
|
|
|
|
typedef struct loadstatus { |
|
|
|
|
tv_t oldest_sharesummary_firstshare_n; |
|
|
|
|
tv_t newest_createdate_workinfo; |
|
|
|
|
tv_t newest_createdate_auth; |
|
|
|
|
tv_t newest_createdate_auths; |
|
|
|
|
tv_t newest_createdate_poolstats; |
|
|
|
|
} LOADSTATUS; |
|
|
|
|
static LOADSTATUS dbstatus; |
|
|
|
@ -136,6 +136,7 @@ static LOADSTATUS dbstatus;
|
|
|
|
|
/* Temporary while doing restart - it (of course) contains the fields
|
|
|
|
|
* required to track the newest userstats per user/worker |
|
|
|
|
*/ |
|
|
|
|
static K_TREE *userstats_ccl_root; |
|
|
|
|
static K_STORE *userstats_ccl; |
|
|
|
|
|
|
|
|
|
// size limit on the command string
|
|
|
|
@ -1096,13 +1097,15 @@ static K_STORE *userstats_summ;
|
|
|
|
|
#endif |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
/* summarisation of the userstats after this many days are done
|
|
|
|
|
/* TODO: summarisation of the userstats after this many days are done
|
|
|
|
|
* at the day level and the above stats are deleted from the db |
|
|
|
|
* Obvious WARNING - the larger this is, the more stats in the DB |
|
|
|
|
* This is summary level '2' (TODO) |
|
|
|
|
* This is summary level '2' |
|
|
|
|
*/ |
|
|
|
|
#define USERSTATS_DB_D 7 |
|
|
|
|
#define USERSTATS_DB_DS (USERSTATS_DB_D * (60*60*24)) |
|
|
|
|
|
|
|
|
|
// true if _new is newer, i.e. _old is before _new
|
|
|
|
|
#define tv_newer(_old, _new) (((_old)->tv_sec == (_new)->tv_sec) ? \ |
|
|
|
|
((_old)->tv_usec < (_new)->tv_usec) : \
|
|
|
|
|
((_old)->tv_sec < (_new)->tv_sec)) |
|
|
|
@ -2938,6 +2941,11 @@ static bool workinfo_fill(PGconn *conn)
|
|
|
|
|
workinfo_root = add_to_ktree(workinfo_root, item, cmp_workinfo); |
|
|
|
|
workinfo_height_root = add_to_ktree(workinfo_height_root, item, cmp_workinfo_height); |
|
|
|
|
k_add_head(workinfo_store, item); |
|
|
|
|
|
|
|
|
|
if (tv_newer(&(dbstatus.newest_createdate_workinfo), &(row->createdate))) { |
|
|
|
|
memcpy(&(dbstatus.newest_createdate_workinfo), &(row->createdate), |
|
|
|
|
sizeof(row->createdate)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!ok) |
|
|
|
|
k_add_head(workinfo_list, item); |
|
|
|
@ -3534,7 +3542,7 @@ static bool sharesummary_fill(PGconn *conn)
|
|
|
|
|
|
|
|
|
|
// TODO: limit how far back
|
|
|
|
|
sel = "select " |
|
|
|
|
"userid,workername,workinfoid,diffacc,diffsta,diffdup,diffhi" |
|
|
|
|
"userid,workername,workinfoid,diffacc,diffsta,diffdup,diffhi," |
|
|
|
|
"diffrej,shareacc,sharesta,sharedup,sharehi,sharerej," |
|
|
|
|
"sharecount,errorcount,firstshare,lastshare,complete" |
|
|
|
|
MODIFYDATECONTROL |
|
|
|
@ -3666,6 +3674,13 @@ static bool sharesummary_fill(PGconn *conn)
|
|
|
|
|
k_add_head(sharesummary_store, item); |
|
|
|
|
|
|
|
|
|
workerstatus_update(NULL, NULL, NULL, row); |
|
|
|
|
|
|
|
|
|
if (tolower(row->complete[0]) == SUMMARY_NEW && |
|
|
|
|
(dbstatus.oldest_sharesummary_firstshare_n.tv_sec == 0 || |
|
|
|
|
!tv_newer(&(dbstatus.oldest_sharesummary_firstshare_n), &(row->firstshare)))) { |
|
|
|
|
memcpy(&(dbstatus.oldest_sharesummary_firstshare_n), &(row->firstshare), |
|
|
|
|
sizeof(row->createdate)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!ok) |
|
|
|
|
k_add_head(sharesummary_list, item); |
|
|
|
@ -4206,6 +4221,11 @@ static bool auths_fill(PGconn *conn)
|
|
|
|
|
auths_root = add_to_ktree(auths_root, item, cmp_auths); |
|
|
|
|
k_add_head(auths_store, item); |
|
|
|
|
workerstatus_update(row, NULL, NULL, NULL); |
|
|
|
|
|
|
|
|
|
if (tv_newer(&(dbstatus.newest_createdate_auths), &(row->createdate))) { |
|
|
|
|
memcpy(&(dbstatus.newest_createdate_auths), &(row->createdate), |
|
|
|
|
sizeof(row->createdate)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!ok) |
|
|
|
|
k_add_head(auths_list, item); |
|
|
|
@ -4299,7 +4319,7 @@ static bool poolstats_add(PGconn *conn, bool store, char *poolinstance,
|
|
|
|
|
ins = "insert into poolstats " |
|
|
|
|
"(poolinstance,elapsed,users,workers,hashrate," |
|
|
|
|
"hashrate5m,hashrate1hr,hashrate24hr" |
|
|
|
|
SIMPLEDATECONTROL ") values (" PQPARAM11 ")"; |
|
|
|
|
SIMPLEDATECONTROL ") values (" PQPARAM12 ")"; |
|
|
|
|
|
|
|
|
|
res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0); |
|
|
|
|
rescode = PQresultStatus(res); |
|
|
|
@ -4413,8 +4433,17 @@ static bool poolstats_fill(PGconn *conn)
|
|
|
|
|
break; |
|
|
|
|
TXT_TO_DOUBLE("hashrate24hr", field, row->hashrate24hr); |
|
|
|
|
|
|
|
|
|
SIMPLEDATEFLDS(res, i, row, ok); |
|
|
|
|
if (!ok) |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
poolstats_root = add_to_ktree(poolstats_root, item, cmp_poolstats); |
|
|
|
|
k_add_head(poolstats_store, item); |
|
|
|
|
|
|
|
|
|
if (tv_newer(&(dbstatus.newest_createdate_poolstats), &(row->createdate))) { |
|
|
|
|
memcpy(&(dbstatus.newest_createdate_poolstats), &(row->createdate), |
|
|
|
|
sizeof(row->createdate)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!ok) |
|
|
|
|
k_add_head(poolstats_list, item); |
|
|
|
@ -4646,6 +4675,54 @@ static bool userstats_add(char *poolinstance, char *elapsed, char *username,
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void userstats_update_ccl(USERSTATS *row) |
|
|
|
|
{ |
|
|
|
|
USERSTATS userstats, *tmp; |
|
|
|
|
K_TREE_CTX ctx[1]; |
|
|
|
|
K_ITEM look, *item; |
|
|
|
|
char buf[DATE_BUFSIZ+1]; |
|
|
|
|
|
|
|
|
|
userstats.userid = row->userid; |
|
|
|
|
STRNCPY(userstats.workername, row->workername); |
|
|
|
|
memcpy(&(userstats.statsdate), &(row->statsdate), sizeof(row->statsdate)); |
|
|
|
|
// Start of the next timeband after this row
|
|
|
|
|
switch (row->summarylevel[0]) { |
|
|
|
|
case SUMMARY_DB: |
|
|
|
|
userstats.statsdate.tv_sec += USERSTATS_DB_S; |
|
|
|
|
break; |
|
|
|
|
case SUMMARY_FULL: |
|
|
|
|
userstats.statsdate.tv_sec += USERSTATS_DB_DS; |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
tv_to_buf(&(row->statsdate), buf, sizeof(buf)); |
|
|
|
|
// Bad userstats are not fatal
|
|
|
|
|
LOGERR("Unknown userstats summarylevel '%c' " |
|
|
|
|
"userid "PRId64" workername %s statsdate %s", |
|
|
|
|
row->summarylevel[0], row->userid, |
|
|
|
|
row->workername, buf); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
look.data = (void *)(&userstats); |
|
|
|
|
item = find_in_ktree(userstats_ccl_root, &look, cmp_userstats_workername, ctx); |
|
|
|
|
if (item) { |
|
|
|
|
tmp = DATA_USERSTATS(item); |
|
|
|
|
if (tv_newer(&(tmp->statsdate), &(userstats.statsdate))) |
|
|
|
|
memcpy(&(tmp->statsdate), &(userstats.statsdate), sizeof(tmp->statsdate)); |
|
|
|
|
} else { |
|
|
|
|
K_WLOCK(userstats_list); |
|
|
|
|
item = k_unlink_head(userstats_list); |
|
|
|
|
tmp = DATA_USERSTATS(item); |
|
|
|
|
bzero(tmp, sizeof(*tmp)); |
|
|
|
|
tmp->userid = userstats.userid; |
|
|
|
|
STRNCPY(tmp->workername, userstats.workername); |
|
|
|
|
memcpy(&(tmp->statsdate), &(userstats.statsdate), sizeof(tmp->statsdate)); |
|
|
|
|
userstats_ccl_root = add_to_ktree(userstats_ccl_root, item, |
|
|
|
|
cmp_userstats_workername); |
|
|
|
|
k_add_head(userstats_ccl, item); |
|
|
|
|
K_WUNLOCK(userstats_list); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// TODO: data selection - only require ?
|
|
|
|
|
static bool userstats_fill(PGconn *conn) |
|
|
|
|
{ |
|
|
|
@ -4748,6 +4825,7 @@ static bool userstats_fill(PGconn *conn)
|
|
|
|
|
k_add_head(userstats_store, item); |
|
|
|
|
|
|
|
|
|
workerstatus_update(NULL, NULL, row, NULL); |
|
|
|
|
userstats_update_ccl(row); |
|
|
|
|
} |
|
|
|
|
if (!ok) |
|
|
|
|
k_add_head(userstats_list, item); |
|
|
|
@ -4870,6 +4948,10 @@ static bool getdata()
|
|
|
|
|
goto matane; |
|
|
|
|
if (!(ok = shareerrors_fill())) |
|
|
|
|
goto matane; |
|
|
|
|
if (!(ok = sharesummary_fill(conn))) |
|
|
|
|
goto matane; |
|
|
|
|
if (!(ok = blocks_fill(conn))) |
|
|
|
|
goto matane; |
|
|
|
|
if (!(ok = auths_fill(conn))) |
|
|
|
|
goto matane; |
|
|
|
|
if (!(ok = poolstats_fill(conn))) |
|
|
|
@ -4975,76 +5057,115 @@ static void clean_up(ckpool_t *ckp)
|
|
|
|
|
static bool setup_data() |
|
|
|
|
{ |
|
|
|
|
K_TREE_CTX ctx[1]; |
|
|
|
|
K_ITEM look, *found; |
|
|
|
|
K_ITEM look, *found, *ccl; |
|
|
|
|
WORKINFO wi; |
|
|
|
|
char buf[DATE_BUFSIZ+1]; |
|
|
|
|
tv_t statsdate; |
|
|
|
|
|
|
|
|
|
transfer_list = k_new_list("Transfer", sizeof(TRANSFER), ALLOC_TRANSFER, LIMIT_TRANSFER, true); |
|
|
|
|
transfer_list = k_new_list("Transfer", sizeof(TRANSFER), |
|
|
|
|
ALLOC_TRANSFER, LIMIT_TRANSFER, true); |
|
|
|
|
transfer_store = k_new_store(transfer_list); |
|
|
|
|
transfer_root = new_ktree(); |
|
|
|
|
transfer_list->dsp_func = dsp_transfer; |
|
|
|
|
|
|
|
|
|
users_list = k_new_list("Users", sizeof(USERS), ALLOC_USERS, LIMIT_USERS, true); |
|
|
|
|
users_list = k_new_list("Users", sizeof(USERS), |
|
|
|
|
ALLOC_USERS, LIMIT_USERS, true); |
|
|
|
|
users_store = k_new_store(users_list); |
|
|
|
|
users_root = new_ktree(); |
|
|
|
|
userid_root = new_ktree(); |
|
|
|
|
|
|
|
|
|
workers_list = k_new_list("Workers", sizeof(WORKERS), ALLOC_WORKERS, LIMIT_WORKERS, true); |
|
|
|
|
workers_list = k_new_list("Workers", sizeof(WORKERS), |
|
|
|
|
ALLOC_WORKERS, LIMIT_WORKERS, true); |
|
|
|
|
workers_store = k_new_store(workers_list); |
|
|
|
|
workers_root = new_ktree(); |
|
|
|
|
|
|
|
|
|
payments_list = k_new_list("Payments", sizeof(PAYMENTS), ALLOC_PAYMENTS, LIMIT_PAYMENTS, true); |
|
|
|
|
payments_list = k_new_list("Payments", sizeof(PAYMENTS), |
|
|
|
|
ALLOC_PAYMENTS, LIMIT_PAYMENTS, true); |
|
|
|
|
payments_store = k_new_store(payments_list); |
|
|
|
|
payments_root = new_ktree(); |
|
|
|
|
|
|
|
|
|
idcontrol_list = k_new_list("IDControl", sizeof(IDCONTROL), ALLOC_IDCONTROL, LIMIT_IDCONTROL, true); |
|
|
|
|
idcontrol_list = k_new_list("IDControl", sizeof(IDCONTROL), |
|
|
|
|
ALLOC_IDCONTROL, LIMIT_IDCONTROL, true); |
|
|
|
|
idcontrol_store = k_new_store(idcontrol_list); |
|
|
|
|
|
|
|
|
|
workinfo_list = k_new_list("WorkInfo", sizeof(WORKINFO), ALLOC_WORKINFO, LIMIT_WORKINFO, true); |
|
|
|
|
workinfo_list = k_new_list("WorkInfo", sizeof(WORKINFO), |
|
|
|
|
ALLOC_WORKINFO, LIMIT_WORKINFO, true); |
|
|
|
|
workinfo_store = k_new_store(workinfo_list); |
|
|
|
|
workinfo_root = new_ktree(); |
|
|
|
|
workinfo_height_root = new_ktree(); |
|
|
|
|
|
|
|
|
|
shares_list = k_new_list("Shares", sizeof(SHARES), ALLOC_SHARES, LIMIT_SHARES, true); |
|
|
|
|
shares_list = k_new_list("Shares", sizeof(SHARES), |
|
|
|
|
ALLOC_SHARES, LIMIT_SHARES, true); |
|
|
|
|
shares_store = k_new_store(shares_list); |
|
|
|
|
shares_root = new_ktree(); |
|
|
|
|
|
|
|
|
|
shareerrors_list = k_new_list("ShareErrors", sizeof(SHAREERRORS), ALLOC_SHAREERRORS, LIMIT_SHAREERRORS, true); |
|
|
|
|
shareerrors_list = k_new_list("ShareErrors", sizeof(SHAREERRORS), |
|
|
|
|
ALLOC_SHAREERRORS, LIMIT_SHAREERRORS, true); |
|
|
|
|
shareerrors_store = k_new_store(shareerrors_list); |
|
|
|
|
shareerrors_root = new_ktree(); |
|
|
|
|
|
|
|
|
|
sharesummary_list = k_new_list("ShareSummary", sizeof(SHARESUMMARY), ALLOC_SHARESUMMARY, LIMIT_SHARESUMMARY, true); |
|
|
|
|
sharesummary_list = k_new_list("ShareSummary", sizeof(SHARESUMMARY), |
|
|
|
|
ALLOC_SHARESUMMARY, LIMIT_SHARESUMMARY, true); |
|
|
|
|
sharesummary_store = k_new_store(sharesummary_list); |
|
|
|
|
sharesummary_root = new_ktree(); |
|
|
|
|
sharesummary_workinfoid_root = new_ktree(); |
|
|
|
|
sharesummary_list->dsp_func = dsp_sharesummary; |
|
|
|
|
|
|
|
|
|
blocks_list = k_new_list("Blocks", sizeof(BLOCKS), ALLOC_BLOCKS, LIMIT_BLOCKS, true); |
|
|
|
|
blocks_list = k_new_list("Blocks", sizeof(BLOCKS), |
|
|
|
|
ALLOC_BLOCKS, LIMIT_BLOCKS, true); |
|
|
|
|
blocks_store = k_new_store(blocks_list); |
|
|
|
|
blocks_root = new_ktree(); |
|
|
|
|
|
|
|
|
|
auths_list = k_new_list("Auths", sizeof(AUTHS), ALLOC_AUTHS, LIMIT_AUTHS, true); |
|
|
|
|
auths_list = k_new_list("Auths", sizeof(AUTHS), |
|
|
|
|
ALLOC_AUTHS, LIMIT_AUTHS, true); |
|
|
|
|
auths_store = k_new_store(auths_list); |
|
|
|
|
auths_root = new_ktree(); |
|
|
|
|
|
|
|
|
|
poolstats_list = k_new_list("PoolStats", sizeof(POOLSTATS), ALLOC_POOLSTATS, LIMIT_POOLSTATS, true); |
|
|
|
|
poolstats_list = k_new_list("PoolStats", sizeof(POOLSTATS), |
|
|
|
|
ALLOC_POOLSTATS, LIMIT_POOLSTATS, true); |
|
|
|
|
poolstats_store = k_new_store(poolstats_list); |
|
|
|
|
poolstats_root = new_ktree(); |
|
|
|
|
|
|
|
|
|
userstats_list = k_new_list("UserStats", sizeof(USERSTATS), ALLOC_USERSTATS, LIMIT_USERSTATS, true); |
|
|
|
|
userstats_list = k_new_list("UserStats", sizeof(USERSTATS), |
|
|
|
|
ALLOC_USERSTATS, LIMIT_USERSTATS, true); |
|
|
|
|
userstats_store = k_new_store(userstats_list); |
|
|
|
|
userstats_eos_store = k_new_store(userstats_list); |
|
|
|
|
userstats_summ = k_new_store(userstats_list); |
|
|
|
|
userstats_ccl = k_new_store(userstats_list); |
|
|
|
|
userstats_root = new_ktree(); |
|
|
|
|
userstats_list->dsp_func = dsp_userstats; |
|
|
|
|
userstats_ccl_root = new_ktree(); |
|
|
|
|
|
|
|
|
|
workerstatus_list = k_new_list("WorkerStatus", sizeof(WORKERSTATUS), ALLOC_WORKERSTATUS, LIMIT_WORKERSTATUS, true); |
|
|
|
|
workerstatus_list = k_new_list("WorkerStatus", sizeof(WORKERSTATUS), |
|
|
|
|
ALLOC_WORKERSTATUS, LIMIT_WORKERSTATUS, true); |
|
|
|
|
workerstatus_store = k_new_store(workerstatus_list); |
|
|
|
|
workerstatus_root = new_ktree(); |
|
|
|
|
|
|
|
|
|
if (!getdata()) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
tv_to_buf(&(dbstatus.oldest_sharesummary_firstshare_n), buf, sizeof(buf)); |
|
|
|
|
LOGWARNING("%s(): %s oldest DB incomplete sharesummary", __func__, buf); |
|
|
|
|
tv_to_buf(&(dbstatus.newest_createdate_workinfo), buf, sizeof(buf)); |
|
|
|
|
LOGWARNING("%s(): %s newest DB workinfo", __func__, buf); |
|
|
|
|
tv_to_buf(&(dbstatus.newest_createdate_auths), buf, sizeof(buf)); |
|
|
|
|
LOGWARNING("%s(): %s newest DB auths", __func__, buf); |
|
|
|
|
tv_to_buf(&(dbstatus.newest_createdate_poolstats), buf, sizeof(buf)); |
|
|
|
|
LOGWARNING("%s(): %s newest DB poolstats", __func__, buf); |
|
|
|
|
|
|
|
|
|
bzero(&statsdate, sizeof(statsdate)); |
|
|
|
|
ccl = userstats_ccl->head; |
|
|
|
|
while (ccl) { |
|
|
|
|
if (statsdate.tv_sec == 0 || |
|
|
|
|
!tv_newer(&statsdate, &(DATA_USERSTATS(ccl)->statsdate))) |
|
|
|
|
memcpy(&statsdate, &(DATA_USERSTATS(ccl)->statsdate), sizeof(statsdate)); |
|
|
|
|
ccl = ccl->next; |
|
|
|
|
} |
|
|
|
|
tv_to_buf(&statsdate, buf, sizeof(buf)); |
|
|
|
|
LOGWARNING("%s(): %s oldest new DB userstats", __func__, buf); |
|
|
|
|
free_ktree(userstats_ccl_root, NULL); |
|
|
|
|
k_list_transfer_to_head(userstats_ccl, userstats_list); |
|
|
|
|
|
|
|
|
|
workinfo_current = last_in_ktree(workinfo_height_root, ctx); |
|
|
|
|
if (workinfo_current) { |
|
|
|
|
STRNCPY(wi.coinbase1, DATA_WORKINFO(workinfo_current)->coinbase1); |
|
|
|
@ -5201,7 +5322,7 @@ static char *cmd_poolstats(char *cmd, __maybe_unused char *id, tv_t *now, char *
|
|
|
|
|
if (!i_createdate) |
|
|
|
|
return strdup(reply); |
|
|
|
|
TXT_TO_CTV("createdate", DATA_TRANSFER(i_createdate)->data, createdate); |
|
|
|
|
if (tvdiff(&createdate, &(row.createdate)) > STATS_PER) |
|
|
|
|
if (tvdiff(&createdate, &(DATA_POOLSTATS(ps)->createdate)) > STATS_PER) |
|
|
|
|
store = true; |
|
|
|
|
else |
|
|
|
|
store = false; |
|
|
|
@ -6494,7 +6615,7 @@ static void summarise_userstats()
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
memcpy(&when, &(DATA_USERSTATS(tail)->statsdate), sizeof(when)); |
|
|
|
|
/* Convent when to the start of the timeframe after the one it is in
|
|
|
|
|
/* Convert when to the start of the timeframe after the one it is in
|
|
|
|
|
* assume timeval ignores leapseconds ... */ |
|
|
|
|
when.tv_sec = when.tv_sec - (when.tv_sec % USERSTATS_DB_S) + USERSTATS_DB_S; |
|
|
|
|
when.tv_usec = 0; |
|
|
|
|