Skip to content

fix: add DB stale-cache fallback to stats, prices, markets/stats, and crank#164

Open
0x-SquidSol wants to merge 2 commits intodcccrypto:mainfrom
0x-SquidSol:fix/add-db-cache-fallback-to-aggregate-endpoints
Open

fix: add DB stale-cache fallback to stats, prices, markets/stats, and crank#164
0x-SquidSol wants to merge 2 commits intodcccrypto:mainfrom
0x-SquidSol:fix/add-db-cache-fallback-to-aggregate-endpoints

Conversation

@0x-SquidSol
Copy link
Copy Markdown
Contributor

@0x-SquidSol 0x-SquidSol commented Apr 9, 2026

Summary

  • Issue: 4 critical aggregate endpoints returned hard 500 errors during Supabase outages, while /markets and /funding/global already had graceful degradation via withDbCacheFallback. These endpoints power frontend dashboards — a 500 makes the entire app appear broken.
  • Fix: Wraps all 4 with the existing withDbCacheFallback pattern, serving stale cached data (up to 1 hour, with Warning and X-Cache-Status headers) during outages, or 503 if no cache available.

Endpoints protected

Endpoint Cache Key Queries
GET /stats stats:platform All 4 sequential queries wrapped atomically
GET /prices/markets prices:markets Single query
GET /markets/stats markets:stats Single query
GET /crank/status crank:status Single query

Design decisions

  • /stats wraps all 4 queries as one unit — partial data (e.g., volume but 0 trades) would be worse than a consistently-stale snapshot
  • Per-slab endpoints NOT wrapped — high cache-key cardinality, stale per-market data is more misleading than a clean error
  • Cache keys follow domain:scope convention matching existing markets:all and funding:global

Test changes

  • 3 test files updated: error tests now expect 503 (not 500) when DB fails with no stale cache
  • Added clearDbCache() to beforeEach to prevent cross-test cache pollution

Test plan

  • tsc --noEmit passes
  • vitest run passes (186/186 tests)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved error handling for database unavailability across market data routes. Service now returns 503 (Service Unavailable) status instead of 500 when the database is temporarily inaccessible, with fallback to cached data when available.
  • Refactor

    • Consolidated database fallback logic for improved reliability and consistency across API routes.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 9, 2026

Warning

Rate limit exceeded

@0x-SquidSol has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 27 minutes and 53 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 27 minutes and 53 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0f0ff881-14cb-4c77-9fb0-308300efc69f

📥 Commits

Reviewing files that changed from the base of the PR and between 69e4c6d and 871a32d.

📒 Files selected for processing (7)
  • src/routes/crank.ts
  • src/routes/markets.ts
  • src/routes/prices.ts
  • src/routes/stats.ts
  • tests/routes/crank.test.ts
  • tests/routes/prices.test.ts
  • tests/routes/stats.test.ts
📝 Walkthrough

Walkthrough

The PR refactors four route handlers (crank, markets, prices, stats) to use a centralized withDbCacheFallback utility function for error handling and cache fallback logic, replacing inline try/catch blocks. Corresponding tests are updated to expect HTTP 503 (instead of 500) when database failures occur without stale cache.

Changes

Cohort / File(s) Summary
Database Cache Fallback Integration
src/routes/crank.ts, src/routes/markets.ts, src/routes/prices.ts, src/routes/stats.ts
Replaced direct Supabase queries wrapped in try/catch blocks with calls to withDbCacheFallback(). Error handling and fallback behavior now delegated to the shared utility; responses return directly when utility yields an HTTP Response, otherwise return JSON from callback result.
Test Updates
tests/routes/crank.test.ts, tests/routes/prices.test.ts, tests/routes/stats.test.ts
Added clearDbCache() calls in beforeEach hooks to reset cache state between tests. Updated database-error test cases to expect HTTP 503 with error message "Database temporarily unavailable" instead of 500 with route-specific messages.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • dcccrypto

Poem

🐰 Cache the dreams, let fallbacks reign,
When databases stumble, we don't complain,
Unified handlers, status codes clear,
503's whisper what we all need to hear!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding DB stale-cache fallback to four aggregate endpoints.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/routes/stats.test.ts (1)

329-340: Please cover the stale-cache hit path too.

This only verifies the cache-miss failure case. The behavior this PR actually adds is “successful read seeds cache, later DB failure returns stale data with Warning / X-Cache-Status headers”; without that case, the main fallback path can regress unnoticed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/routes/stats.test.ts` around lines 329 - 340, Add a test that exercises
the stale-cache hit path by first seeding the cache with a successful response
then simulating a DB error and asserting the stale fallback behavior: use
mockSupabase.from to return valid count/data and call
statsRoutes().request("/stats") to populate the cache (via the same code path
that seeds it), then reconfigure mockSupabase.from to return error (new
Error("Database error")) and call statsRoutes().request("/stats") again; assert
the second response is 200, the JSON matches the previously returned data, and
response headers include the stale indicators (e.g., Warning and X-Cache-Status
set to a stale value). Ensure you reuse chainable/mock setup and statsRoutes()
so the same cache mechanism is exercised.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/routes/stats.test.ts`:
- Around line 329-340: Add a test that exercises the stale-cache hit path by
first seeding the cache with a successful response then simulating a DB error
and asserting the stale fallback behavior: use mockSupabase.from to return valid
count/data and call statsRoutes().request("/stats") to populate the cache (via
the same code path that seeds it), then reconfigure mockSupabase.from to return
error (new Error("Database error")) and call statsRoutes().request("/stats")
again; assert the second response is 200, the JSON matches the previously
returned data, and response headers include the stale indicators (e.g., Warning
and X-Cache-Status set to a stale value). Ensure you reuse chainable/mock setup
and statsRoutes() so the same cache mechanism is exercised.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6c75a538-3fca-4491-bb78-82b0e4f2ee74

📥 Commits

Reviewing files that changed from the base of the PR and between fb94c29 and 69e4c6d.

📒 Files selected for processing (7)
  • src/routes/crank.ts
  • src/routes/markets.ts
  • src/routes/prices.ts
  • src/routes/stats.ts
  • tests/routes/crank.test.ts
  • tests/routes/prices.test.ts
  • tests/routes/stats.test.ts

Upstream changed /prices/:slab to ascending order + limit 1500
(for chart rendering), but the test still expected descending + 100.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@0x-SquidSol
Copy link
Copy Markdown
Contributor Author

⚠️ Merge conflict — needs rebase against current main

This PR has merge conflicts due to upstream changes in src/routes/stats.ts, src/routes/prices.ts, src/routes/markets.ts, and src/routes/crank.ts (SDK migration, explicit column selects via #115, startup validation changes).

The fix itself is still needed — no merged PR extends withDbCacheFallback to /stats, /prices/markets, /markets/stats, or /crank/status. These 4 aggregate endpoints still return hard 500s during Supabase outages instead of serving stale cached data.

To resolve: Rebase against main and resolve conflicts in the 4 route files and their test files.

… crank endpoints

Four critical aggregate endpoints returned hard 500 errors during
Supabase outages while /markets and /funding/global already had
graceful degradation via withDbCacheFallback. These endpoints power
frontend dashboards and landing pages — a 500 here makes the entire
app appear broken.

Now wraps these endpoints with the existing withDbCacheFallback
pattern, serving stale cached data (up to 1 hour, with Warning
and X-Cache-Status headers) during DB outages, or 503 if no cache
is available.

Endpoints added:
- GET /stats (cache key: stats:platform) — all 4 queries wrapped atomically
- GET /prices/markets (cache key: prices:markets)
- GET /markets/stats (cache key: markets:stats)
- GET /crank/status (cache key: crank:status)

Tests updated to expect 503 (not 500) on DB failure with no stale
cache, and to clear DB cache between tests to prevent cross-test
cache pollution.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant