Description
Production GET responses for miner and dashboard endpoints are cached for 60 seconds by the global CustomCacheInterceptor, but webhook handlers and queue workers never invalidate those cache entries after mirror data changes. Validators and dashboards polling shortly after GitHub activity can receive stale PR/issue state, labels, review summaries, and merge timestamps even though the database has already been updated.
Only GET /api/v1/health and GET /api/v1/pulls/:owner/:repo/:number/files opt out via @NoCache().
Steps to Reproduce
- Run the service with
NODE_ENV=production and Redis configured (REDIS_HOST set — see packages/das/src/cache/cache.module.ts).
- Call
GET /api/v1/miners/{githubId}/pulls?since={iso} (or GET /api/v1/dashboard/issues?since={iso}) and note a PR's state, merged_at, and review_summary.
- Merge that PR on GitHub so the mirror processes the
pull_request.closed webhook and updates pull_requests in Postgres.
- Within 60 seconds, repeat the same GET request with identical query parameters.
- Observe the cached pre-merge payload is returned (
generated_at unchanged, state still OPEN, missing merged_at / updated review counts).
For comparison, GET /api/v1/pulls/{owner}/{repo}/{number}/files (which uses @NoCache()) reflects webhook updates immediately.
Expected Behavior
After webhook ingestion updates mirror rows, subsequent API reads should reflect the new data (or the cache should be scoped/narrow enough that scoring consumers are not served stale snapshots for a full TTL window).
Actual Behavior
Cached GET responses for miners/dashboard/repos endpoints can remain stale for up to 60 seconds after webhooks mutate the underlying tables. No invalidation hook runs in webhook handlers (packages/das/src/webhook/handlers/*) or fetch workers (packages/das/src/queue/fetch.processor.ts).
Environment
- OS: Linux
- Runtime/Node version: Node 20
- Browser (if applicable): N/A
- Deployment notes: caching is effectively disabled in non-production (
DEV_CACHE_TTL_MS = 1), so the bug manifests in production Redis-backed deployments.
Additional Context
Affected code
packages/das/src/cache/cache.module.ts — registers global CustomCacheInterceptor with CACHE_TTL_MS = 60_000 in production.
packages/das/src/cache/custom-cache.interceptor.ts — caches all GET handlers unless decorated with @NoCache().
packages/das/src/api/miners/miners.controller.ts — all endpoints are GET/POST; GET routes (/pulls, /issues) are cached.
packages/das/src/api/dashboard/dashboard.controller.ts — GET /issues is cached.
packages/das/src/api/repos/repos.controller.ts — maintainer listing GET is cached.
Why this is distinct from open items
Suggested fix directions
- Add
@NoCache() to scoring-facing read endpoints (miners, dashboard, repos), matching /pulls/.../files.
- Or invalidate/delete cache keys when webhook handlers update entities (e.g. by repo + resource type).
- Or shorten TTL / use cache keys that incorporate
repos.last_event_at.
Impact
Scoring cycles and dashboards that poll the mirror immediately after GitHub events can act on outdated miner payloads — missing merges, stale review penalties, or old label attribution — for up to one minute per cached URL.
Description
Production GET responses for miner and dashboard endpoints are cached for 60 seconds by the global
CustomCacheInterceptor, but webhook handlers and queue workers never invalidate those cache entries after mirror data changes. Validators and dashboards polling shortly after GitHub activity can receive stale PR/issue state, labels, review summaries, and merge timestamps even though the database has already been updated.Only
GET /api/v1/healthandGET /api/v1/pulls/:owner/:repo/:number/filesopt out via@NoCache().Steps to Reproduce
NODE_ENV=productionand Redis configured (REDIS_HOSTset — seepackages/das/src/cache/cache.module.ts).GET /api/v1/miners/{githubId}/pulls?since={iso}(orGET /api/v1/dashboard/issues?since={iso}) and note a PR'sstate,merged_at, andreview_summary.pull_request.closedwebhook and updatespull_requestsin Postgres.generated_atunchanged,statestillOPEN, missingmerged_at/ updated review counts).For comparison,
GET /api/v1/pulls/{owner}/{repo}/{number}/files(which uses@NoCache()) reflects webhook updates immediately.Expected Behavior
After webhook ingestion updates mirror rows, subsequent API reads should reflect the new data (or the cache should be scoped/narrow enough that scoring consumers are not served stale snapshots for a full TTL window).
Actual Behavior
Cached GET responses for miners/dashboard/repos endpoints can remain stale for up to 60 seconds after webhooks mutate the underlying tables. No invalidation hook runs in webhook handlers (
packages/das/src/webhook/handlers/*) or fetch workers (packages/das/src/queue/fetch.processor.ts).Environment
DEV_CACHE_TTL_MS = 1), so the bug manifests in production Redis-backed deployments.Additional Context
Affected code
packages/das/src/cache/cache.module.ts— registers globalCustomCacheInterceptorwithCACHE_TTL_MS = 60_000in production.packages/das/src/cache/custom-cache.interceptor.ts— caches all GET handlers unless decorated with@NoCache().packages/das/src/api/miners/miners.controller.ts— all endpoints are GET/POST; GET routes (/pulls,/issues) are cached.packages/das/src/api/dashboard/dashboard.controller.ts—GET /issuesis cached.packages/das/src/api/repos/repos.controller.ts— maintainer listing GET is cached.Why this is distinct from open items
created_at; this report is about stale cached payloads after live updates, not sort semantics.Suggested fix directions
@NoCache()to scoring-facing read endpoints (miners, dashboard, repos), matching/pulls/.../files.repos.last_event_at.Impact
Scoring cycles and dashboards that poll the mirror immediately after GitHub events can act on outdated miner payloads — missing merges, stale review penalties, or old label attribution — for up to one minute per cached URL.