Expected Behavior: Sequence Gaps
It is normal and expected to see gaps in transaction ID sequences. For example, you might observe transaction IDs jumping from 17498 directly to 17531, skipping several numbers in between. This is not a bug - it’s an intentional design choice in PostgreSQL for performance reasons.Why Do Gaps Occur?
Transaction ID sequences can have gaps for several reasons:1. Rolled Back Transactions
When a SQL transaction is rolled back, any sequence values that were consumed during that transaction are not returned to the sequence. This is because PostgreSQL sequences operate outside of the transactional context.Why Sequences Are Non-Transactional
Sequences are designed to be non-transactional to avoid concurrent locking contention. Rolling back a
nextval() operation would require coordination between all transactions using that sequence, which would severely impact performance.2. Failed Database Connections
If a database connection fails during a transaction, the sequence values consumed up to that point are lost and will create gaps in the sequence.3. Dry-Run Operations
When you use thedry-run parameter to preview transactions without committing them, the operation still consumes sequence values from the transaction ID sequence. Since these transactions are never committed to the ledger, the consumed IDs become gaps.
When using the dry-run feature extensively in production environments, be aware that it will contribute to gaps in your transaction ID sequence. This is normal behavior and does not affect the integrity of your ledger.
4. Database Crashes and Recovery
PostgreSQL sequences pre-allocate values in batches for performance. During crash recovery, any pre-allocated but unused values are lost, creating gaps in the sequence.Impact on Your Application
These gaps have no negative impact on ledger functionality:- ✅ All committed transactions are recorded correctly
- ✅ Transaction ordering is preserved
- ✅ Data integrity is maintained
- ✅ Audit trails remain complete
When to Be Concerned
You should only investigate further if you notice:- Extremely large gaps (thousands of missing IDs in a short time period) - this might indicate connection issues or excessive dry-run usage
- Decreasing transaction IDs - this would indicate a serious problem and should never happen
- Duplicate transaction IDs - this would indicate a critical database issue
Best Practices
Don't Rely on Consecutive IDs
Never build application logic that depends on transaction IDs being consecutive. Use transaction IDs only for:
- Uniquely identifying transactions
- Ordering transactions chronologically
- Creating pagination cursors
Use Timestamps for Business Logic
If your business logic requires tracking continuous sequences (like invoice numbers), implement a separate gapless sequence using application-level logic rather than relying on database sequence IDs.
Related Topics
- Concurrency Model - Learn about transaction atomicity and locking
- Transactions - Understand the transaction model in Formance Ledger