Report #69661
[bug\_fix] PostgreSQL sequence contention \(nextval LWLock\)
Increase the sequence cache size using 'ALTER SEQUENCE seq\_name CACHE 1000;' to reduce the frequency of LWLock acquisitions, or migrate to UUIDv7 keys generated client-side to eliminate the centralized sequence bottleneck entirely.
Journey Context:
A developer builds a high-throughput telemetry ingestion service using PostgreSQL 15. The table uses 'id SERIAL PRIMARY KEY' and ingests batches of 1000 events per transaction across 100 concurrent Go goroutines. Initially, performance peaks at 50,000 inserts per second. When scaling to 500 goroutines expecting 250,000 inserts/sec, throughput plateaus at 80,000 inserts/sec and latency spikes from 5ms to 500ms. The developer analyzes pg\_stat\_activity and sees all 500 connections in 'active' state, but wait\_event\_type is 'LWLock' and wait\_event is 'sequence'. Checking pg\_stat\_statements, the INSERT execution time increased by 100x. Initially, the developer suspects disk I/O or checkpointing, but iostat shows 90% idle. They discover that SERIAL creates a sequence and nextval\(\) requires an LWLock to refill the in-memory cache when exhausted. The default CACHE is 1, meaning every insert from every backend contends for the sequence lock. The developer runs 'ALTER SEQUENCE events\_id\_seq CACHE 10000;', reserving 10,000 IDs per backend per allocation. Immediately, the LWLock contention vanishes. Throughput jumps to 300,000 inserts/sec because backends now reserve IDs in bulk, reducing lock contention by 99.9%. The fix works because it amortizes the LWLock cost across thousands of inserts rather than every single one.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-20T23:24:42.028212+00:00— report_created — created