// The client has (or will soon have) the given file. // Try to send it results that use that file. // If don't get any the first time, // trigger the work generator, then try again. // static int send_results_for_file( char* filename, int& nsent, bool /*in_working_set*/ ) { SCHED_DB_RESULT result, prev_result; char buf[256], query[1024]; int i, maxid, retval_max, retval_lookup, sleep_made_no_work=0; nsent = 0; if (!work_needed(true)) { return 0; } // find largest ID of results already sent to this user for this // file, if any. Any result that is sent will have userid field // set, so unsent results can not be returned by this query. // #ifdef USE_REGEXP char pattern[256], escaped_pattern[256]; sprintf(pattern, "%s__", filename); escape_mysql_like_pattern(pattern, escaped_pattern); sprintf(buf, "where userid=%d and name like binary '%s%%'", g_reply->user.id, escaped_pattern ); #else sprintf(buf, "where userid=%d and name>binary '%s__' and name<binary '%s__~'", g_reply->user.id, filename, filename ); #endif retval_max = result.max_id(maxid, buf); if (retval_max) { prev_result.id = 0; } else { retval_lookup = prev_result.lookup_id(maxid); if (retval_lookup) return ERR_DB_NOT_FOUND; } for (i=0; i<100; i++) { // avoid infinite loop int query_retval; if (!work_needed(true)) break; if (config.debug_locality) { log_messages.printf(MSG_NORMAL, "[locality] in_send_results_for_file(%s, %d) prev_result.id=%d\n", filename, i, prev_result.id ); } // find unsent result with next larger ID than previous largest ID // if (config.one_result_per_user_per_wu && prev_result.id) { // if one result per user per WU, insist on different WUID too // #ifdef USE_REGEXP sprintf(query, "INNER JOIN (SELECT id FROM result WHERE name like binary '%s%%' and id>%d and workunitid<>%d and server_state=%d order by id limit 1) AS single USING (id) ", escaped_pattern, prev_result.id, prev_result.workunitid, RESULT_SERVER_STATE_UNSENT ); #else sprintf(query, "INNER JOIN (SELECT id FROM result WHERE name>binary '%s__' and name<binary '%s__~' and id>%d and workunitid<>%d and server_state=%d order by id limit 1) AS single USING (id) ", filename, filename, prev_result.id, prev_result.workunitid, RESULT_SERVER_STATE_UNSENT ); #endif } else { #ifdef USE_REGEXP sprintf(query, "INNER JOIN (SELECT id FROM result WHERE name like binary '%s%%' and id>%d and server_state=%d order by id limit 1) AS single USING (id) ", escaped_pattern, prev_result.id, RESULT_SERVER_STATE_UNSENT ); #else sprintf(query, "INNER JOIN (SELECT id FROM result WHERE name>binary '%s__' and name<binary '%s__~' and id>%d and server_state=%d order by id limit 1) AS single USING (id) ", filename, filename, prev_result.id, RESULT_SERVER_STATE_UNSENT ); #endif } // Use a transaction so that if we get a result, // someone else doesn't send it before we do // boinc_db.start_transaction(); query_retval = result.lookup(query); if (query_retval) { int make_work_retval; // no unsent results are available for this file // boinc_db.commit_transaction(); // see if no more work remains to be made for this file, // or if an attempt to make more work fails. // make_work_retval=make_more_work_for_file(filename); if (config.debug_locality) { log_messages.printf(MSG_NORMAL, "[locality] make_more_work_for_file(%s, %d)=%d\n", filename, i, make_work_retval ); } if (make_work_retval) { // can't make any more work for this file if (config.one_result_per_user_per_wu) { // do an EXPENSIVE db query #ifdef USE_REGEXP sprintf(query, "INNER JOIN (SELECT id FROM result WHERE server_state=%d and name like binary '%s%%' limit 1) AS single USING (id)", RESULT_SERVER_STATE_UNSENT, escaped_pattern ); #else sprintf(query, "INNER JOIN (SELECT id FROM result WHERE server_state=%d and name>binary '%s__' and name<binary '%s__~' limit 1) AS single USING (id)", RESULT_SERVER_STATE_UNSENT, filename, filename ); #endif // re-using result -- do I need to clear it? if (!result.lookup(query)) { // some results remain -- but they are not suitable // for us because they must be for a WU that we have // already looked at. break; } } // config.one_result_per_user_per_wu // arrive here if and only if there exist no further // unsent results for this file. flag_for_possible_removal(filename); if (config.debug_locality) { log_messages.printf(MSG_NORMAL, "[locality] No remaining work for file %s (%d), flagging for removal\n", filename, i ); } break; } // make_work_retval // If the user has not configured us to wait and try // again, or we have already tried to find work for this // file, we are finished. // if (!config.locality_scheduling_wait_period || sleep_made_no_work) { break; } // wait a bit and try again to find a suitable unsent result sleep(config.locality_scheduling_wait_period); sleep_made_no_work=1; } // query_retval else { int retval_send; // we found an unsent result, so try sending it. // This *should* always work. // retval_send = possibly_send_result(result); boinc_db.commit_transaction(); // if no app version or not enough resources, give up completely // if (retval_send == ERR_NO_APP_VERSION || retval_send==ERR_INSUFFICIENT_RESOURCE) return retval_send; // if we couldn't send it for other reason, something's wacky; // print a message, but keep on looking. // David, this is NOT wacky. Consider the following // scenario: WU A has result 1 and WU B has result 2. // These are both sent to a host. Some time later, result // 1 fails and the transitioner creates a new result, // result 3 for WU A. Then the host requests a new // result. The maximum result already sent to the host is // 2. The next unsent result (sorted by ID) is #3. But // since it is for WU A, and since the host has already // gotten a result for WU A, it's infeasible. So I think // this is only wacky if !one_wu_per_result_per_host. if (!retval_send) { nsent++; sleep_made_no_work=0; } else if (!config.one_result_per_user_per_wu) { log_messages.printf(MSG_CRITICAL, "Database inconsistency? possibly_send_result(%d) failed for [RESULT#%d], returning %d\n", i, result.id, retval_send ); // If another scheduler instance 'snatched' the result // from under our noses, then possibly_send_result() // will return ERR_DB_NOT_FOUND // } else if (retval_send != ERR_DB_NOT_FOUND) { if (config.debug_locality) { log_messages.printf(MSG_NORMAL, "[locality] possibly_send_result [RESULT#%d]: %s\n", result.id, boincerror(retval_send) ); } } prev_result = result; } // query_retval } // loop over 0<i<100 return 0; }
// DAVID, this is missing a return value! Am I right that this will // also eventually move 'non locality' work through and out of the // system? // // This looks for work created in the range t_min < t < t_max. Use // t_min=INT_MIN if you wish to leave off the left constraint. // static int send_old_work(int t_min, int t_max) { char buf[1024], filename[256]; int retval, extract_retval, nsent; SCHED_DB_RESULT result; int now=time(0); if (!work_needed(true)) { return 0; } // restrict values to full hours; // this allows the DB to cache query results in some cases // t_max = (t_max/3600)*3600; boinc_db.start_transaction(); // Note: the following queries look convoluted. // But apparently the simpler versions (without the inner join) // are a lot slower. // if (t_min != INT_MIN) { sprintf(buf, "INNER JOIN (SELECT id FROM result WHERE server_state=%d and %d<create_time and create_time<%d limit 1) AS single USING (id)", RESULT_SERVER_STATE_UNSENT, t_min, t_max ); } else { sprintf(buf, "INNER JOIN (SELECT id FROM result WHERE server_state=%d and create_time<%d limit 1) AS single USING (id)", RESULT_SERVER_STATE_UNSENT, t_max ); } retval = result.lookup(buf); if (!retval) { retval = possibly_send_result(result); boinc_db.commit_transaction(); if (!retval) { double age=(now-result.create_time)/3600.0; if (config.debug_locality) { log_messages.printf(MSG_NORMAL, "[locality] send_old_work(%s) sent result created %.1f hours ago [RESULT#%d]\n", result.name, age, result.id ); } extract_retval=extract_filename(result.name, filename); if (!extract_retval) { send_results_for_file(filename, nsent, false); } else { // David, is this right? Is this the only place in // the locality scheduler that non-locality work // // gets done? if (config.debug_locality) { log_messages.printf(MSG_NORMAL, "[locality] Note: sent NON-LOCALITY result %s\n", result.name ); } } } else if (retval == ERR_NO_APP_VERSION || retval==ERR_INSUFFICIENT_RESOURCE) { // if no app version found or no resources, give up completely! return retval; } } else { boinc_db.commit_transaction(); } if (retval) { double older=(now-t_max)/3600.0; if (t_min != INT_MIN) { double young=(now-t_min)/3600.0; if (config.debug_locality) { log_messages.printf(MSG_NORMAL, "[locality] send_old_work() no feasible result younger than %.1f hours and older than %.1f hours\n", young, older ); } } else { if (config.debug_locality) { log_messages.printf(MSG_NORMAL, "[locality] send_old_work() no feasible result older than %.1f hours\n", older ); } } } // DAVID, YOU CHANGED THIS FROM VOID TO INT. IS THIS THE RIGHT // RETURN VAL? You should probably use the return value from // sent_results_for_file as well. return retval; }
// DAVID, this is missing a return value! Am I right that this will // also eventually move 'non locality' work through and out of the // system? // // This looks for work created in the range t_min < t < t_max. Use // t_min=INT_MIN if you wish to leave off the left constraint. // static int send_old_work(int t_min, int t_max) { char buf[1024], filename[256]; int retval, extract_retval, nsent; DB_RESULT result; int now=time(0); if (!work_needed(true)) { return 0; } boinc_db.start_transaction(); if (t_min != INT_MIN) { sprintf(buf, "where server_state=%d and %d<create_time and create_time<%d limit 1", RESULT_SERVER_STATE_UNSENT, t_min, t_max ); } else { sprintf(buf, "where server_state=%d and create_time<%d limit 1", RESULT_SERVER_STATE_UNSENT, t_max ); } retval = result.lookup(buf); if (!retval) { retval = possibly_send_result(result); boinc_db.commit_transaction(); if (!retval) { double age=(now-result.create_time)/3600.0; if (config.debug_locality) { log_messages.printf(MSG_NORMAL, "[locality] send_old_work(%s) sent result created %.1f hours ago [RESULT#%d]\n", result.name, age, result.id ); } extract_retval=extract_filename(result.name, filename); if (!extract_retval) { send_results_for_file(filename, nsent, false); } else { // David, is this right? Is this the only place in // the locality scheduler that non-locality work // // gets done? if (config.debug_locality) { log_messages.printf(MSG_NORMAL, "[locality] Note: sent NON-LOCALITY result %s\n", result.name ); } } } else if (retval == ERR_NO_APP_VERSION || retval==ERR_INSUFFICIENT_RESOURCE) { // if no app version found or no resources, give up completely! return retval; } } else { boinc_db.commit_transaction(); } if (retval) { double older=(now-t_max)/3600.0; if (t_min != INT_MIN) { double young=(now-t_min)/3600.0; if (config.debug_locality) { log_messages.printf(MSG_NORMAL, "[locality] send_old_work() no feasible result younger than %.1f hours and older than %.1f hours\n", young, older ); } } else { if (config.debug_locality) { log_messages.printf(MSG_NORMAL, "[locality] send_old_work() no feasible result older than %.1f hours\n", older ); } } } // DAVID, YOU CHANGED THIS FROM VOID TO INT. IS THIS THE RIGHT // RETURN VAL? You should probably use the return value from // sent_results_for_file as well. return retval; }