Skip to content

Add skip-empty-xacts option#293

Open
adrijshikhar wants to merge 1 commit into
eulerto:masterfrom
adrijshikhar:skip-empty-xacts
Open

Add skip-empty-xacts option#293
adrijshikhar wants to merge 1 commit into
eulerto:masterfrom
adrijshikhar:skip-empty-xacts

Conversation

@adrijshikhar

@adrijshikhar adrijshikhar commented Jun 2, 2026

Copy link
Copy Markdown

Fixes #106. Supersedes #248, which can no longer be reopened (the fork that submitted it was deleted).

Logical decoding emits a BEGIN/COMMIT pair even for transactions that contain no decodable changes (DDL, VACUUM FULL, REFRESH MATERIALIZED VIEW) or whose changes were all filtered out (filter-tables, add-tables, filter-msg-prefixes). Consumers are flooded with empty changesets; #106 reports thousands of them.

This adds a skip-empty-xacts option (default false, existing behavior unchanged). Same approach as test_decoding.

How it works

  • State is two booleans in JsonDecodingData — no per-transaction allocation.
  • When the option is enabled, the BEGIN output is deferred until the first change that survives the filter checks (add-tables, filter-tables, actions) is emitted, and the COMMIT output is suppressed if no change was emitted.
  • Transactional messages mark the transaction as non-empty; prefix-filtered and non-transactional messages don't.
  • TRUNCATE marks the transaction as non-empty only when at least one truncated relation survives table filtering.
  • Skipped transactions are reported via skipped_xact to OutputPluginUpdateProgress (16+) / update_replication_progress (15), resolving the two XXX comments in pg_decode_commit_txn, so the walsender keeps sending keepalives during long runs of skipped transactions.
  • Works with both format versions and with write-in-chunks (the change callback prepares its chunk after the deferred BEGIN chunk is written).

Tests

Regression test built from the reproduction cases reported in #106: add-tables filtering, partitioned tables, DDL-only transactions, TRUNCATE, VACUUM FULL, REFRESH MATERIALIZED VIEW, plus transactional / non-transactional / prefix-filtered messages. Covers format versions 1 and 2 and write-in-chunks. VACUUM FULL and REFRESH MATERIALIZED VIEW assert on counts rather than raw output because their transaction counts vary across server versions.

make installcheck: all 31 tests pass (PostgreSQL 17).

Logical decoding emits a BEGIN/COMMIT pair even for transactions that
contain no decodable changes (DDL, VACUUM FULL, REFRESH MATERIALIZED
VIEW) or whose changes were all filtered out (filter-tables, add-tables,
filter-msg-prefixes). Consumers are flooded with empty changesets; see
issue eulerto#106 for reports of thousands of them.

Add a skip-empty-xacts option (default false, preserving the current
behavior). When enabled, the BEGIN output is deferred until the first
change that survives filtering is emitted and the COMMIT output is
suppressed if no change was emitted. Transactional messages count as
changes; non-transactional messages are not affected. On PostgreSQL 15
and later, skipped transactions are reported to the progress machinery
(skipped_xact) so the walsender keeps sending keepalives during long
runs of skipped transactions.

This is the same approach used by test_decoding.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Getting an overwhelming amount of transactions with empty change sets despite using add-tables

1 participant