PostgreSQL Sequences: A Simple Way to Spot Unexpected Gaps

Sequence gaps in PostgreSQL are often attributed to transaction rollbacks or concurrent access, but a lesser-known cause can be reproduced with surprising ease. Consider a basic table and sequence setup:

  • check_seq with an integer ID and two attribute columns
  • check_seq_seq configured to increment by 1, starting at 1, with no cycle

The quirk emerges when using jsonb_populate_record to insert rows. By building a JSON object that calls nextval() for the ID field, then populating the record from that JSON, the sequence advances in a way that can leave visible holes in the ID assignment.

Executing two identical inserts with this method yields a result that shows the gaps plainly, as evidenced by the linked output image. The pattern demonstrates that the JSON population path interacts with sequence calls differently than a direct nextval() insert would.

The takeaway: if you rely on jsonb_populate_record for inserts, verify your sequence behavior — gaps may appear where you least expect them, without any rollback or concurrent transaction involved.