Skip to content

Extract ctid strategy - #979

Open
kvch wants to merge 5 commits into
mainfrom
pk-strategy-table-reader-refactoring
Open

Extract ctid strategy#979
kvch wants to merge 5 commits into
mainfrom
pk-strategy-table-reader-refactoring

Conversation

@kvch

@kvch kvch commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Description

Refactors pkg/snapshot/generator/postgres/data so the tableReader strategy is a factory of read sessions instead of a single object the generator reaches into.

Now a tableReader only opens a session per schema, and the session (already bound to its schema and strategy-specific state) does the reading. ctidReader produces a ctidSession that owns the snapshotID, removing it from the signatures of readTable, getTableInfo, and snapshotTableRange, making it impossible to pair a session with the wrong reader, instead of relying on documentation.

Related Issue(s)

Type of Change

  • 🔧 Refactoring (no functional changes)

Changes Made

  • Introduce tableReader/readSession interfaces (table_reader.go) and extract the ctid strategy into ctidSession/ctidReader (ctid_table_reader.go), which owns the snapshotID and totalBytes measurement
  • Move getSnapshotSchemaTotalBytes onto ctidSession.totalBytes — the generator owns the progress bar, the strategy owns how the bytes behind it are measured
  • Wrap the session as well as the reader in instrumentedTableReader; beginSchema now opens a span too, so table spans get a schema parent
  • Extract rowSink (row_sink.go): the row → WAL event → processor → progress path is shared regardless of which rows a strategy reads, so it no longer sits inlined in snapshotTableRange
  • Extract snapshotTx (snapshot_tx.go) and progressTracker (progress_tracker.go) to collapse the duplicated progressTracking bool + progressBars map into a single value with set/advance/complete methods; a zero-value
    tracker no-ops, removing per-call guards
  • Build the reader in newTableReader, so choosing a strategy and applying decorators happens in one place instead of in NewSnapshotGenerator

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed
  • All existing tests pass

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Code is well-commented
  • Documentation updated where necessary

@kvch
kvch force-pushed the pk-strategy-table-reader-refactoring branch from eb1ddb3 to 4f453d8 Compare July 20, 2026 13:53
kvch added 2 commits July 20, 2026 15:54
- Collapse the progressTracking bool + progressBars map clump, which was
  duplicated across the generator and the reader, into a single
  progressTracker value with set/advance/complete methods. A zero-value
  tracker is disabled and no-ops, so callers no longer guard each call.
@kvch
kvch force-pushed the pk-strategy-table-reader-refactoring branch from 4f453d8 to 0cb8d34 Compare July 20, 2026 13:54
@github-actions

Copy link
Copy Markdown

Merging this branch will decrease overall coverage

Impacted Packages Coverage Δ 🤖
github.com/xataio/pgstream/pkg/snapshot/generator/postgres/data 77.04% (-0.10%) 👎

Coverage by file

Changed files (no unit tests)

Changed File Coverage Δ Total Covered Missed 🤖
github.com/xataio/pgstream/pkg/snapshot/generator/postgres/data/ctid_table_reader.go 95.52% (+95.52%) 67 (+67) 64 (+64) 3 (+3) 🌟
github.com/xataio/pgstream/pkg/snapshot/generator/postgres/data/instrumented_table_reader.go 0.00% (ø) 5 (+5) 0 5 (+5)
github.com/xataio/pgstream/pkg/snapshot/generator/postgres/data/instrumented_table_snapshot_generator.go 0.00% (ø) 0 (-4) 0 0 (-4)
github.com/xataio/pgstream/pkg/snapshot/generator/postgres/data/pg_snapshot_generator.go 75.59% (-7.66%) 127 (-82) 96 (-78) 31 (-4) 👎
github.com/xataio/pgstream/pkg/snapshot/generator/postgres/data/progress_tracker.go 76.92% (+76.92%) 13 (+13) 10 (+10) 3 (+3) 🌟
github.com/xataio/pgstream/pkg/snapshot/generator/postgres/data/snapshot_tx.go 100.00% (+100.00%) 13 (+13) 13 (+13) 0 🌟
github.com/xataio/pgstream/pkg/snapshot/generator/postgres/data/table_reader.go 0.00% (ø) 0 0 0

Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code.

Changed unit test files

  • github.com/xataio/pgstream/pkg/snapshot/generator/postgres/data/pg_snapshot_generator_test.go

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

Coverage

Total: 59.8% (±0.0% vs main)

Coverage in packages changed by this PR:

Package Coverage Δ
pkg/snapshot/generator/postgres/data 77.3% -2.1%

kvch and others added 2 commits August 12, 2026 15:56
readSession carried a snapshotID, which is a transaction snapshot detail
of the ctid strategy, and the generator reached into it: for its log
fields and to run its own getSnapshotSchemaTotalBytes. So the generator
still knew how the ctid reader works, and a strategy that doesn't export
a transaction snapshot would have had nothing to put in that field.

A tableReader now only opens a session per schema, and the session does
the reading, already bound to its schema and to the state its strategy
needs. ctidReader produces a ctidSession that owns the snapshotID, which
also drops the id from the signatures of readTable, getTableInfo and
snapshotTableRange, and makes it impossible to pair a session with the
wrong reader - the previous contract could only document that.

Alongside:

- Move getSnapshotSchemaTotalBytes onto ctidSession.totalBytes. The
  generator owns the progress bar, the strategy owns how the bytes
  behind it are measured.
- Wrap the session as well as the reader in instrumentedTableReader,
  since the reader hands out the sessions that do the work. beginSchema
  now opens a span too, so table spans have a schema parent.
- Extract rowSink: the row to wal event to processor to progress path is
  the same whichever rows a strategy decides to read, so it no longer
  sits inlined in snapshotTableRange next to the query and tx handling.
- Build the reader in newTableReader, so choosing a strategy and
  applying the decorators happens in one place instead of in
  NewSnapshotGenerator.
@kvch
kvch marked this pull request as ready for review August 12, 2026 15:10
@kvch kvch added the ai-review Set for simple PRs that does not require human approval label Aug 12, 2026
@xata-bot

Copy link
Copy Markdown

⚠️ The pgstream review agent is deferring to a human reviewer.

  • 962 substantive lines exceeds the 300-line ceiling for auto-review

tier T1-agent. Address the above (or get a human review) and re-add the ai-review label to re-run.

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

Labels

ai-review Set for simple PRs that does not require human approval

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Snapshots] PK snapshot: tableReader seam refactor

2 participants