From ed1555a53a1fd6fbc3870f0da578d53a265abb43 Mon Sep 17 00:00:00 2001 From: Atsushi Torikoshi Date: Wed, 12 Aug 2026 15:39:02 +0900 Subject: [PATCH v3] Use start LSN for transactional logical decoding messages Logical decoding currently reports the end LSN of a logical message record for both transactional and non-transactional messages. This differs from regular transactional changes, for which the reported LSN identifies the start of the WAL record. This difference can matter to logical decoding consumers that persist the LSN of decoded changes and use it to determine where to resume decoding after an unexpected interruption. For transactional logical messages, this patch uses the start LSN, as is done for other transactional changes. For non-transactional logical messages, it continues to use the end LSN. Such messages are processed independently of a surrounding transaction, so the end LSN can be used as their confirmed flush position. --- doc/src/sgml/protocol.sgml | 3 ++- src/backend/replication/logical/decode.c | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index 49f81676712..a65a15ad9c7 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -6678,7 +6678,8 @@ psql "dbname=postgres replication=database" -c "IDENTIFY_SYSTEM;" Int64 (XLogRecPtr) - The LSN of the logical decoding message. + The start LSN of the logical decoding message for transactional + messages, or its end LSN for non-transactional messages. diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c index c944be4ac83..fd727a7ec7f 100644 --- a/src/backend/replication/logical/decode.c +++ b/src/backend/replication/logical/decode.c @@ -668,7 +668,12 @@ logicalmsg_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf) if (!message->transactional) snapshot = SnapBuildGetOrBuildSnapshot(builder); - ReorderBufferQueueMessage(ctx->reorder, xid, snapshot, buf->endptr, + /* + * Non-transactional messages are processed as separate transactions on + * the receiver, so use endptr as the confirmed flush position for them. + */ + ReorderBufferQueueMessage(ctx->reorder, xid, snapshot, + message->transactional ? buf->origptr : buf->endptr, message->transactional, message->message, /* first part of message is * prefix */ -- 2.48.1