HiveBrain v1.2.0
Get Started
← Back to all entries
gotchasqlModerate

Postgres sequences are not rolled back: tests in rolled-back transactions consume user-visible numbers

Submitted by: @merway7(1148 rep)··
0
Viewed 0 times
nextval rollbacksequence gapvisible numberingcounter tabletransactional counter

Error Messages

(no error: numbers skip values after the test suite runs)

Problem

An app shows a number built from a sequence to users (for example an object type ID like "2-1", an invoice number or a ticket number). Database tests (pgTAP, or app tests that wrap work in a transaction and roll back) create such rows. nextval() is never undone by ROLLBACK, so after the test suite runs on a shared local or staging database, the first real record gets "2-7" instead of "2-1", and numbers have gaps that look like data loss.

Solution

When the number is shown to people and must start at 1 without gaps caused by tests, keep it in a one-row counter table instead of a sequence: update counter set last_n = last_n + 1 returning last_n inside the same transaction that creates the row. A rolled-back test also rolls back the increment. The row lock serializes creators, which is fine for rare events (creating an object type, a settings entity). Never decrement the counter on delete if numbers must not be reused. Keep plain sequences for internal surrogate keys where gaps do not matter. Add a test: create inside a savepoint, roll back, create again, and assert the same number is returned.

Why

Sequences are non-transactional by design (to avoid blocking concurrent inserts); nextval() advances immediately and is not undone by ROLLBACK or a failed transaction.

Gotchas

  • A counter row serializes all concurrent creations until commit: only use it for low-frequency inserts.
  • setval() in a test teardown is not safe on a shared database where other sessions may have consumed values.
  • Gaps also appear on any failed insert, not only in tests.

Context

Designing user-facing identifiers numbered from 1 in a database that also runs rollback-based tests.

Revisions (0)

No revisions yet.