Repository navigation
Conversation
…te prefetch - backend(cache): expose stale fail-open reads with headers/metrics and a bypass path (Junirezz#1553) - backend(idempotency): add a processing lease timeout with safe reclaim (Junirezz#1556) - backend(outbox): add lease expiry, fencing tokens, and a stuck-relay reaper (Junirezz#1557) - frontend(prefetch): cover heavy routes, wire nav hover/focus, and record LCP metrics (Junirezz#1569) Repairs committed merge artifacts in idempotency.ts and eventOutbox.ts and aligns the idempotency store's dry-run return contract.
|
@kingksjo Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix: cache failover staleness, idempotency leases, outbox fencing, and heavy-route prefetch
Summary
This PR implements four related reliability/performance issues in a single branch:
EventOutbox.lockedAt/lockedBywithout lease expiry risks stuck relayIt also repairs committed merge artifacts in
backend/src/idempotency.tsandbackend/src/eventOutbox.ts(duplicate class/interface declarations andundefined
prismareferences) so both modules load and compile again, andaligns
IdempotencyStore.pruneStaleKeys()with thedryRunresult contractalready asserted by
idempotencyRetention.test.ts.Changes
#1553 — Cache fail-open staleness + bypass
backend/src/redisCache.tsgetFromCacheDetailed()returning{ entry, backend, stale, fromFallback, ageMs }.getFromCache()remains backward compatible.with entry age derived from the stored TTL/expiry.
redis_cache_fallback_total{route,reason}redis_cache_stale_serve_total{route,backend}redis_cache_bypass_total{route}backend/src/middleware/cache.tsX-Cache-Stale,X-Cache-Backend,X-Cache-Age, andWarning: 110 - "Response is stale"on fallback hits.Cache-Control: no-storeso a CDN/browsercannot pin a stale vault summary indefinitely.
X-Cache-Bypass: true(orCache-Control: no-cache/no-store) skips the lookup and repopulates theentry; the response carries
X-Cache-Bypass: true.Test:
backend/src/__tests__/cacheFailover.test.tscovers stale metadata,headers, metrics, bypass detection, and cache repopulation.
#1556 — Idempotency processing lease + safe reclaim
backend/src/idempotency.tsIDEMPOTENCY_PROCESSING_LEASE_MS, default2 minutes) independent of the 24h replay-retention TTL. The TTL semantics are
documented in the module header: retention governs how long completed/failed
responses can be replayed; the lease governs how long a
pendingrecord mayblock retries.
isProcessingLeaseExpired()andprocessingLeaseRemainingSeconds()helpers.reclaimExpiredIdempotencyRecord()performs a guarded delete(
status: 'pending'+createdAt <= cutoff) so a concurrent completion isnever clobbered;
enforceIdempotency()reclaims an expired slot and allowsthe retry.
reclaimExpiredIdempotencyRecords()bulk reaper andstartIdempotencyLeaseReaper()for periodic sweeps.retryAfter.Test:
backend/src/__tests__/idempotencyPendingLease.test.tscovers expiredlease recovery, live-lease blocking, guarded-delete races, and completed replay.
#1557 — Outbox lease expiry, fencing, and stuck-relay reaper
backend/src/eventOutbox.tslockedAt/lockedByare explicitly documented and used as a lease(
OUTBOX_LOCK_TIMEOUT_MS, default 60s).buildOutboxLeaseToken, surfaced asEventOutboxRecord.leaseToken) built from the owner and claim timestamp.Relay/failure/dead-letter writes now use a conditional
updateManyon theexact
(lockedBy, lockedAt)pair, so a worker whose lease was re-claimedcannot overwrite the newer attempt.
reapStuckRelays(), which releases expired leases. It runs at the startof every relay cycle and on its own timer (
OUTBOX_REAPER_INTERVAL_MS,default half the lease).
processOutbox()claims rows with a status + expiry guard and stopped writingcompletion state through unfenced
update()calls.Test:
backend/src/__tests__/eventOutboxLease.test.tscovers lease configuration,fencing-token derivation, expired-lease reaping, crash recovery on the next
relay cycle, and mid-delivery re-claim fencing.
#1569 — Route prefetch coverage + LCP measurement
frontend/src/lib/routePrefetch.ts/vault-healthtorouteImportsandLazyVaultHealth.HEAVY_ROUTES(/compare,/vault-health,/settings) to the idleprefetch via
prefetchDashboardRoutes(); addedprefetchHeavyRoutes().getRoutePrefetchHandlers()now warms ononMouseEnter,onPointerEnter,onFocus, andonTouchStart, and failed prefetches are retryable.observeLcp()) and navigation sampling(
recordRouteNavigation()/getRoutePrefetchMetrics()) so warm vs coldnavigations can be compared in the field.
frontend/src/App.tsxnavigation's prefetch/LCP status.
LazyVaultHealthand removes duplicate route declarations.frontend/src/components/Navbar.tsx+ i18n locales/compare,/admin,/vault-health,/settings) in desktop, mobile, and dropdown menus.nav.vaultHealth/nav.settingstranslations (en/es).Test:
frontend/src/lib/routePrefetch.test.tscovers heavy-route coverage,handler exposure, and navigation metric recording.
Test/verification notes
No build was run per the task instructions. New tests were added for every
acceptance criterion and the existing suites were reviewed for API
compatibility:
getFromCache()keeps its original signature/return shape.IdempotencyStoreandidempotencyStoreremain importable fromidempotency.tsvia re-export.pruneStaleKeys()now returns thedryRunflag expected byidempotencyRetention.test.ts.EventOutboxRecordkeeps all prior fields (and additionally exposesvaultId/leaseToken);getMetrics()keeps its exact six-field shape.compatible.
Closes #1553
Closes #1556
Closes #1557
Closes #1569