Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
    npro  
 Modern all-safe Rust Network Protocol library supporting h1, h2, h3, ws, wt sans-IO and with socket IO + tls
git clone https://npro.rs/repo/npro
 
root / assets / arch-riscv64.svg
Author[]Andy Green <andy@warmcat.com> 2026-05-20 06:26 UTC
Committer[]agreen <Andy Green> 2026-05-22 13:04 UTC
Tree81b0e423d7ce122190f40edd63adc2119af2d71d   Raw Patch
 
retry task: keep all runs
retry task: keep all runs
diff --git a/assets/sai.css b/assets/sai.css index ea28791..7075275 100644 --- a/assets/sai.css +++ b/assets/sai.css @@ -41,7 +41,7 @@ img.tread2 { img.zup { position: relative; - z-index: 1001 + z-index: 11 } .tab label { @@ -1413,3 +1413,82 @@ canvas.power-graph { .taskstate6.prog-95 { background: linear-gradient(90deg, #df3030 95%, transparent 95%); } .taskstate6.prog-100 { background: linear-gradient(90deg, #df3030 100%, transparent 100%); } +.taskstate.has_runs { + border: 2px solid black; + margin: -1px; + position: relative; + z-index: 10; +} + +.taskstate.has_runs:hover { + z-index: 1001; + opacity: 1.0 !important; +} + +.runs-popup { + display: none; + position: absolute; + background: #ccc; + border: 1px solid black; + padding: 5px; + z-index: 2000; + opacity: 1.0 !important; + box-shadow: 2px 2px 5px rgba(0,0,0,0.5); +} + +.taskstate:hover { + opacity: 1.0 !important; +} + +.taskstate:hover .runs-popup { + display: block; +} + +.runs-popup table { + border-collapse: collapse; +} + +.runs-popup td { + padding: 2px 5px; + white-space: nowrap; +} + +.runs-header-container { + margin-top: -8px; +} + +.runs-table { + border-collapse: separate; + border-spacing: 4px 0px; +} + +.runs-table td { + text-align: center; + padding: 0px 2px; +} + +.runs-popup table { + border-collapse: collapse; +} + +.runs-popup td { + padding: 2px 8px; + text-align: left; +} + +.run-current-decal { + border: 2px solid #000; + position: relative; + border-radius: 4px; + display: inline-block; +} + +.run-decal { + position: relative; + border-radius: 4px; + display: inline-block; +} + +.runs-time-cell { + white-space: nowrap; +} diff --git a/assets/sai.js b/assets/sai.js index ec2f829..8dac6a9 100644 --- a/assets/sai.js +++ b/assets/sai.js @@ -1089,6 +1089,22 @@ function sai_taskinfo_render(t, now_ut) sai_arts = ""; } + if (t.runs && t.runs.length > 1) { + var r1 = "", r2 = ""; + s += "<div class=\"runs-header-container\"><table class=\"runs-table\"><tr>"; + for (var n = t.runs.length - 1; n >= 0; n--) { + var r = t.runs[n]; + var ridx = typeof r.run !== 'undefined' ? r.run : 0; + var current = (ridx == (typeof t.t.run !== 'undefined' ? t.t.run : 0)); + var dcl = current ? "run-current-decal" : "run-decal"; + var decal = "<div class=\"taskstate taskstate" + r.state + " " + dcl + "\"><a href=\"?task=" + t.t.uuid + "&run=" + ridx + "\">" + sai_plat_icon(r.platform, 0) + "</a></div>"; + var timeStr = r.started ? agify(now_ut, r.started) + " ago" : "pending"; + r1 += "<td>" + decal + "</td>"; + r2 += "<td><span class=\"ti5\">" + timeStr + "</span></td>"; + } + s += r1 + "</tr><tr>" + r2 + "</tr></table></div>"; + } + s += "</td></tr>"; s += "</td></tr></table></table>"; @@ -1159,28 +1175,28 @@ function summarize_build_situation(event_uuid) if (!roo) return { text: "" }; - same = roo.querySelectorAll(".taskstate"); + same = roo.querySelectorAll(".taskstate:not(.run-decal)"); if (same) total = same.length; - same = roo.querySelectorAll(".taskstate0"); + same = roo.querySelectorAll(".taskstate0:not(.run-decal)"); if (same) pending = same.length; - same = roo.querySelectorAll(".taskstate1"); + same = roo.querySelectorAll(".taskstate1:not(.run-decal)"); if (same) ongoing += same.length; - same = roo.querySelectorAll(".taskstate2"); + same = roo.querySelectorAll(".taskstate2:not(.run-decal)"); if (same) ongoing += same.length; - same = roo.querySelectorAll(".taskstate3"); + same = roo.querySelectorAll(".taskstate3:not(.run-decal)"); if (same) good = same.length; - same = roo.querySelectorAll(".taskstate4"); + same = roo.querySelectorAll(".taskstate4:not(.run-decal)"); if (same) bad += same.length; - same = roo.querySelectorAll(".taskstate5"); + same = roo.querySelectorAll(".taskstate5:not(.run-decal)"); if (same) bad += same.length; // treat cancelled as bad - same = roo.querySelectorAll(".taskstate6"); + same = roo.querySelectorAll(".taskstate6:not(.run-decal)"); if (same) ongoing += same.length; @@ -1348,9 +1364,25 @@ function sai_event_render(o, now_ut, reset_all_icon) s += "<table><tr><td class=\"atop\">"; + var run_max = {}, run_list = {}; + for (q = 0; q < o.t.length; q++) { + var tx = o.t[q]; + var ru = typeof tx.run !== 'undefined' ? tx.run : 0; + if (!run_list[tx.uuid]) run_list[tx.uuid] = []; + run_list[tx.uuid].push(tx); + if (typeof run_max[tx.uuid] === 'undefined' || ru > (typeof run_max[tx.uuid].run !== 'undefined' ? run_max[tx.uuid].run : 0)) + run_max[tx.uuid] = tx; + } + for (var uid in run_list) { + run_list[uid].sort(function(a, b) { var ar = typeof a.run !== 'undefined' ? a.run : 0; var br = typeof b.run !== 'undefined' ? b.run : 0; return ar - br; }); + } + for (q = 0; q < o.t.length; q++) { var t = o.t[q]; + if (t !== run_max[t.uuid]) + continue; + if (t.taskname !== ctn) { if (ctn !== "") { s += "<div class=\"ib\"><table class=\"nomar\">" + @@ -1363,10 +1395,22 @@ function sai_event_render(o, now_ut, reset_all_icon) } s1 += "<div id=\"taskstate_" + t.uuid + "\" class=\"taskstate taskstate" + t.state + + (run_list[t.uuid].length > 1 ? " has_runs" : "") + "\" data-event-uuid=\"" + san(e.uuid) + "\" data-platform=\"" + san(t.platform) + "\" data-rebuildable=\"" + t.rebuildable + "\">"; s1 += "<a href=\"/sai/index.html?task=" + t.uuid + "\">" + sai_plat_icon(t.platform, 0) + "</a>"; + if (run_list[t.uuid].length > 1) { + s1 += "<div class=\"runs-popup\"><table>"; + for (var w = 0; w < run_list[t.uuid].length; w++) { + var rt = run_list[t.uuid][w]; + var rr = typeof rt.run !== 'undefined' ? rt.run : 0; + var decal = "<div class=\"taskstate taskstate" + rt.state + " run-decal\"><a href=\"/sai/index.html?task=" + t.uuid + "&run=" + rr + "\">" + sai_plat_icon(rt.platform, 0) + "</a></div>"; + var timeStr = rt.started ? agify(now_ut, rt.started) + " ago" : "pending"; + s1 += "<tr><td>" + decal + "</td><td class=\"runs-time-cell\"><span class=\"ti5\">" + timeStr + "</span></td></tr>"; + } + s1 += "</table></div>"; + } s1 += "</div>"; } @@ -1385,7 +1429,7 @@ function sai_event_render(o, now_ut, reset_all_icon) s += "</tr>"; - return s; + return "<tbody id=\"ev-group-" + o.e.uuid + "\">" + s + "</tbody>"; } function getBuilderHostname(platName) { @@ -2032,10 +2076,10 @@ function ws_open_sai() document.body.classList.remove("overlay-active"); var par = new URLSearchParams(window.location.search), - tid, eid; + tid, eid, run_idx; tid = par.get('task'); eid = par.get('event'); - + run_idx = par.get('run'); if (tid) { /* @@ -2046,13 +2090,17 @@ function ws_open_sai() console.log("tid " + tid); - sai.send("{\"schema\":" + + var req = "{\"schema\":" + "\"com.warmcat.sai.taskinfo\"," + "\"js_api_version\": " + SAI_JS_API_VERSION + "," + "\"logs\": 1," + - "\"last_log_ts\":" + last_log_timestamp + "," + - "\"task_hash\":" + - JSON.stringify(tid) + "}"); + "\"last_log_ts\":" + last_log_timestamp + ","; + if (run_idx) + req += "\"run\":" + run_idx + ","; + else + req += "\"run\": -1,"; + req += "\"task_hash\":" + JSON.stringify(tid) + "}"; + sai.send(req); return; } @@ -2302,15 +2350,16 @@ function ws_open_sai() // console.log("jso.overview.length " + jso.overview.length); if (jso.overview.length == 1 && - document.getElementById("esr-" + jso.overview[0].e.uuid)) { - /* this is just the summary box, not the tasks */ - document.getElementById("esr-" + jso.overview[0].e.uuid).innerHTML = - sai_event_summary_render(jso.overview[0], now_ut, 1); - - /* if the task status icons exist, update their state */ - - for (n = jso.overview[0].t.length - 1; n >= 0; n--) - refresh_state(jso.overview[0].t[n]); + document.getElementById("ev-group-" + jso.overview[0].e.uuid)) { + /* completely replace the event HTML to capture new runs/tasks */ + document.getElementById("ev-group-" + jso.overview[0].e.uuid).outerHTML = + sai_event_render(jso.overview[0], now_ut, 1); + + /* restore progress bars via refresh_state */ + if (jso.overview[0].t) { + for (n = jso.overview[0].t.length - 1; n >= 0; n--) + refresh_state(jso.overview[0].t[n]); + } update_summary_and_progress(jso.overview[0].e.uuid); @@ -2469,7 +2518,15 @@ function ws_open_sai() /* update task summary if shown anywhere */ if (document.getElementById("taskinfo-" + jso.t.uuid)) { - console.log("FOUND taskinfo-" + jso.t.uuid); + if (typeof window.current_task_run !== 'undefined' && window.current_task_run !== jso.t.run) { + if (document.getElementById("sai-task-logs")) + document.getElementById("sai-task-logs").innerHTML = ""; + /* update the URL without reloading so sharing works */ + const newUrl = new URL(window.location.href); + newUrl.searchParams.set('run', jso.t.run); + window.history.replaceState({}, '', newUrl); + } + window.current_task_run = jso.t.run; document.getElementById("taskinfo-" + jso.t.uuid).innerHTML = sai_taskinfo_render(jso); if (jso.e) { if (document.getElementById("esr-" + jso.e.uuid)) @@ -2495,6 +2552,7 @@ function ws_open_sai() if (url_task_uuid === jso.t.uuid && document.getElementById("sai_sticky")) { + window.current_task_run = jso.t.run; document.getElementById("sai_sticky").innerHTML = "<div class=\"taskinfo\" id=\"taskinfo-" + san(jso.t.uuid) + "\">" + @@ -2536,15 +2594,21 @@ function ws_open_sai() console.log(rs); sai.send(rs); + var tid = san(e.srcElement.id.substring(8)); + if (new URLSearchParams(window.location.search).get('run')) { + window.location.search = '?task=' + tid; + return; + } + /* * and immediately re-request the task info, so we can get * the new logs */ - var tid = san(e.srcElement.id.substring(8)); var rq = "{\"schema\":" + "\"com.warmcat.sai.taskinfo\"," + "\"js_api_version\": " + SAI_JS_API_VERSION + "," + "\"logs\": 1," + + "\"run\": -1," + "\"last_log_ts\":" + last_log_timestamp + "," + "\"task_hash\":" + JSON.stringify(tid) + "}"; diff --git a/assets/tc-msvc-schannel.svg b/assets/tc-msvc-schannel.svg new file mode 100644 index 0000000..dd47f12 --- /dev/null +++ b/assets/tc-msvc-schannel.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3.816 3.816" height="14.423" width="14.423" xmlns:v="https://vecta.io/nano"><circle r="1.908" cy="1.908" cx="1.908" opacity=".12" fill="#f9f9f9"/><path d="M2.382 3.194c-.03-.01-.154-.14-.475-.495l-.448-.48c-.007 0-.14.104-.297.232-.33.27-.3.258-.483.183-.172-.07-.156-.002-.16-.713s-.01-.663.115-.724c.217-.105.192-.113.533.166l.29.23c.005 0 .208-.218.45-.485.456-.502.467-.512.553-.512.028 0 .61.275.64.303.056.05.055.024.055 1.01 0 1.113.032.967-.244 1.102-.407.198-.45.213-.53.183zm.117-1.3l-.01-.524a19.92 19.92 0 0 0-.634.52c-.014.017-.012.023.018.047l.322.262.296.23c.005-.003.01-.243.01-.534zm-1.4.1a.56.56 0 0 0 .075-.095 2.9 2.9 0 0 0-.298-.326c-.015 0-.026.556-.012.627l.008.04.076-.082.15-.166z" fill="#b88aed"/><path d="M2.312 3.12l-.395-.43-.387-.426-.07-.074-.268.22c-.37.305-.33.29-.502.214-.18-.078-.163.002-.162-.714s-.014-.64.142-.707c.188-.08.163-.088.407.11l.265.216.112.086c.006 0 .453-.485.792-.86.11-.12.14-.14.188-.13.058.015.063.046.063.394v.32l-.336.276-.34.288c-.004.013.003.02.407.347l.273.227c.015.022.01.635-.004.668-.012.026-.062.054-.095.054-.006 0-.046-.036-.09-.08zM1.036 2.084l.16-.186a3.42 3.42 0 0 0-.331-.359c-.015 0-.03.67-.015.706.01.028.026.014.186-.162zM2.485 3.19c.008-.003.02-.003.022.001-.03.006-.032.003-.022-.001zm.66-.39zm0-.035z" fill="#9260cc"/><path d="M.708 2.62c-.202-.083-.18.002-.18-.72l.012-.607c.012-.055.084-.012.186.1l.107.116.004.174.006.39c.001.26-.013.256.183.045.175-.19.175-.19.22-.136l.11.12c.04.044.075.084.075.09s-.214.188-.495.415c-.083.067-.1.068-.227.016zm1.043-.795c-.067-.058-.07-.064-.043-.078.053-.027.14-.025.175.003l.034.028-.046.048c-.057.058-.05.058-.12-.001zm.187-.087c-.012-.013-.01-.017.018-.024.062-.015.18-.114.195-.164.007-.024.078-.037.078-.015 0 .013-.042.034-.05.025a1.37 1.37 0 0 1-.207.184c-.01.006-.023.003-.032-.007zm-.315-.112c.013-.001.02.006.02.008 0 .01-.01.005-.02-.008zm-.122-.02c0-.006.005-.015.012-.02.012.01.006.02 0 .023zm.522-.001c.004-.006.012-.012.018-.012.01.018.003.023-.007.023zm-.432-.1c.012-.016.026-.03.032-.03s-.001.013-.017.03c-.035.037-.042.038-.015 0zm.666-.018c.004-.006.013-.012.02-.012 0 .018-.008.023-.014.023zm.055-.036c.003-.008.012-.018.02-.02.008.02-.002.028-.01.03zm-.636-.04zm.035-.035z" fill="#6e40a4"/></svg> diff --git a/src/common/c-sqlite3.c b/src/common/c-sqlite3.c index c37f028..0aeab79 100644 --- a/src/common/c-sqlite3.c +++ b/src/common/c-sqlite3.c @@ -75,7 +75,7 @@ sai_event_db_ensure_open(struct lws_context *cx, lws_dll2_owner_t *sqlite3_cache return 3; } - sai_sqlite3_statement(*ppdb, "CREATE UNIQUE INDEX IF NOT EXISTS idx_task_uuid ON tasks(uuid);", "create task index"); + sai_sqlite3_statement(*ppdb, "CREATE UNIQUE INDEX IF NOT EXISTS idx_task_uuid ON tasks(uuid, run);", "create task index"); sai_sqlite3_statement(*ppdb, "PRAGMA journal_mode=WAL;", "set WAL"); @@ -95,6 +95,11 @@ sai_event_db_ensure_open(struct lws_context *cx, lws_dll2_owner_t *sqlite3_cache sai_sqlite3_statement(*ppdb, "CREATE INDEX IF NOT EXISTS idx_art_task ON artifacts(task_uuid);", "create artifact index"); + + /* Migrate the unique index to include the run column */ + sqlite3_exec(*ppdb, "DROP INDEX IF EXISTS idx_task_uuid;", NULL, NULL, NULL); + sqlite3_exec(*ppdb, "CREATE UNIQUE INDEX idx_task_uuid ON tasks(uuid, run);", NULL, NULL, NULL); + sc = malloc(sizeof(*sc)); if (!sc) { lwsl_err("%s: unable to alloc sc for %s\n", __func__, filepath); diff --git a/src/common/include/private.h b/src/common/include/private.h index b4773c8..cdf0ba9 100644 --- a/src/common/include/private.h +++ b/src/common/include/private.h @@ -221,6 +221,7 @@ typedef struct { char told_ongoing; char rebuildable; + int run; } sai_task_t; struct saib_logproxy { @@ -374,6 +375,7 @@ typedef struct { /* builder can report this along with step completion */ unsigned int avail_mem_kib; unsigned int avail_sto_kib; + int run; } sai_log_t; typedef struct { @@ -395,6 +397,7 @@ typedef struct { int uid; int fd; char sent_json; + int run; } sai_artifact_t; /* communication part of resource allocation requests */ @@ -624,6 +627,7 @@ typedef struct sai_browse_rx_taskinfo { unsigned int js_api_version; unsigned int offset; uint8_t logs; + int run; } sai_browse_rx_taskinfo_t; /* sai-power -> sai-server, tells it that a platform is being powered up */ @@ -794,9 +798,9 @@ extern const lws_struct_map_t lsm_schema_map_ta[1], lsm_schema_map_plat_simple[1], lsm_event[12], - lsm_task[30], - lsm_log[7], - lsm_artifact[8], + lsm_task[31], + lsm_log[8], + lsm_artifact[9], lsm_plat_list[1], lsm_schema_map_plat[1], lsm_task_rej[4], diff --git a/src/common/struct-metadata.c b/src/common/struct-metadata.c index a07826d..242ead9 100644 --- a/src/common/struct-metadata.c +++ b/src/common/struct-metadata.c @@ -178,6 +178,7 @@ const lws_struct_map_t lsm_task[] = { LSM_UNSIGNED (sai_task_t, est_compute_ms, "est_compute_ms"), LSM_SIGNED (sai_task_t, parallel, "parallel"), LSM_SIGNED (sai_task_t, rebuildable, "rebuildable"), + LSM_SIGNED (sai_task_t, run, "run"), }; const lws_struct_map_t lsm_schema_json_map_task[] = { @@ -242,6 +243,7 @@ const lws_struct_map_t lsm_log[] = { LSM_UNSIGNED (sai_log_t, finished, "finished"), LSM_CARRAY (sai_log_t, task_uuid, "task_uuid"), LSM_STRING_PTR (sai_log_t, log, "log"), + LSM_SIGNED (sai_log_t, run, "run"), }; const lws_struct_map_t lsm_schema_json_map_log[] = { @@ -288,6 +290,7 @@ const lws_struct_map_t lsm_artifact[] = { LSM_BLOB_PTR (sai_artifact_t, blob, "blob"), LSM_UNSIGNED (sai_artifact_t, timestamp, "timestamp"), LSM_UNSIGNED (sai_artifact_t, len, "len"), + LSM_SIGNED (sai_artifact_t, run, "run"), }; const lws_struct_map_t lsm_schema_json_map_artifact[] = { diff --git a/src/power/p-ws-server.c b/src/power/p-ws-server.c index 02403b1..1e88492 100644 --- a/src/power/p-ws-server.c +++ b/src/power/p-ws-server.c @@ -202,8 +202,8 @@ saip_m_rx(void *userobj, const uint8_t *buf, size_t len, int flags) lws_struct_args_t a; struct lejp_ctx ctx; - lwsl_notice("%s: PPPPPPPP len %d, flags: %d (saip_server_t %p)\n", __func__, (int)len, flags, (void *)sps); - lwsl_hexdump_notice(buf, len); + // lwsl_notice("%s: PPPPPPPP len %d, flags: %d (saip_server_t %p)\n", __func__, (int)len, flags, (void *)sps); + // lwsl_hexdump_notice(buf, len); memset(&a, 0, sizeof(a)); a.map_st[0] = lsm_saip_rx_map; diff --git a/src/server/s-central.c b/src/server/s-central.c index 82fd9ab..2a08d86 100644 --- a/src/server/s-central.c +++ b/src/server/s-central.c @@ -119,7 +119,8 @@ sais_central_clean_abandoned(struct vhd *vhd) lws_snprintf(s, sizeof(s), "SELECT uuid FROM tasks WHERE " "(state = %d OR state = %d) AND " - "started != 0 AND started < %llu", + "started != 0 AND started < %llu AND " + "run=(SELECT max(run) FROM tasks t2 WHERE t2.uuid = tasks.uuid)", SAIES_PASSED_TO_BUILDER, SAIES_BEING_BUILT, (unsigned long long) (lws_now_secs() - @@ -226,7 +227,8 @@ sais_central_gc_deleted_events_cb(lws_sorted_usec_list_t *sul) /* Pick 1 task that is stopped to garbage collect */ /* 0=WAITING, 3=SUCCESS, 4=FAIL, 5=CANCELLED, 8=NOT_READY, 9=STEP_SUCCESS, 10=PAUSED */ - lws_snprintf(q, sizeof(q), "SELECT uuid FROM tasks WHERE state IN (0, 3, 4, 5, 8, 9, 10) LIMIT 1"); + lws_snprintf(q, sizeof(q), "SELECT uuid FROM tasks WHERE state IN (0, 3, 4, 5, 8, 9, 10) " + "AND run=(SELECT max(run) FROM tasks t2 WHERE t2.uuid = tasks.uuid) LIMIT 1"); if (sqlite3_prepare_v2(pdb, q, -1, &tsm, NULL) == SQLITE_OK) { if (sqlite3_step(tsm) == SQLITE_ROW) { diff --git a/src/server/s-task-helpers.c b/src/server/s-task-helpers.c index b6c7d2b..2fa22aa 100644 --- a/src/server/s-task-helpers.c +++ b/src/server/s-task-helpers.c @@ -76,7 +76,7 @@ int sais_bind_task_to_builder(struct vhd *vhd, const char *builder_name, const char *builder_uuid, const char *task_uuid) { - char update[384], esc[96], esc1[96], esc2[96], event_uuid[33]; + char update[512], esc[96], esc1[96], esc2[96], event_uuid[33]; struct lwsac *ac = NULL; sai_event_t *e = NULL; lws_dll2_owner_t o; @@ -132,8 +132,8 @@ sais_bind_task_to_builder(struct vhd *vhd, const char *builder_name, */ lws_snprintf(update, sizeof(update), - "update tasks set builder='%s',builder_name='%s' where uuid='%s'", - esc1, esc, esc2); + "update tasks set builder='%s',builder_name='%s' where uuid='%s' and run=(select max(run) from tasks where uuid='%s')", + esc1, esc, esc2, esc2); if (sqlite3_exec((sqlite3 *)e->pdb, update, NULL, NULL, NULL) != SQLITE_OK) { lwsl_err("%s: %s: %s: fail\n", __func__, update, @@ -155,7 +155,7 @@ int sais_set_task_state(struct vhd *vhd, const char *task_uuid, sai_event_state_t state, uint64_t started, uint64_t duration) { - char update[384], esc1[96], esc2[96], esc3[32], esc4[32], event_uuid[33]; + char update[512], esc1[96], esc2[96], esc3[32], esc4[32], event_uuid[33]; sai_event_state_t oes, sta, task_ostate, ostate = state; unsigned int count = 0, count_good = 0, count_bad = 0; uint64_t started_orig = started; @@ -207,7 +207,7 @@ sais_set_task_state(struct vhd *vhd, const char *task_uuid, * grab the current state of it for seeing if it changed */ lws_snprintf(update, sizeof(update), - "select state from tasks where uuid='%s'", esc2); + "select state from tasks where uuid='%s' order by run desc limit 1", esc2); if (sqlite3_exec((sqlite3 *)e->pdb, update, sql3_get_integer_cb, &task_ostate, NULL) != SQLITE_OK) { lwsl_err("%s: %s: %s: fail\n", __func__, update, @@ -240,9 +240,9 @@ sais_set_task_state(struct vhd *vhd, const char *task_uuid, */ lws_snprintf(update, sizeof(update), - "update tasks set state=%d%s%s%s where uuid='%s'", state, + "update tasks set state=%d%s%s%s where uuid='%s' and run=(select max(run) from tasks where uuid='%s')", state, esc3, esc4, state == SAIES_WAITING && started_orig == 1 ? - ",build_step=0" : "", esc2); + ",build_step=0" : "", esc2, esc2); if (sqlite3_exec((sqlite3 *)e->pdb, update, NULL, NULL, NULL) != SQLITE_OK) { lwsl_err("%s: %s: %s: fail\n", __func__, update, @@ -280,7 +280,7 @@ sais_set_task_state(struct vhd *vhd, const char *task_uuid, * So, how many tasks for this event? */ - if (sqlite3_exec((sqlite3 *)e->pdb, "select count(state) from tasks", + if (sqlite3_exec((sqlite3 *)e->pdb, "select count(*) from (select max(run) from tasks group by uuid)", sql3_get_integer_cb, &count, NULL) != SQLITE_OK) { lwsl_err("%s: %s: %s: fail\n", __func__, update, sqlite3_errmsg(vhd->server.pdb)); @@ -291,7 +291,7 @@ sais_set_task_state(struct vhd *vhd, const char *task_uuid, * ... how many completed well? */ - if (sqlite3_exec((sqlite3 *)e->pdb, "select count(state) from tasks where state == 3", + if (sqlite3_exec((sqlite3 *)e->pdb, "select count(*) from (select max(run) as mx, state from tasks group by uuid) where state == 3", sql3_get_integer_cb, &count_good, NULL) != SQLITE_OK) { lwsl_err("%s: %s: %s: fail\n", __func__, update, sqlite3_errmsg(vhd->server.pdb)); @@ -302,7 +302,7 @@ sais_set_task_state(struct vhd *vhd, const char *task_uuid, * ... how many failed? */ - if (sqlite3_exec((sqlite3 *)e->pdb, "select count(state) from tasks where state == 4", + if (sqlite3_exec((sqlite3 *)e->pdb, "select count(*) from (select max(run) as mx, state from tasks group by uuid) where state == 4", sql3_get_integer_cb, &count_bad, NULL) != SQLITE_OK) { lwsl_err("%s: %s: %s: fail\n", __func__, update, sqlite3_errmsg(vhd->server.pdb)); @@ -375,7 +375,7 @@ bail: int sais_task_pause(struct vhd *vhd, const char *task_uuid) { - char event_uuid[33], esc_uuid[129], q[128]; + char event_uuid[33], esc_uuid[129], q[384]; int build_step = -1, state = -1; sqlite3 *pdb = NULL; @@ -388,7 +388,7 @@ sais_task_pause(struct vhd *vhd, const char *task_uuid) lws_sql_purify(esc_uuid, task_uuid, sizeof(esc_uuid)); lws_snprintf(q, sizeof(q), - "select build_step,state from tasks where uuid='%s'", + "select build_step,state from tasks where uuid='%s' order by run desc limit 1", esc_uuid); if (sqlite3_exec(pdb, q, sql3_get_integer_cb, &build_step, @@ -408,8 +408,8 @@ sais_task_pause(struct vhd *vhd, const char *task_uuid) if (build_step > 0) { build_step--; lws_snprintf(q, sizeof(q), - "update tasks set build_step=%d where uuid='%s'", - build_step, esc_uuid); + "update tasks set build_step=%d where uuid='%s' and run=(select max(run) from tasks where uuid='%s')", + build_step, esc_uuid, esc_uuid); sqlite3_exec(pdb, q, NULL, NULL, NULL); } sais_task_stop_on_builders(vhd, task_uuid); @@ -486,7 +486,7 @@ sais_task_stop_on_builders(struct vhd *vhd, const char *task_uuid) builder_name[0] = '\0'; lws_sql_purify(esc_uuid, task_uuid, sizeof(esc_uuid)); - lws_snprintf(q, sizeof(q), "select builder_name from tasks where uuid='%s'", + lws_snprintf(q, sizeof(q), "select builder_name from tasks where uuid='%s' order by run desc limit 1", esc_uuid); if (sqlite3_exec(pdb, q, sql3_get_string_cb, builder_name, NULL) != SQLITE_OK || @@ -565,36 +565,51 @@ sais_task_clear_build_and_logs(struct vhd *vhd, const char *task_uuid, int from_ return SAI_DB_RESULT_ERROR; } - lws_sql_purify(esc, task_uuid, sizeof(esc)); - lws_snprintf(cmd, sizeof(cmd), "delete from logs where task_uuid='%s'", - esc); - - ret = sqlite3_exec(pdb, cmd, NULL, NULL, NULL); - if (ret != SQLITE_OK) { - sai_event_db_close(&vhd->sqlite3_cache, &pdb); - if (ret == SQLITE_BUSY) - return SAI_DB_RESULT_BUSY; - lwsl_err("%s: %s: %s: fail\n", __func__, cmd, - sqlite3_errmsg(pdb)); - return SAI_DB_RESULT_ERROR; - } - lws_snprintf(cmd, sizeof(cmd), "delete from artifacts where task_uuid='%s'", - esc); + struct lwsac *ac = NULL; + lws_dll2_owner_t o; - ret = sqlite3_exec(pdb, cmd, NULL, NULL, NULL); - if (ret != SQLITE_OK) { - sai_event_db_close(&vhd->sqlite3_cache, &pdb); - if (ret == SQLITE_BUSY) - return SAI_DB_RESULT_BUSY; - lwsl_err("%s: %s: %s: fail\n", __func__, cmd, - sqlite3_errmsg(pdb)); - return SAI_DB_RESULT_ERROR; + lws_sql_purify(esc, task_uuid, sizeof(esc)); + lws_snprintf(cmd, sizeof(cmd), " and uuid='%s'", esc); + + ret = lws_struct_sq3_deserialize(pdb, cmd, "run desc", lsm_schema_sq3_map_task, &o, &ac, 0, 1); + if (ret >= 0 && o.head) { + sai_task_t *t = lws_container_of(o.head, sai_task_t, list); + + if (!from_rejection) + t->run++; + + t->started = 0; + t->duration = 0; + t->build_step = 0; + + if (!from_rejection) { + /* Reset builder bindings for the new run so it can be picked up by any suitable builder */ + t->builder[0] = '\0'; + t->builder_name[0] = '\0'; + t->server_name = ""; + /* serialize as a new row */ + lws_struct_sq3_serialize(pdb, lsm_schema_sq3_map_task, &o, 0); + } else { + t->state = SAIES_WAITING; + /* update the existing row if it's a rejection */ + lws_snprintf(cmd, sizeof(cmd), + "update tasks set state=%d,started=0,duration=0,build_step=0 where uuid='%s' and run=%d", + SAIES_WAITING, esc, t->run); + sqlite3_exec(pdb, cmd, NULL, NULL, NULL); + } } - + + lwsac_free(&ac); sai_event_db_close(&vhd->sqlite3_cache, &pdb); - /* 1,1 == reset started and duration in db for task to 0 */ - sais_set_task_state(vhd, task_uuid, SAIES_WAITING, 1, 1); + if (!from_rejection) { + sais_set_task_state(vhd, task_uuid, SAIES_WAITING, 0, 0); + sais_taskchange(vhd->h_ss_websrv, task_uuid, SAIES_WAITING); + sais_eventchange(vhd->h_ss_websrv, event_uuid, SAIES_WAITING); + } else { + sais_taskchange(vhd->h_ss_websrv, task_uuid, SAIES_WAITING); + sais_eventchange(vhd->h_ss_websrv, event_uuid, SAIES_WAITING); + } sais_task_stop_on_builders(vhd, task_uuid); @@ -623,7 +638,7 @@ sais_task_clear_build_and_logs(struct vhd *vhd, const char *task_uuid, int from_ sai_db_result_t sais_task_rebuild_last_step(struct vhd *vhd, const char *task_uuid) { - char esc[96], cmd[256], event_uuid[33]; + char esc[96], cmd[384], event_uuid[33]; struct lwsac *ac = NULL; sqlite3 *pdb = NULL; lws_dll2_owner_t o; @@ -660,8 +675,8 @@ sais_task_rebuild_last_step(struct vhd *vhd, const char *task_uuid) if (task->build_step > 0) { lws_snprintf(cmd, sizeof(cmd), - "update tasks set build_step=%d where uuid='%s'", - task->build_step - 1, esc); + "update tasks set build_step=%d where uuid='%s' and run=(select max(run) from tasks where uuid='%s')", + task->build_step - 1, esc, esc); ret = sqlite3_exec(pdb, cmd, NULL, NULL, NULL); if (ret != SQLITE_OK) { diff --git a/src/server/s-task.c b/src/server/s-task.c index c2a2668..9bb4ca7 100644 --- a/src/server/s-task.c +++ b/src/server/s-task.c @@ -165,6 +165,29 @@ sais_prune_inflight_list(struct vhd *vhd) /* + * Helper to identify and fix stale corrupted legacy tasks + */ +static int +sais_check_and_fix_stale_task(sqlite3 *pdb, sai_task_t *t) +{ + int max_run = -1; + char q[200], esc[100]; + + lws_sql_purify(esc, t->uuid, sizeof(esc)); + lws_snprintf(q, sizeof(q), "select max(run) from tasks where uuid='%s'", esc); + + if (sqlite3_exec(pdb, q, sql3_get_integer_cb, &max_run, NULL) == SQLITE_OK) { + if (max_run >= 0 && (int)t->run < max_run) { + lwsl_err("%s: task %s run %d is stale (max %d), marking deleted\n", __func__, esc, t->run, max_run); + lws_snprintf(q, sizeof(q), "update tasks set state=7 where uuid='%s' and run=%d", esc, t->run); + sqlite3_exec(pdb, q, NULL, NULL, NULL); + return 1; + } + } + return 0; +} + +/* * Find the most recent task that still needs doing for platform, on any event */ static const sai_task_t * @@ -234,8 +257,8 @@ sais_task_pending(struct vhd *vhd, struct pss *pss, sai_plat_t *cb, */ lws_snprintf(query, sizeof(query), "select count(state) from tasks where " - "(state = 0 or state = 9) and platform = '%s' and " - "(builder_name IS NULL or builder_name = '' or builder_name = '%s')", + "state IN(0,9) and platform='%s' and " + "(builder_name IS NULL or builder_name IN('','%s'))", esc_plat, esc_bname); m = sqlite3_exec(pdb, query, sql3_get_integer_cb, &pending_count, NULL); @@ -367,9 +390,8 @@ sais_task_pending(struct vhd *vhd, struct pss *pss, sai_plat_t *cb, lws_sql_purify(esc_taskname, fti->taskname, sizeof(esc_taskname)); lws_snprintf(pf, sizeof(pf), - " and (state == 0 or state == 9) and " - "(platform == '%s') and (taskname == '%s') and " - "(builder_name IS NULL or builder_name == '' or builder_name == '%s')", + " and state IN(0,9) and platform='%s' and taskname='%s' and " + "(builder_name IS NULL or builder_name IN('','%s'))", esc_plat, esc_taskname, esc_bname); lwsac_free(&pss->ac_alloc_task); @@ -380,6 +402,14 @@ sais_task_pending(struct vhd *vhd, struct pss *pss, sai_plat_t *cb, if (n < 0 || !owner.count) goto next1; + sai_task_t *t = lws_container_of(owner.head, sai_task_t, list); + if (sais_check_and_fix_stale_task(pdb, t)) { + sai_event_db_close(&vhd->sqlite3_cache, &pdb); + lwsac_free(&ac); + lwsac_free(&failed_ac); + return NULL; + } + lwsl_notice("%s: Prioritizing failed task for %s ('%s')\n", __func__, platform, fti->taskname); @@ -399,8 +429,8 @@ next1: ; /* We have fallen back to doing tasks earliest-first */ lws_snprintf(pf, sizeof(pf), - " and (state = 0 or state = 9) and (platform = '%s') and " - "(builder_name IS NULL or builder_name = '' or builder_name = '%s')", + " and state IN(0,9) and platform='%s' and " + "(builder_name IS NULL or builder_name IN('','%s'))", esc_plat, esc_bname); lwsac_free(&pss->ac_alloc_task); @@ -413,6 +443,14 @@ next1: ; if (n < 0 || !owner.count || !pss->ac_alloc_task) goto close_next; + sai_task_t *t = lws_container_of(owner.head, sai_task_t, list); + if (sais_check_and_fix_stale_task(pdb, t)) { + sai_event_db_close(&vhd->sqlite3_cache, &pdb); + lwsac_free(&ac); + lwsac_free(&failed_ac); + return NULL; + } + lwsl_info("%s: orig exit\n", __func__); sai_event_db_close(&vhd->sqlite3_cache, &pdb); lwsac_free(&ac); @@ -815,7 +853,7 @@ sais_create_and_offer_task_step(struct vhd *vhd, const char *task_uuid) lws_sql_purify(esc_uuid, task_uuid, sizeof(esc_uuid)); lws_snprintf(update, sizeof(update), " and state != 4 and uuid='%s'", esc_uuid); - n = lws_struct_sq3_deserialize(pdb, update, NULL, + n = lws_struct_sq3_deserialize(pdb, update, "run desc", lsm_schema_sq3_map_task, &o, &ac, 0, 1); if (n < 0 || !o.head) { lwsl_warn("%s: bailing as nothing with state != 4\n", __func__); diff --git a/src/server/s-webops.c b/src/server/s-webops.c index bd665aa..22f68ec 100644 --- a/src/server/s-webops.c +++ b/src/server/s-webops.c @@ -227,7 +227,7 @@ sais_event_reset(struct vhd *vhd, const char *event_uuid) vhd->sqlite3_path_lhs, event_uuid, 0, &pdb)) return SAI_DB_RESULT_ERROR; - if (lws_struct_sq3_deserialize(pdb, NULL, NULL, + if (lws_struct_sq3_deserialize(pdb, " and run=(select max(run) from tasks t2 where t2.uuid=tasks.uuid)", NULL, lsm_schema_sq3_map_task, &o, &ac, 0, 999) >= 0) { @@ -317,7 +317,8 @@ sais_event_delete(struct vhd *vhd, const char *event_uuid) /* 3. Drop active builders gracefully without loading huge JSON objects */ if (sai_event_db_ensure_open(vhd->context, &vhd->sqlite3_cache, vhd->sqlite3_path_lhs, event_uuid, 0, &pdb) == 0) { - lws_snprintf(qu, sizeof(qu), "SELECT uuid FROM tasks WHERE state != 0 AND state != 3 AND state != 4 AND state != 5"); + lws_snprintf(qu, sizeof(qu), "SELECT uuid FROM tasks WHERE state != 0 AND state != 3 AND state != 4 AND state != 5 " + "AND run=(SELECT max(run) FROM tasks t2 WHERE t2.uuid = tasks.uuid)"); if (sqlite3_prepare_v2(pdb, qu, -1, &sm, NULL) == SQLITE_OK) { while (sqlite3_step(sm) == SQLITE_ROW) { const unsigned char *task_uuid = sqlite3_column_text(sm, 0); @@ -360,7 +361,7 @@ sais_plat_reset(struct vhd *vhd, const char *event_uuid, const char *platform) return SAI_DB_RESULT_ERROR; lws_sql_purify(esc, platform, sizeof(esc)); - lws_snprintf(filt, sizeof(filt), " and platform='%s'", esc); + lws_snprintf(filt, sizeof(filt), " and platform='%s' and run=(select max(run) from tasks t2 where t2.uuid=tasks.uuid)", esc); if (lws_struct_sq3_deserialize(pdb, filt, NULL, lsm_schema_sq3_map_task, diff --git a/src/server/s-ws-builder.c b/src/server/s-ws-builder.c index 413f15e..6131260 100644 --- a/src/server/s-ws-builder.c +++ b/src/server/s-ws-builder.c @@ -116,6 +116,17 @@ sais_dump_logs_to_db(lws_sorted_usec_list_t *sul) * more efficient */ + int run = 0; + char q[128]; + + lws_snprintf(q, sizeof(q), "select max(run) from tasks where uuid='%s'", lcpt->uuid); + sqlite3_exec(pdb, q, sql3_get_integer_cb, &run, NULL); + + lws_start_foreach_dll(struct lws_dll2 *, pq, lcpt->cache.head) { + sai_log_t *hl = lws_container_of(pq, sai_log_t, list); + hl->run = run; + } lws_end_foreach_dll(pq); + sqlite3_exec(pdb, "BEGIN TRANSACTION", NULL, NULL, &err); if (err) sqlite3_free(err); @@ -251,8 +262,8 @@ sais_log_to_db(struct vhd *vhd, sai_log_t *log) lws_sql_purify(esc_uuid, log->task_uuid, sizeof(esc_uuid)); lws_snprintf(q, sizeof(q), - "UPDATE tasks SET build_step=%d WHERE uuid='%s'", - step, esc_uuid); + "UPDATE tasks SET build_step=%d WHERE uuid='%s' and run=(select max(run) from tasks where uuid='%s')", + step, esc_uuid, esc_uuid); if (sai_sqlite3_statement(pdb, q, "update build_step")) lwsl_err("%s: failed to update build_step\n", __func__); @@ -455,8 +466,8 @@ sais_builder_disconnected(struct vhd *vhd, struct lws *wsi) lws_snprintf(q, sizeof(q), "SELECT uuid FROM tasks WHERE " - "(state = %d OR state = %d) AND " - "builder_name = ?", + "builder_name=? AND (state = %d OR state = %d) " + "AND run=(SELECT max(run) FROM tasks t2 WHERE t2.uuid = tasks.uuid)", SAIES_PASSED_TO_BUILDER, SAIES_BEING_BUILT); @@ -532,7 +543,7 @@ static int sais_process_rej(struct vhd *vhd, struct pss *pss, sai_plat_t *sp, sai_rejection_t *rej) { - char event_uuid[33], do_remove_uuid = 0, q[128], esc_uuid[129]; + char event_uuid[33], do_remove_uuid = 0, q[384], esc_uuid[129]; int n, build_step = -1; sqlite3 *pdb = NULL; sai_uuid_list_t *ul; @@ -553,7 +564,7 @@ sais_process_rej(struct vhd *vhd, struct pss *pss, lws_sql_purify(esc_uuid, rej->task_uuid, sizeof(esc_uuid)); lws_snprintf(q, sizeof(q), - "select build_step from tasks where uuid='%s'", + "select build_step from tasks where uuid='%s' order by run desc limit 1", esc_uuid); if (sqlite3_exec(pdb, q, sql3_get_integer_cb, &build_step, @@ -567,22 +578,23 @@ sais_process_rej(struct vhd *vhd, struct pss *pss, build_step++; lws_snprintf(q, sizeof(q), "update tasks set build_step=%d " - "where state != 4 and uuid='%s'", - build_step, esc_uuid); - sqlite3_exec(pdb, q, NULL, NULL, NULL); + "where uuid='%s' and run=(select max(run) from tasks where uuid='%s')", + build_step, esc_uuid, esc_uuid); + if (sai_sqlite3_statement(pdb, q, "update build_step accepted")) + lwsl_err("%s: failed to update build_step\n", __func__); lwsl_notice("%s: &&&&&&&& build_step set to %d\n", __func__, build_step); if (build_step == 1) { pss->first_log_timestamp = (uint64_t)lws_now_secs(); lws_snprintf(q, sizeof(q), - "update tasks set started=%llu where uuid='%s'", - (unsigned long long)pss->first_log_timestamp, esc_uuid); + "update tasks set started=%llu where uuid='%s' and run=(select max(run) from tasks where uuid='%s')", + (unsigned long long)pss->first_log_timestamp, esc_uuid, esc_uuid); lwsl_warn("%s: &&&&&&&&&&&&&&&&&&&&&&&&&& setting task %s started to %llu\n", __func__, esc_uuid, (unsigned long long)pss->first_log_timestamp); - if (sqlite3_exec(pdb, q, NULL, NULL, NULL) != SQLITE_OK) + if (sai_sqlite3_statement(pdb, q, "update started")) lwsl_notice("%s: unable to set started\n", __func__); } @@ -1074,7 +1086,7 @@ sais_ws_json_rx_builder(struct vhd *vhd, struct pss *pss, uint8_t *buf, size_t b lws_sql_purify(esc, ap->task_uuid, sizeof(esc)); lws_snprintf(s, sizeof(s)," and uuid == \"%s\"", esc); n = lws_struct_sq3_deserialize(pss->pdb_artifact, s, - NULL, lsm_schema_sq3_map_task, + "run desc", lsm_schema_sq3_map_task, &o, &ac, 0, 1); if (n < 0 || !o.head) { sai_event_db_close(&vhd->sqlite3_cache, &pss->pdb_artifact); @@ -1108,6 +1120,8 @@ sais_ws_json_rx_builder(struct vhd *vhd, struct pss *pss, uint8_t *buf, size_t b sai_uuid16_create(pss->vhd->context, ap->artifact_down_nonce); + ap->run = task->run; + lws_dll2_owner_clear(&o); lws_dll2_add_head(&ap->list, &o); @@ -1210,7 +1224,7 @@ sais_ws_json_rx_builder(struct vhd *vhd, struct pss *pss, uint8_t *buf, size_t b ap = (sai_artifact_t *)pss->a.dest; lws_sql_purify(esc, ap->task_uuid, sizeof(esc)); - lws_snprintf(s, sizeof(s)," select state from tasks where uuid == \"%s\"", esc); + lws_snprintf(s, sizeof(s)," select state from tasks where uuid == \"%s\" order by run desc limit 1", esc); if (sqlite3_exec((sqlite3 *)pss->pdb_artifact, s, sql3_get_integer_cb, &state, NULL) != SQLITE_OK) { lwsl_err("%s: %s: %s: fail\n", __func__, s, diff --git a/src/server/s-ws-web.c b/src/server/s-ws-web.c index c8d4f6e..b665f1d 100644 --- a/src/server/s-ws-web.c +++ b/src/server/s-ws-web.c @@ -76,6 +76,7 @@ static lws_struct_map_t lsm_browser_taskinfo[] = { LSM_UNSIGNED (sai_browse_rx_taskinfo_t, logs, "logs"), LSM_UNSIGNED (sai_browse_rx_taskinfo_t, js_api_version, "js_api_version"), LSM_UNSIGNED (sai_browse_rx_taskinfo_t, last_log_ts, "last_log_ts"), + LSM_SIGNED (sai_browse_rx_taskinfo_t, run, "run"), }; static const lws_struct_map_t lsm_schema_json_map[] = { diff --git a/src/web/CMakeLists.txt b/src/web/CMakeLists.txt index 8ee5585..8eb8e40 100644 --- a/src/web/CMakeLists.txt +++ b/src/web/CMakeLists.txt @@ -142,6 +142,7 @@ add_dependencies(${SUB} sai_git_hash) ../../assets/tc-mingw32.svg ../../assets/tc-mingw64.svg ../../assets/tc-msvc.svg + ../../assets/tc-msvc-schannel.svg ../../assets/tc-wmbedtlsmsvc.svg ../../assets/tc-noptmsvc.svg ../../assets/ubuntu.svg diff --git a/src/web/w-private.h b/src/web/w-private.h index 8e4d74c..b2e0ae7 100644 --- a/src/web/w-private.h +++ b/src/web/w-private.h @@ -73,6 +73,7 @@ struct pss { uint64_t sub_timestamp; char sub_task_uuid[65]; + int sub_run; char specific_ref[65]; char specific_task[65]; char specific_project[96]; diff --git a/src/web/w-ws-browser.c b/src/web/w-ws-browser.c index 01188f5..bc3bff2 100644 --- a/src/web/w-ws-browser.c +++ b/src/web/w-ws-browser.c @@ -61,6 +61,7 @@ static lws_struct_map_t lsm_browser_taskinfo[] = { LSM_UNSIGNED (sai_browse_rx_taskinfo_t, js_api_version, "js_api_version"), LSM_UNSIGNED (sai_browse_rx_taskinfo_t, offset, "offset"), LSM_UNSIGNED (sai_browse_rx_taskinfo_t, last_log_ts, "last_log_ts"), + LSM_SIGNED (sai_browse_rx_taskinfo_t, run, "run"), }; /* @@ -120,6 +121,7 @@ enum { typedef struct sai_browse_taskreply { const sai_event_t *event; const sai_task_t *task; + lws_dll2_owner_t runs; } sai_browse_taskreply_t; static lws_struct_map_t lsm_taskreply[] = { @@ -127,6 +129,8 @@ static lws_struct_map_t lsm_taskreply[] = { lsm_event, "e"), LSM_CHILD_PTR (sai_browse_taskreply_t, task, sai_task_t, NULL, lsm_task, "t"), + LSM_LIST (sai_browse_taskreply_t, runs, sai_task_t, list, NULL, + lsm_task, "runs"), }; const lws_struct_map_t lsm_schema_json_map_taskreply[] = { @@ -257,13 +261,13 @@ bail: /* we leave an allocation in sch->query_ac ... */ static int -saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub) +saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub, int run_idx) { char qu[192], event_uuid[33], esc2[96], buf[4096 + LWS_PRE], *start = buf + LWS_PRE, *p = start, *end = buf + sizeof(buf); const sai_event_t *one_event = NULL; sai_browse_taskreply_t task_reply; - struct lwsac *query_ac = NULL; + struct lwsac *query_ac = NULL, *runs_ac = NULL, *art_ac = NULL; sai_task_t *one_task = NULL; lws_struct_serialize_t *js; char esc[256], filt[128]; @@ -310,9 +314,19 @@ saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub) */ lws_sql_purify(esc, task_uuid, sizeof(esc)); - lws_snprintf(qu, sizeof(qu), " and uuid='%s'", esc); - n = lws_struct_sq3_deserialize(pdb, qu, NULL, lsm_schema_sq3_map_task, + if (run_idx >= 0) + lws_snprintf(qu, sizeof(qu), " and uuid='%s' and run=%d", esc, run_idx); + else + lws_snprintf(qu, sizeof(qu), " and uuid='%s'", esc); + n = lws_struct_sq3_deserialize(pdb, qu, run_idx >= 0 ? NULL : "run desc", lsm_schema_sq3_map_task, &o, &query_ac, 0, 1); + + memset(&task_reply, 0, sizeof(task_reply)); + lws_dll2_owner_clear(&task_reply.runs); + lws_snprintf(qu, sizeof(qu), " and uuid='%s'", esc); + lws_struct_sq3_deserialize(pdb, qu, "run desc", lsm_schema_sq3_map_task, + &task_reply.runs, &runs_ac, 0, 100); + sai_event_db_close(&pss->vhd->sqlite3_cache, &pdb); if (n < 0 || !o.head) goto bail; @@ -364,8 +378,6 @@ saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub) else one_event = lws_container_of(o.head, sai_event_t, list); - memset(&task_reply, 0, sizeof(task_reply)); - /* * We're sending a browser the specific task info that he * asked for. @@ -392,9 +404,15 @@ saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub) do { n = (int)lws_struct_json_serialize(js, (uint8_t *)p, lws_ptr_diff_size_t(end, p), &w); + if (n == LSJS_RESULT_ERROR) { + lws_struct_json_serialize_destroy(&js); + lwsl_notice("%s: taskinfo: error generating json\n", __func__); + goto bail; + } + p += w; if (lws_ptr_diff_size_t(end, (uint8_t *)p) < 512) { - saiw_ws_broadcast_browsers_REQUIRES_LWS_PRE(pss->vhd, start, + saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start, lws_ptr_diff_size_t(p, start), lws_write_ws_flags(LWS_WRITE_TEXT, fi, 0)); p = start; @@ -417,23 +435,23 @@ saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub) pss->vhd->sqlite3_path_lhs, event_uuid, 0, &pdb)) { - lws_snprintf(filt, sizeof(filt), " and (task_uuid == '%s')", - one_task->uuid); + if (run_idx >= 0) + lws_snprintf(filt, sizeof(filt), " and (task_uuid == '%s') and run=%d", + one_task->uuid, run_idx); + else + lws_snprintf(filt, sizeof(filt), " and (task_uuid == '%s') and run=%d", + one_task->uuid, one_task->run); if (lws_struct_sq3_deserialize(pdb, filt, NULL, lsm_schema_sq3_map_artifact, &owner, - &query_ac, 0, 10)) + &art_ac, 0, 10)) lwsl_err("%s: get afcts failed\n", __func__); sai_event_db_close(&pss->vhd->sqlite3_cache, &pdb); } - if (n == LSJS_RESULT_ERROR) { - lwsl_notice("%s: taskinfo: error generating json\n", __func__); - goto bail; - } - p += w; + saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start, lws_ptr_diff_size_t(p, start), @@ -442,9 +460,18 @@ saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub) /* does he want to subscribe to logs? */ if (logsub && !pss->subs_list.owner) { strcpy(pss->sub_task_uuid, one_task->uuid); + pss->sub_run = run_idx >= 0 ? run_idx : one_task->run; lws_dll2_add_head(&pss->subs_list, &pss->vhd->subs_owner); pss->sub_timestamp = pss->initial_log_timestamp; /* where we got up to */ saiw_broadcast_logs_batch(pss->vhd, pss); + } else if (!strcmp(pss->sub_task_uuid, one_task->uuid)) { + /* If already subscribed to this task, track new runs automatically */ + int new_run = run_idx >= 0 ? run_idx : one_task->run; + if (pss->sub_run != new_run) { + pss->sub_run = new_run; + pss->sub_timestamp = 0; + saiw_broadcast_logs_batch(pss->vhd, pss); + } } saiw_browser_broadcast_queue_builders(pss->vhd, pss); @@ -492,11 +519,15 @@ saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub) } lwsac_free(&query_ac); + lwsac_free(&runs_ac); + lwsac_free(&art_ac); return 0; bail: lwsac_free(&query_ac); + lwsac_free(&runs_ac); + lwsac_free(&art_ac); return 1; } @@ -514,7 +545,7 @@ saiw_subs_task_state_change(struct vhd *vhd, const char *task_uuid) struct pss *pss = lws_container_of(p, struct pss, subs_list); if (!strcmp(pss->sub_task_uuid, task_uuid)) - saiw_pss_schedule_taskinfo(pss, task_uuid, 0); + saiw_pss_schedule_taskinfo(pss, task_uuid, 0, pss->sub_run); } lws_end_foreach_dll(p); @@ -529,7 +560,7 @@ saiw_browsers_task_state_change(struct vhd *vhd, const char *task_uuid) struct pss *pss = lws_container_of(p, struct pss, same); if (!pss->is_gitohashi) - saiw_pss_schedule_taskinfo(pss, task_uuid, 0); + saiw_pss_schedule_taskinfo(pss, task_uuid, 0, -1); } lws_end_foreach_dll(p); return 0; @@ -636,7 +667,7 @@ saiw_ws_json_rx_browser(struct vhd *vhd, struct pss *pss, uint8_t *buf, else pss->initial_log_timestamp = 0; - if (saiw_pss_schedule_taskinfo(pss, ti->task_hash, !!ti->logs)) + if (saiw_pss_schedule_taskinfo(pss, ti->task_hash, !!ti->logs, ti->run)) goto soft_error; goto ok; @@ -797,8 +828,8 @@ saiw_broadcast_logs_batch(struct vhd *vhd, struct pss *pss) lwsac_free(&pss->logs_ac); lws_snprintf(esc, sizeof(esc), - "and task_uuid='%s' and timestamp > %llu", - pss->sub_task_uuid, + "and task_uuid='%s' and run=%d and timestamp > %llu", + pss->sub_task_uuid, pss->sub_run, (unsigned long long)pss->sub_timestamp); // lwsl_notice("%s: collecting logs %s\n", __func__, esc); @@ -1073,7 +1104,7 @@ saiw_browser_queue_overview(struct vhd *vhd, struct pss *pss) } else { task_ac = NULL; lws_dll2_owner_clear(&task_owner); - if (lws_struct_sq3_deserialize(pdb, NULL, NULL, + if (lws_struct_sq3_deserialize(pdb, NULL, "taskname, platform", lsm_schema_sq3_map_task, &task_owner, &task_ac, 0, 999)) { lwsl_err("%s: OVERVIEW 1 failed\n", __func__);
Page fetched 0s ago, creation time: 12ms (vhost etag hits: 0%, cache hits: 0%)