Skip to content

perf(rpc): fix POST /links/create 3–6s latency during Redis instability - #677

Draft
izadoesdev wants to merge 4 commits into
mainfrom
cursor/links-create-perf-redis-backfill-a605
Draft

perf(rpc): fix POST /links/create 3–6s latency during Redis instability#677
izadoesdev wants to merge 4 commits into
mainfrom
cursor/links-create-perf-redis-backfill-a605

Conversation

@izadoesdev

@izadoesdev izadoesdev commented Aug 26, 2026

Copy link
Copy Markdown
Member

Description

Fixes POST /links/create taking 3–6.5 seconds in production during Redis instability periods. Root cause analysis showed two compounding code-level bottlenecks that amplify Redis timeout penalties:

Bottleneck 1 — Redundant Redis backfill after known failure (~1.5s saved)

When beginCachedLinkMutation fails due to Redis being unavailable, the handler falls back to a PG-only create (correct), but then calls backfillLinkCache which hits the same dead Redis and blocks for another 1.5s (LINK_CACHE_OPERATION_DEADLINE_MS) before failing. This call always fails when Redis is down — it's pure waste.

Bottleneck 2 — Awaited audit INSERT blocks the response (~50–100ms saved)

runAuditedMutation awaits writeMutationAudit after every successful tracked mutation. Since appendAuditEvent already has an outbox fallback for durability, blocking the HTTP response on this INSERT is unnecessary latency.

Full request timeline during Redis instability (before fix):

Step Component Cost
1 resolveApiKey / getMemberRole via cacheable ~2s (shared Redis 2s timeout → DB fallback)
2 beginCachedLinkMutation via link cache Redis ~1.5s (link cache Redis timeout)
3 PG INSERT (the actual work) ~50ms
4 backfillLinkCache via link cache Redis ~1.5s wasted (same dead Redis)
5 writeMutationAudit PG INSERT ~50–100ms blocked
Total ~5.1–5.3s

After fix, steps 4 and 5 are eliminated from the response path: step 4 is skipped entirely when Redis is known down, step 5 is fire-and-forget.

Remaining latency from Redis instability (~3.5s) is infrastructure, not code:

  • The shared Redis cacheable 2s timeout on resolveApiKey/getMemberRole is an infra-level Redis health issue
  • The link cache Redis 1.5s deadline on beginCachedLinkMutation is the designed failover window
  • Both are appropriate — they keep the service available while Redis recovers

Changes:

  • Track cacheUnavailable flag in the link create handler when begin-mutation errors
  • Skip backfillLinkCache when Redis is known to be down (success path + reconciliation path)
  • Make backfillLinkCache fire-and-forget (unawaited) in the non-error cache-bypass path
  • Make writeMutationAudit fire-and-forget in runAuditedMutation (audit has outbox fallback)

Slice

  • Issue: Production POST /links/create latency investigation (2026-08-26)
  • Scope / owning surface: rpc (links router + audit middleware)
  • Dependencies or overlapping PRs: None
Checklist
  • This branch started from current staging and does not include another unmerged PR unless it is named above.
  • This is one independently reviewable slice; unrelated cleanup or refactors are in separate PRs.
  • I checked open PRs for overlapping files, contracts, schemas, or deployment configuration and made any dependency explicit above.
  • This PR targets staging; after it closes, this branch will not be reused for another change.
  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
Open in Web Open in Cursor 

Summary by cubic

Fixes POST /links/create taking 3–6.5s during Redis instability by cutting redundant Redis waits and adding fail-fast behavior so one timeout no longer compounds into sequential 1.5–2s penalties per request.

Changes

  • Link cache commands now fail fast for 5s after a recent Redis failure instead of paying the per-call deadline.
  • Deletes the backfillLinkCache path entirely, removing ~1.5s of dead-Redis waiting per create in cache-bypass and reconciliation flows.
  • Unblocks writeMutationAudit (fire-and-forget) since appendAuditEvent has an outbox fallback; healthy-Redis behavior is unchanged.

Written for commit 9bfee91. Summary will update on new commits.

Review in cubic

cursoragent and others added 2 commits August 26, 2026 16:36
…link create

When beginCachedLinkMutation fails due to Redis being unavailable, the
subsequent backfillLinkCache call hits the same dead Redis and blocks for
another 1.5s (the LINK_CACHE_OPERATION_DEADLINE_MS timeout) before
failing. This adds 1.5s of wasted latency to every link create request
during Redis instability.

Changes:
- Track cacheUnavailable flag when begin-mutation errors on Redis
- Skip backfillLinkCache entirely when Redis is known to be down (both
  in the success path and in the reconciliation path)
- Make backfillLinkCache fire-and-forget (unawaited) in the cache-bypass
  path since the PG row is already committed and backfill errors are
  already caught internally
- Make writeMutationAudit fire-and-forget in runAuditedMutation since
  appendAuditEvent already has outbox fallback for durability

Net effect: eliminates ~1.5s of dead Redis waiting per create request
during cache outages, plus removes the sequential audit INSERT from the
response path for all tracked mutations.

Co-authored-by: iza <izadoesdev@users.noreply.github.com>
Co-authored-by: iza <izadoesdev@users.noreply.github.com>
@vercel

vercel Bot commented Aug 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dashboard Ready Ready Preview Aug 26, 2026 5:13pm
databuddy-status Ready Ready Preview Aug 26, 2026 5:13pm
documentation Ready Ready Preview Aug 26, 2026 5:13pm

@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d0e08001-230b-4d00-af22-dcd40bc7a599

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@unkey-deploy

unkey-deploy Bot commented Aug 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Unkey Deploy

Name Status Preview Inspect Updated (UTC)
links (preview) Ready Visit Preview Inspect Aug 26, 2026 5:12pm

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.

2 participants