Question Details

No question body available.

Tags

java postgresql wal logical-replication

Answers (1)

March 26, 2026 Score: 3 Rep: 258,914 Quality: Medium Completeness: 100%

That change was introduced by PostgreSQL commit d5a9d86d8ffcadc52ff3729cd00fbd83bc38643c in v15.

Reading the commit message and the code comments, we can get some interesting information.

From the commit message:

    When skipping empty transactions in synchronous replication mode, we send
    a keepalive message to avoid delaying such transactions.

A code comment:

/
  Maintain a per-transaction level variable to track whether the transaction
  has sent BEGIN. BEGIN is only sent when the first change in a transaction
  is processed. This makes it possible to skip sending a pair of BEGIN/COMMIT
  messages for empty transactions which saves network bandwidth.
 
  This optimization is not used for prepared transactions because if the
  WALSender restarts after prepare of a transaction and before commit prepared
  of the same transaction then we won't be able to figure out if we have
  skipped sending BEGIN/PREPARE of a transaction as it was empty. This is
  because we would have lost the in-memory txndata information that was
  present prior to the restart. This will result in sending a spurious
  COMMIT PREPARED without a corresponding prepared transaction at the
  downstream which would lead to an error when it tries to process it.
 
  XXX We could achieve this optimization by changing protocol to send
  additional information so that downstream can detect that the corresponding
  prepare has not been sent. However, adding such a check for every
  transaction in the downstream could be costly so we might want to do it
  optionally.
 
  We also don't have this optimization for streamed transactions because
  they can contain prepared transactions.
 /

This suggests two possible remedies:

  1. Use streaming mode in your subscription.

    With logical replication, that would work with

    ALTER SUBSCRIPTION subname SET (streaming = parallel);
    

    On the protocol level, you'd have to call STARTREPLICATION with the pgoutput options streaming parallel.

  2. Use synchronous replication. That's probably not what you want to do.