Skip to content

feat(x2a): implement cascading invalidation - #4488

Open
yray-pixel wants to merge 2 commits into
redhat-developer:mainfrom
yray-pixel:CascadingInvaladation
Open

feat(x2a): implement cascading invalidation#4488
yray-pixel wants to merge 2 commits into
redhat-developer:mainfrom
yray-pixel:CascadingInvaladation

Conversation

@yray-pixel

Copy link
Copy Markdown
Contributor

When a phase completes successfully, any finished jobs for downstream phases are automatically marked as stale.

@rhdh-gh-app

rhdh-gh-app Bot commented Aug 30, 2026

Copy link
Copy Markdown

Changed Packages

Package Name Package Path Changeset Bump Current Version
@red-hat-developer-hub/backstage-plugin-x2a-backend workspaces/x2a/plugins/x2a-backend patch v2.0.0
@red-hat-developer-hub/backstage-plugin-x2a-common workspaces/x2a/plugins/x2a-common patch v2.0.0
@red-hat-developer-hub/backstage-plugin-x2a-node workspaces/x2a/plugins/x2a-node patch v1.0.0
@red-hat-developer-hub/backstage-plugin-x2a workspaces/x2a/plugins/x2a patch v2.0.0

@codecov

codecov Bot commented Aug 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.69767% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.63%. Comparing base (db7bee2) to head (806b605).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #4488   +/-   ##
=======================================
  Coverage   61.62%   61.63%           
=======================================
  Files        2569     2569           
  Lines      102831   102864   +33     
  Branches    28778    28809   +31     
=======================================
+ Hits        63368    63397   +29     
- Misses      37646    37649    +3     
- Partials     1817     1818    +1     
Flag Coverage Δ *Carryforward flag
adoption-insights 84.55% <ø> (ø) Carriedforward from 9347635
ai-integrations 69.09% <ø> (ø) Carriedforward from 9347635
app-defaults 55.55% <ø> (ø) Carriedforward from 9347635
augment 46.67% <ø> (ø) Carriedforward from 9347635
boost 79.84% <ø> (ø) Carriedforward from 9347635
bulk-import 72.79% <ø> (ø) Carriedforward from 9347635
cost-management 13.55% <ø> (ø) Carriedforward from 9347635
dcm 72.09% <ø> (ø) Carriedforward from 9347635
e2e-adoption-insights 60.00% <ø> (ø) Carriedforward from 9347635
e2e-extensions 62.13% <ø> (ø) Carriedforward from 9347635
e2e-global-header 49.45% <ø> (ø) Carriedforward from 9347635
e2e-homepage 61.11% <ø> (ø) Carriedforward from 9347635
e2e-intelligent-assistant 46.65% <ø> (ø) Carriedforward from 9347635
e2e-orchestrator 49.52% <ø> (ø) Carriedforward from 9347635
e2e-orchestrator-plugin 49.51% <ø> (ø) Carriedforward from 9347635
e2e-quickstart 55.21% <ø> (ø) Carriedforward from 9347635
e2e-scorecard 50.21% <ø> (ø) Carriedforward from 9347635
e2e-theme 16.36% <ø> (ø) Carriedforward from 9347635
extensions 56.59% <ø> (ø) Carriedforward from 9347635
global-floating-action-button 71.18% <ø> (ø) Carriedforward from 9347635
global-header 66.50% <ø> (ø) Carriedforward from 9347635
homepage 48.48% <ø> (ø) Carriedforward from 9347635
install-dynamic-plugins 58.57% <ø> (ø) Carriedforward from 9347635
intelligent-assistant 75.35% <ø> (ø) Carriedforward from 9347635
konflux 91.98% <ø> (ø) Carriedforward from 9347635
lightspeed 69.02% <ø> (ø) Carriedforward from 9347635
mcp-integrations 84.14% <ø> (ø) Carriedforward from 9347635
orchestrator 70.87% <ø> (ø) Carriedforward from 9347635
quickstart 63.74% <ø> (ø) Carriedforward from 9347635
sandbox 79.56% <ø> (ø) Carriedforward from 9347635
scorecard 87.41% <ø> (ø) Carriedforward from 9347635
theme 88.91% <ø> (ø) Carriedforward from 9347635
translations 5.12% <ø> (ø) Carriedforward from 9347635
x2a 77.17% <90.69%> (+0.06%) ⬆️

