From 0b3d909d9da38575ba66d11d5c11e4d6127f9be2 Mon Sep 17 00:00:00 2001 From: Karl Rister Date: Mon, 10 Aug 2026 14:43:38 -0500 Subject: [PATCH] feat: surface partial-run status in get-result-summary (PERFNFV-464) Adds a GET /api/v1/run/:id/partial-status endpoint (mirrors the existing /benchmark endpoint) that reads the run.partial/ run.dropped-engines fields already added to the v9dev+ run mapping (PR#204) via the existing cdm.getRunData() helper, gracefully defaulting to partial=false/dropped-engines=[] for older CDM versions that predate these fields. get-result-summary.js now fetches this alongside tags/benchmark and prints "partial: yes (N engine(s) dropped)" when applicable, matching the wording bin/result-processor.py already uses for `crucible ls` in the crucible repo -- closing the gap flagged in crucible PR#652's review, where docs/how-benchmark-execution-works.md claimed both `crucible ls` and `crucible get result` surface this, but only the former did. Verified end-to-end: the existing partial=false path against a real indexed run, and the partial=true path against a synthetic run+ iteration doc pair inserted directly into OpenSearch and deleted afterward (a genuinely-indexed partial:true run is hard to produce -- a dropped engine tends to break later pipeline steps before indexing completes). --- queries/cdmq/get-result-summary.js | 17 +++++++++++++++++ queries/cdmq/server.js | 21 +++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/queries/cdmq/get-result-summary.js b/queries/cdmq/get-result-summary.js index d73591c5..91e69754 100644 --- a/queries/cdmq/get-result-summary.js +++ b/queries/cdmq/get-result-summary.js @@ -202,6 +202,23 @@ async function main() { thisRun['run-id'] = runId; thisRun['iterations'] = []; + // Fetch partial-run status + var partialResp; + try { + partialResp = await apiGet(baseUrl, runPrefix + '/partial-status'); + } catch (error) { + console.error('Error fetching partial-run status for run ' + runId + ': ' + error.message); + process.exit(1); + } + thisRun['partial'] = partialResp.partial; + thisRun['dropped-engines'] = partialResp['dropped-engines']; + if (partialResp.partial) { + logOutput( + ' partial: yes (' + partialResp['dropped-engines'].length + ' engine(s) dropped)', + program.outputFormat + ); + } + // Fetch tags var tagsResp; try { diff --git a/queries/cdmq/server.js b/queries/cdmq/server.js index 47cbe636..0ffce0d1 100755 --- a/queries/cdmq/server.js +++ b/queries/cdmq/server.js @@ -379,6 +379,27 @@ app.get('/api/v1/run/:id/benchmark', resolveRun, async (req, res) => { } }); +// -------------------------------------------------------------------------------------------------------------- +// GET /api/v1/run/:id/partial-status — get partial-run/dropped-engines status for a run +// -------------------------------------------------------------------------------------------------------------- +app.get('/api/v1/run/:id/partial-status', resolveRun, async (req, res) => { + try { + const { instance, yearDotMonth, runId } = req.cdm; + var runDataArr = await cdm.getRunData(instance, runId, yearDotMonth); + var runData = (runDataArr && runDataArr[0]) || {}; + var partial = (runData.run && runData.run.partial) || false; + var droppedEngines = (runData.run && runData.run['dropped-engines']) || []; + serverLog('[' + Date.now() + '] GET /api/v1/run/' + runId + '/partial-status returned partial=' + partial); + res.json({ partial: partial, 'dropped-engines': droppedEngines }); + } catch (error) { + serverError('Error in GET /api/v1/run/:id/partial-status:', error); + res.status(500).json({ + code: 'INTERNAL_ERROR', + error: 'Failed to get partial-run status: ' + error.message + }); + } +}); + // -------------------------------------------------------------------------------------------------------------- // GET /api/v1/run/:id/iterations — get iteration UUIDs for a run // --------------------------------------------------------------------------------------------------------------