BLAST submit idempotency key from the SPA¶
Motivation¶
The backend BLAST submit route (POST /api/blast/jobs) already supports
idempotent submits: when the request carries an idempotency_key, it derives a
deterministic job_id from (tenant, caller, idempotency_key) (uuid5) and
returns the existing job on replay instead of creating a second one. Without a
key it falls back to a fresh uuid4 per request, so every submit is a new job.
The SPA's New Search page never sent an idempotency_key, so that dedup path was
effectively dead for dashboard submits. The only protection against a duplicate
BLAST run was the submit button disabling itself while the mutation was in flight
(submitPending). That guards a single double-click in one tab, but not:
- a transport-level replay (browser / proxy re-sending the same POST), or
- a submit fired from two tabs.
Either could create two real BLAST jobs on the cluster.
Change¶
useSubmitMutation now attaches a stable idempotency_key (a crypto.randomUUID(),
with a timestamp+random fallback for non-secure-context browsers) to each submit
request when the caller did not already provide one. The key is generated inside
mutationFn; mutations use the default retry: 0, so the key is stable for the
lifetime of one submit attempt and a backend-side replay of the same request body
dedupes to the same job_id.
web/src/api/blast.types.ts:BlastSubmitRequest.idempotency_key?: string.web/src/pages/blastSubmit/useSubmitMutation.ts: generate + attach the key.
No backend change — this activates the existing server-side dedup.
Validation¶
cd web && npx vitest run src/pages/blastSubmit— 189 passed.cd web && npm run build— clean.npx eslint— clean on both files.- Backend dedup behaviour is already covered by
api/tests/test_blast_submit_route_options.py.
User-facing effect¶
Double-submitting the same New Search (rapid resubmit, flaky network retry, or a second tab) now returns the original job instead of starting a duplicate cluster run — saving compute and avoiding confusing duplicate rows on Recent searches.