*This pull request uses carry forward flags. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update db7bee2...806b605. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sonarqubecloud

Copy link
Copy Markdown

@yray-pixel
yray-pixel marked this pull request as ready for review August 30, 2026 17:32
@yray-pixel
yray-pixel requested review from a team, eloycoto and mareklibra as code owners August 30, 2026 17:32
@eloycoto

eloycoto commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

@mareklibra can you check this PR about the migration, LGTM, but I would like to make sure? and also the MCP server, I do not think that we need anything here, but it's good to check.

table.string('status').notNullable().defaultTo('pending').checkIn(statuses);
table.string('phase').notNullable().defaultTo('init').checkIn(PHASES);
table
.uuid('project_id')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SQLite recreate copies jobs with INSERT … SELECT *, but createJobsTable column order no longer matches the live table (project_id/module_id were moved before error_details in 806b605). That maps error_details → project_id (NOT NULL). Success rows with NULL error_details will fail the migration; other rows will be silently corrupted. Restore the adversarial-migration column order, or use a named-column INSERT. Please add a test that inserts a job, runs this up(), and asserts project_id is unchanged.

});

const lastJob = jobs[0];
if (lastJob && JobStatus.from(lastJob.status).isSuccess()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After this invalidation, getNextPhase / getLastPhaseReached (not updated in this PR) still treat a stale migrate job as “the last phase reached”. For a module waiting on publish, re-running analyze then makes bulk “run next” call publish using stale migrate artifacts. Skip stale the same way cancelled is skipped so the next phase is the first stale/unfinished one. Please add getNextPhase tests for migrate stale → migrate, and migrate+publish stale → migrate.

}

return { status: 'pending', errorDetails: undefined };
const status: ModuleStatus = JobStatus.from(latestPhaseJob.status).isStale()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mapping stale → success hides the new state at module/project level: ModuleStatusCell still shows the Published chip whenever module.publish exists, and calculateProjectStatus counts the module in neither finished nor waiting. module.statuses.stale translations added in this PR are unused. Please either add stale to ModuleStatus and return it, or compute aggregate status from the earliest stale downstream phase. moduleStatus.test.ts needs cases for this.

x2aDatabase,
logger,
);
} catch (err) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalidation runs after updateJob has already committed success and errors are only logged. If markJobsAsStale fails, downstream jobs stay success and the UI never shows stale. Please run both updates in one transaction or fail the collectArtifacts callback if invalidation fails so the operator can retry.

}

return { status: 'pending', errorDetails: undefined };
const status: ModuleStatus = JobStatus.from(latestPhaseJob.status).isStale()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moduleStatus.ts now maps stale → success with no new tests, and getNextPhase / ModuleStatusCell / projectStatus / PhasesCard adversarial canRun were not updated for the new status. Please add moduleStatus and getNextPhase cases, plus a collectArtifacts test that an errored downstream job is not marked stale (the 806b605 isSuccess filter).

'@red-hat-developer-hub/backstage-plugin-x2a': patch
---

invaldating stale jobs - jobs that had a former phase ran again

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use a changelog-ready sentence, e.g. “Mark downstream phase jobs as stale when an upstream phase is re-run successfully.”

`ALTER TABLE jobs DROP CONSTRAINT IF EXISTS jobs_status_check`,
);
await knex.raw(
`ALTER TABLE jobs ADD CONSTRAINT jobs_status_check CHECK (status IN ('pending', 'running', 'success', 'error', 'cancelled'))`,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

down() re-applies a CHECK without 'stale'. On Postgres this fails if any row is already stale; on SQLite INSERT into the restored table will fail the same way. Please convert stale rows first, or document that down is unsupported after this status has been used.


const previousRunSucceeded =
!!phase?.status && JobStatus.from(phase.status).isSuccess();
const isStale = !!phase?.status && JobStatus.from(phase.status).isStale();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

previousRunSucceeded is isSuccess() only, so a stale phase gets the first-run button label (“Run migrate”) plus staleInstructions. Consider treating stale as a previous run (isSuccess() || isStale()) so the CTA stays “Re-run …”.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants