Skip to content

fix(ratelimit): persist rate-limit counters across process restarts #1168 - #1301

Open
Wiseman52 wants to merge 1 commit into
rinafcode:mainfrom
Wiseman52:fix/issue-1168-persist-ratelimit-counters
Open

fix(ratelimit): persist rate-limit counters across process restarts #1168#1301
Wiseman52 wants to merge 1 commit into
rinafcode:mainfrom
Wiseman52:fix/issue-1168-persist-ratelimit-counters

Conversation

@Wiseman52

Copy link
Copy Markdown
Contributor

Overview

This PR backs the in-memory rate-limit counters with PostgreSQL so they survive process restarts and deploys. Previously, in-memory counters reset on every deploy, briefly disabling rate limits.

Related Issue

Closes #1168

Changes

📦 Database Persistence

[MODIFY] src/lib/ratelimit.ts

  • Import query from the DB pool and persist every rate-limit write asynchronously (fire-and-forget)
  • Load all non-expired rate-limit entries from the database into the in-memory cache on process startup (non-blocking)
  • Gracefully fall back to in-memory only if the database is unavailable

[ADD] src/lib/db/migrations/006_create_rate_limits_table.sql

  • rate_limits table with identifier (PK), count, reset_at, created_at, updated_at
  • Index on reset_at for efficient expired-entry cleanup

✅ Tests

[MODIFY] src/lib/ratelimit.test.ts
[MODIFY] src/app/api/tutorials/__tests__/ratelimit.test.ts

  • Mock @/lib/db/pool to prevent real database calls during tests

Verification Results

Typecheck:
✅ No new type errors introduced

Tests:
src/lib/ratelimit.test.ts — 25/25 passed
src/app/api/tutorials/__tests__/ratelimit.test.ts — 18/18 passed
src/app/api/certificates/__tests__/certificate-security.test.ts — 17/17 passed
src/lib/db/__tests__/pool.test.ts — 9/9 passed

Acceptance Criteria

Criteria Status
Implemented across the listed files (ratelimit.ts, db/pool.ts)
Unit/integration tests added or updated and passing
No regression; follows project coding standards

Design Notes

The in-memory Map remains the fast synchronous hot path so that all 30+ existing call sites (withRateLimit, certificate routes, etc.) do not need to become async. Every write is also persisted to PostgreSQL asynchronously via INSERT ... ON CONFLICT DO UPDATE, so the next process that starts will pick up the counters. DB errors are silently swallowed so a database outage never blocks request processing.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff noreply@codebuff.com

Closes rinafcode#1168

In-memory rate-limit counters reset on every deploy, briefly disabling
limits. This change backs the in-memory Map with PostgreSQL so counters
survive restarts while keeping the synchronous API for all callers.

[MODIFY] src/lib/ratelimit.ts
- Import query from db pool and persist every write asynchronously
- Load non-expired entries from DB on module startup (non-blocking)
- Gracefully fall back to in-memory only if DB is unavailable

[ADD] src/lib/db/migrations/006_create_rate_limits_table.sql
- rate_limits table with identifier (PK), count, reset_at, timestamps
- Index on reset_at for efficient expired-entry cleanup

[MODIFY] src/lib/ratelimit.test.ts
[MODIFY] src/app/api/tutorials/__tests__/ratelimit.test.ts
- Mock @/lib/db/pool to prevent real DB calls during tests

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@drips-wave

drips-wave Bot commented Aug 30, 2026

Copy link
Copy Markdown

@Wiseman52 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! 🚀

Learn more about application limits

@RUKAYAT-CODER

Copy link
Copy Markdown
Contributor

Well done on the job done so far!
Kindly fix workflow to pass

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.

Persist rate-limit counters across process restarts

2 participants