Skip to content

[bug] miners/dashboard/repos GET responses cached for 60s with no invalidation after webhooks #201

Description

@andriypolanski

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

  1. Run the service with NODE_ENV=production and Redis configured (REDIS_HOST set — see packages/das/src/cache/cache.module.ts).
  2. 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.
  3. Merge that PR on GitHub so the mirror processes the pull_request.closed webhook and updates pull_requests in Postgres.
  4. Within 60 seconds, repeat the same GET request with identical query parameters.
  5. 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.tsGET /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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions