Skip to content

feat: advisory cache default (CM-1328)#4391

Open
ulemons wants to merge 1 commit into
fix/add-bulk-to-blast-radiusfrom
feat/advisory-cache-default
Open

feat: advisory cache default (CM-1328)#4391
ulemons wants to merge 1 commit into
fix/add-bulk-to-blast-radiusfrom
feat/advisory-cache-default

Conversation

@ulemons

@ulemons ulemons commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds bulk/batch endpoints for blast-radius analysis (submitBlastRadiusJobBatch, getBlastRadiusJobBatch) alongside the existing single-job endpoints, and adds an advisory-level cache so a submit can reuse a recent 'done' analysis for the same (advisoryId, package, ecosystem) instead of always starting a new Temporal workflow. Submitting many advisories at once (e.g. a batch of 20) no longer means 20 unconditional workflow starts — repeated/overlapping submissions within a configurable cache window (BLAST_RADIUS_CACHE_MAX_AGE_DAYS, default 1 day) short-circuit to the cached result.

Changes

  • submitBlastRadiusJobBatch / getBlastRadiusJobBatch: new bulk endpoints mirroring the single-job ones. Batch size is capped lower than the other read-only batch endpoints (packages/advisories/contacts) and stays behind the strict blastRadiusRateLimiter, since each job can trigger a real Temporal workflow start, not just a read.
  • Partial-failure isolation: a single job's workflow.start (or cache-lookup) failure inside submitBlastRadiusJobBatch returns that entry as status: 'failed' without failing the rest of the batch — the cache lookup was moved inside the per-job try/catch (it was originally outside, which would have rejected the whole Promise.all and 500'd the entire batch on a transient DB error during the cache check).
  • getRecentDoneAnalysis (new DAL function in services/libs/data-access-layer/src/packages/blastRadius.ts): finds the most recent 'done' analysis for the same (advisoryOsvId, packageName, ecosystem) within the cache window. Uses IS NOT DISTINCT FROM for packageName since it's nullable (advisory-wide analyses have no package) and plain = never matches NULL.
  • getCachedJobEntry (new shared helper in blastRadius.ts): extracted so the cache-check-and-build-entry logic isn't duplicated between submitBlastRadiusJob and submitBlastRadiusJobBatch's per-job handler — both now call the same function instead of maintaining two copies of the same lookup.
  • force: true on a request bypasses the cache entirely, same as before for the single-job endpoint.
  • Deliberately not migrating blastRadius.ts's row types to @crowd/types (per ADR-0006) or adding DAL integration tests/factories (per ADR-0007) in this PR — scoped decision to keep this change focused on the batch API + cache behavior.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Performance improvement
  • Chore / dependency update
  • Documentation

JIRA ticket

CM-1328

Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
@ulemons ulemons self-assigned this Jul 23, 2026
@ulemons ulemons added the Feature Created by Linear-GitHub Sync label Jul 23, 2026
Copilot AI review requested due to automatic review settings July 23, 2026 14:54
@cursor

cursor Bot commented Jul 23, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes drive Temporal workflow volume and LLM reachability cost via batch submit (up to 20 workflows per call) and cache behavior; partial-failure handling is new but well-tested.

Overview
Adds bulk blast-radius submit and poll on the Akrites external API, and short-circuits single/batch submit when a recent completed analysis already exists for the same advisory, package, and ecosystem.

Advisory cache: Before creating a row or starting analyzeBlastRadius, submit paths call getCachedJobEntrygetRecentDoneAnalysis (default window 1 day, AKRITES_BLAST_RADIUS_CACHE_MAX_AGE_DAYS). Cache hits return 202 with status: 'done' and the existing analysisId; force: true skips the lookup entirely.

New routes: POST .../blast-radius/jobs:batch (up to 20 jobs per request, same strict blastRadiusRateLimiter as single submit) and POST .../blast-radius/jobs:batch/poll (up to 100 UUIDs, paginated, standard read rate limit). OpenAPI documents batch schemas and updates force/cache semantics (replacing the old 7-day cache note).

Batch submit behavior: Each job is processed independently; workflow.start or cache/DB errors on one entry yield status: 'failed' for that entry only. Default blast-radius submit rate limit default rises from 5 to 50 requests/hour.

DAL: Bulk read helpers for analyses, verdicts, and excluded-dependent counts support efficient batch polling.

Reviewed by Cursor Bugbot for commit bfd8a2b. Bugbot is set up for automated code reviews on this repo. Configure here.

@ulemons ulemons changed the title Feat/advisory cache default feat: advisory cache default (CM-1328) Jul 23, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Conventional Commits FTW!

@ulemons
ulemons changed the base branch from main to fix/add-bulk-to-blast-radius July 23, 2026 14:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds batch blast-radius submission/polling and reuses recent completed analyses to reduce duplicate Temporal workflows.

Changes:

  • Adds batch submit and polling APIs with validation and partial-failure handling.
  • Adds advisory-level cache lookup with force bypass.
  • Updates routing, rate limits, tests, and OpenAPI documentation.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
services/libs/data-access-layer/src/packages/blastRadius.ts Adds cache and batch queries.
backend/src/api/public/v1/packages/submitBlastRadiusJobBatch.ts Implements batch submission.
backend/src/api/public/v1/packages/submitBlastRadiusJobBatch.test.ts Tests batch submission behavior.
backend/src/api/public/v1/packages/submitBlastRadiusJob.ts Adds cache reuse to single submissions.
backend/src/api/public/v1/packages/submitBlastRadiusJob.test.ts Tests single-job caching.
backend/src/api/public/v1/packages/getBlastRadiusJobBatch.ts Implements batch polling.
backend/src/api/public/v1/packages/getBlastRadiusJobBatch.test.ts Tests batch polling.
backend/src/api/public/v1/packages/blastRadiusBatch.ts Defines batch schemas and pagination.
backend/src/api/public/v1/packages/blastRadiusBatch.test.ts Tests schemas and pagination.
backend/src/api/public/v1/packages/blastRadius.ts Defines cache configuration and helper.
backend/src/api/public/v1/akrites-external/openapi.yaml Documents caching and batch APIs.
backend/src/api/public/v1/akrites-external/index.ts Registers routes and adjusts rate limiting.
Comments suppressed due to low confidence (2)

backend/src/api/public/v1/akrites-external/openapi.yaml:460

  • Cache-hit entries do not start workflows, so this cost explanation is inaccurate. Qualify the statement as applying to cache misses.
            Capped much lower than the 100-item read batches — each entry
            starts its own Temporal workflow, so the batch multiplies
            workflow starts (and reachability-analysis cost) per request.

backend/src/api/public/v1/akrites-external/openapi.yaml:1137

  • The batch shares the single-submit cache semantics, so only cache misses start workflows. The current absolute wording contradicts the implementation.
        single package. Each entry starts its own Temporal workflow, so the
        batch is capped at 20 jobs (10 recommended as the default batch
        size), much lower than the 100-item read batches, and stays behind
        the same strict rate limiter as the single-job route.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

candidates_considered, started_at, completed_at
FROM blast_radius_analyses
WHERE advisory_osv_id = $(advisoryOsvId)
AND package_name IS NOT DISTINCT FROM $(packageName)
Comment on lines +196 to +200
WHERE advisory_osv_id = $(advisoryOsvId)
AND package_name IS NOT DISTINCT FROM $(packageName)
AND ecosystem = $(ecosystem)
AND status = 'done'
AND completed_at >= NOW() - make_interval(days => $(maxAgeDays))
Comment on lines 59 to +63
status and, once done, results. Bulk submit (jobs:batch) is capped at
20 jobs per request (10 recommended as the default batch size) — each
entry starts its own workflow — and stays behind the same strict rate
limiter as the single-job route; bulk poll
(jobs:batch/poll) is read-only and capped at 100 like the other batch
endpoints. Same interim scope note as Advisories applies (read:packages,
pending a dedicated read:advisories scope).
limiter as the single-job route, defaulting to 50 requests/hour
(configurable via AKRITES_BLAST_RADIUS_RATE_LIMIT_MAX / _WINDOW_MS);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature Created by Linear-GitHub Sync

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants