Skip to content

pgwire: no way to pin a read snapshot across pages — multi-page readers can observe half-applied producer batches; add pg_snapshot_xmin / SHOW SNAPSHOT #370

Closed

Description

@EnRaiha

Version / build tested against

origin/main @ 1ff3551

Deployment mode

Origin, single node (local)

Engine(s) involved

pgwire

Summary

A reader that pages through a table with a keyset cursor takes a fresh snapshot per
page. When a producer writes in batches, page N can land between two of its
transactions and return a set that is internally inconsistent. A loader cannot tell the
difference between "the producer is still writing" and "that is all the data".

Consistent reads across pages is a normal database capability, and the cost here is
small: expose the current snapshot's xmin so a reader can hold a bound.

Steps to reproduce

No dedicated repro needed; it is a missing capability. Current state: there is no
pg_current_snapshot() or pg_snapshot_xmin(), and SHOW STATS is not a snapshot
handle. A keyset page therefore has no way to pin itself. The builtin function registry
is nodedb-sql/src/functions/builtins/scalars.rs, assembled by
builtin_functions in nodedb-sql/src/functions/builtins/all.rs:8, so the new function
has a clear home.

Expected behavior

One of these, whichever fits the function registry best:

SELECT pg_snapshot_xmin(pg_current_snapshot());
-- or
SHOW SNAPSHOT;

A reader captures the value once, then filters later pages with xmin <= that value
(or the equivalent visibility rule), so every page sees the same cut.

What actually happened? (check all that are true)

  • Acknowledged/committed data was lost, corrupted, or silently wrong
  • The server crashed, hung, or failed to start
  • A security or isolation boundary was crossed
  • Core functionality is broken with no acceptable workaround
  • A workaround exists (a producer-side commit table, which pushes work onto writers)

Proposed severity

SEV-3, capability gap. The workaround costs every producer a run table and a discipline.

Reproducibility

Always, under any concurrent producer.

Last known-good version / commit (if a regression)

Not a regression.

Environment & logs

Linux x86_64, single-node origin, trust mode, psql 16.

Before submitting

  • I searched existing issues and this is not a duplicate.
  • I reproduced this on current main.
  • This is not a security vulnerability.

Activity

  1. self-assigned this
    on Sep 27, 2026
  2. farhan-syah commented on Nov 2, 2026

    @farhan-syah
    Member

    Fixed in #442. Page inside one BEGIN ... COMMIT block, and every page reads one cut.

    NodeDB does not add pg_snapshot_xmin or SHOW SNAPSHOT. A reader does not pass a snapshot token between pages.

    BEGIN;
    SELECT id, name FROM users ORDER BY id LIMIT 100;
    SELECT id, name FROM users WHERE id > 'u100' ORDER BY id LIMIT 100;
    SELECT id, name FROM users WHERE id > 'u200' ORDER BY id LIMIT 100;
    COMMIT;
    • The block takes its snapshot at the first statement. Every later page reads that snapshot.
    • A producer transaction that commits after the snapshot is invisible on every page.
    • A producer transaction that committed before it is visible on every page, whole.
    • A read-only block commits without a conflict check, so it never fails with a conflict.
    • This holds on every protocol that supports transaction blocks.

    Limit: a snapshot older than [tuning.network] txn_snapshot_max_age_secs (default 300) fails the next statement with SQLSTATE 40001. Finish the scan inside that window, or raise the value. After a 40001, start a new block and restart the scan.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions