BlogPricing
PostgreSQL

Rate limiting database schema

A rate limiting schema with named quotas, per-key buckets that track the current window, and an audit log for billable usage. Pairs with a Redis fast path; this is the durable store.

Start for free
Database schema previewRate limiting database schema
Recreate this in Raltey

Copy this prompt

Paste this into Raltey's AI agent to generate the same diagram, then tweak it on the canvas.

Prompt
Design a PostgreSQL database schema for: Rate limiting database schema.

A rate limiting schema with named quotas, per-key buckets that track the current window, and an audit log for billable usage. Pairs with a Redis fast path; this is the durable store.

Include these tables: quota, rate_bucket, rate_audit.

Use proper primary keys, foreign keys, and indexes. Export SQL DDL for PostgreSQL.

Context

  • - Real-time decisions live in Redis; this DB is the durable record.
  • - Buckets are upserted at the end of each window.
  • - Audit log captures denied requests for support and billing.

Patterns

  • - Sliding-window counters keyed by (key, quota, window_started).
  • - Redis is the hot path; Postgres is the recoverable source of truth.
  • - Audit log keeps only denied or notable events to bound size.
Tables

The schema, table by table

quota

DBML - quota
Table quota {
  id          uuid pk
  name        text unique not null   // api:reads, ai:generations
  window_sec  int not null
  max_count   int not null
}

rate_bucket

Per-key counter for the current window.

DBML - rate_bucket
Table rate_bucket {
  key            text not null   // user:123, ip:1.2.3.4
  quota_id       uuid [ref: > quota.id]
  window_started timestamptz not null
  count          int not null default 0
  pk (key, quota_id, window_started)
}

rate_audit

Append-only log of denied or notable requests.

DBML - rate_audit
Table rate_audit {
  id         uuid pk
  key        text not null
  quota_id   uuid [ref: > quota.id]
  occurred_at timestamptz default `now()`
  denied     boolean not null
  metadata   jsonb
}

Relationships

FromToType
quotarate_bucket1:N
quotarate_audit1:N

Notes from the field

Frequently asked

Why store rate-limit state in Postgres at all?

For billing, audit, and recovery. Redis is volatile; this table survives a Redis outage and produces the monthly usage report.

How do I support sliding windows?

Keep multiple rate_bucket rows per key (overlapping windows). At decision time, sum the relevant windows. For most apps, fixed windows are enough.

More templates

Open this template in Raltey

Edit it on the canvas, ask the AI agent to extend it, then share or export.

Start for free