agora inbox for pgsql-bugs@postgresql.org  
help / color / mirror / Atom feed
BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming
15+ messages / 5 participants
[nested] [flat]

* BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming
@ 2026-08-11 22:58 PG Bug reporting form <noreply@postgresql.org>
  2026-08-13 17:56 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  0 siblings, 1 reply; 15+ messages in thread

From: PG Bug reporting form @ 2026-08-11 22:58 UTC (permalink / raw)
  To: pgsql-bugs@lists.postgresql.org; +Cc: tyler@smarts.io

The following bug has been logged on the website:

Bug reference:      19616
Logged by:          Tyler Smart
Email address:      tyler@smarts.io
PostgreSQL version: 18.4
Operating system:   Linux (Docker; also Google Cloud SQL)
Description:        

Since PostgreSQL 18, pgoutput can send a Stream Abort ('A') message to a
client that connected with proto_version 1 and never enabled streaming.
Protocol 1 clients do not implement the stream message set, so consumers
fail on it: Debezium (and tools that embed it, like Airbyte) dies with
"Unsupported message type: A", and because the crash repeats at the same WAL
position on every restart, the slot stops advancing until
max_slot_wal_keep_size invalidates it.

I reproduced this on 18.4 (Debian 18.4-1.pgdg13+1, official Docker image)
and on Cloud SQL 18.1. The identical script does not reproduce it on 17.10.

The trigger needs three conditions in one decode run:

1. logical_decoding_work_mem is exceeded, so eviction runs.
2. The eviction candidate has already aborted in clog.
3. That transaction has a subtransaction with changes still in memory.



^ permalink  raw  reply  [nested|flat] 15+ messages in thread

* Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming
  2026-08-11 22:58 BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming PG Bug reporting form <noreply@postgresql.org>
@ 2026-08-13 17:56 ` Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-13 23:03   ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  0 siblings, 1 reply; 15+ messages in thread

From: Andrey Rachitskiy @ 2026-08-13 17:56 UTC (permalink / raw)
  To: tyler@smarts.io; pgsql-bugs@lists.postgresql.org

Hi, Tyler!

Thanks for the report and analysis.

ReorderBufferTruncateTXN is used both after streaming and when discarding
already-aborted transactions at eviction.  Streaming callers mark the
top-level xact before truncating.  The abort-discard path does not mark
it.  The attached patch marks a subxact as streamed only when the
top-level xact already is.  A later abort then does not emit stream_abort
for XIDs that were never sent downstream.

Patch with regress tests attached.

чт, 13 авг. 2026 г. в 20:30, PG Bug reporting form <noreply@postgresql.org>:

> The following bug has been logged on the website:
>
> Bug reference:      19616
> Logged by:          Tyler Smart
> Email address:      tyler@smarts.io
> PostgreSQL version: 18.4
> Operating system:   Linux (Docker; also Google Cloud SQL)
> Description:
>
> Since PostgreSQL 18, pgoutput can send a Stream Abort ('A') message to a
> client that connected with proto_version 1 and never enabled streaming.
> Protocol 1 clients do not implement the stream message set, so consumers
> fail on it: Debezium (and tools that embed it, like Airbyte) dies with
> "Unsupported message type: A", and because the crash repeats at the same
> WAL
> position on every restart, the slot stops advancing until
> max_slot_wal_keep_size invalidates it.
>
> I reproduced this on 18.4 (Debian 18.4-1.pgdg13+1, official Docker image)
> and on Cloud SQL 18.1. The identical script does not reproduce it on 17.10.
>
> The trigger needs three conditions in one decode run:
>
> 1. logical_decoding_work_mem is exceeded, so eviction runs.
> 2. The eviction candidate has already aborted in clog.
> 3. That transaction has a subtransaction with changes still in memory.
>
> From reading REL_18_STABLE, the cause appears to be commit 072ee847ad4
> ("Skip logical decoding of already-aborted transactions"). It added
> ReorderBufferCheckAndTruncateAbortedTXN, which discards aborted
> transactions
> at eviction time via ReorderBufferTruncateTXN. That function marks every
> subtransaction that still has in-memory changes as streamed
> (ReorderBufferMaybeMarkTXNStreamed, in the subtxn loop near
> reorderbuffer.c:1675) without checking whether the connection streams at
> all. The top-level transaction is handled correctly, since its marking
> happens at call sites guarded by the streaming flag. Only the
> subtransaction
> marking is unconditional.
>
> When decoding later reaches the abort record, ReorderBufferAbort sees
> rbtxn_is_streamed on the subtransaction and invokes the stream_abort
> callback (near reorderbuffer.c:3092). pgoutput_stream_abort is guarded only
> by assertions, so production builds write 'A' onto a proto_version 1
> stream. I expect a cassert build to fail Assert(rbtxn_is_streamed(toptxn))
> there instead, since the top-level transaction is not marked, though I have
> not verified that.
>
> client that connected with proto_version 1 and never enabled streaming.
> Protocol 1 clients do not implement the stream message set, so consumers
> fail on it: Debezium (and tools that embed it, like Airbyte) dies with
> "Unsupported message type: A", and because the crash repeats at the same
> WAL
> position on every restart, the slot stops advancing until
> max_slot_wal_keep_size invalidates it.
>
> I reproduced this on 18.4 (Debian 18.4-1.pgdg13+1, official Docker image)
> and on Cloud SQL 18.1. The identical script does not reproduce it on 17.10.
>
> The trigger needs three conditions in one decode run:
>
> 1. logical_decoding_work_mem is exceeded, so eviction runs.
> 2. The eviction candidate has already aborted in clog.
> 3. That transaction has a subtransaction with changes still in memory.
>
> From reading REL_18_STABLE, the cause appears to be commit 072ee847ad4
> ("Skip logical decoding of already-aborted transactions"). It added
> ReorderBufferCheckAndTruncateAbortedTXN, which discards aborted
> transactions
> at eviction time via ReorderBufferTruncateTXN. That function marks every
> subtransaction that still has in-memory changes as streamed
> (ReorderBufferMaybeMarkTXNStreamed, in the subtxn loop near
> reorderbuffer.c:1675) without checking whether the connection streams at
> all. The top-level transaction is handled correctly, since its marking
> happens at call sites guarded by the streaming flag. Only the
> subtransaction
> marking is unconditional.
>
> When decoding later reaches the abort record, ReorderBufferAbort sees
> rbtxn_is_streamed on the subtransaction and invokes the stream_abort
> callback (near reorderbuffer.c:3092). pgoutput_stream_abort is guarded only
> by assertions, so production builds write 'A' onto a proto_version 1
> stream. I expect a cassert build to fail Assert(rbtxn_is_streamed(toptxn))
> there instead, since the top-level transaction is not marked, though I have
> not verified that.
>
> Self-contained reproduction (the SQL decoding interface acts as a
> non-streaming client, so no replication client is needed):
>
>   docker run -d -e POSTGRES_PASSWORD=pw postgres:18 -c wal_level=logical
>
>   CREATE TABLE t(id int, filler text);
>   CREATE PUBLICATION pub FOR TABLE t;
>   SELECT pg_create_logical_replication_slot('s', 'pgoutput');
>
>   BEGIN;
>   SAVEPOINT sp;
>   INSERT INTO t VALUES (0, 'subtransaction-change');
>   RELEASE SAVEPOINT sp;
>   INSERT INTO t SELECT g, repeat('x', 1000) FROM generate_series(1, 5000)
> g;
>   ROLLBACK;
>
>   INSERT INTO t VALUES (1, 'after');
>
>   SET logical_decoding_work_mem = '64kB';
>   SELECT chr(get_byte(data,0)) AS msgtype, count(*)
>   FROM pg_logical_slot_peek_binary_changes('s', NULL, NULL,
>        'proto_version','1','publication_names','pub')
>   GROUP BY 1 ORDER BY 2 DESC;
>
> Actual output on 18.4:
>
>    msgtype | count
>   ---------+-------
>    B       |     1
>    R       |     1
>    C       |     1
>    I       |     1
>    A       |     1
>
> Expected: no A row. A proto_version 1 client must never receive stream
> messages, and an aborted transaction should produce no output at all.
> PG 17.10 produces the expected output with the same script, as does 18.4
> when logical_decoding_work_mem is raised enough that eviction never fires.
>
> Impact: any protocol 1 consumer on a busy PG 18 server can hit this with a
> single canceled or deadlocked transaction that used savepoints, decoded
> while the buffer is past logical_decoding_work_mem. We hit it in production
> through Debezium, where the retry loop pinned the slot until Postgres
> invalidated it. Raising logical_decoding_work_mem only lowers the
> probability.
>
> Suggested direction: ReorderBufferTruncateTXN should mark subtransactions
> as streamed only when truncating on behalf of streaming, the same way the
> top-level marking is already gated, or the abort-discard path should skip
> the marking entirely.
>
>
>
>
>

^ permalink  raw  reply  [nested|flat] 15+ messages in thread

* Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming
  2026-08-11 22:58 BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming PG Bug reporting form <noreply@postgresql.org>
  2026-08-13 17:56 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
@ 2026-08-13 23:03   ` Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 03:03     ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  0 siblings, 1 reply; 15+ messages in thread

From: Andrey Rachitskiy @ 2026-08-13 23:03 UTC (permalink / raw)
  To: tyler@smarts.io; pgsql-bugs@lists.postgresql.org

There are two shapes for the fix and I would like opinions on which is
preferred.  Both are attached, they are mutually exclusive, and each
carries the same regress case.

0001-Don-t-mark-discarded-aborted-subxacts-as-streamed.patch adds the
guard at the call site in ReorderBufferTruncateTXN.  It is a one-line
change and it does not touch the shared helper.

v2-0001-Don-t-mark-discarded-aborted-subxacts-as-streamed.patch moves the
rule into ReorderBufferMaybeMarkTXNStreamed itself.  A subxact is marked
only when it has changes and its top-level xact is already streamed.  This
states the invariant in one place and reads well on its own.

Thoughts?

чт, 13 авг. 2026 г. в 22:56, Andrey Rachitskiy <pl0h0yp1@gmail.com>:

> Hi, Tyler!
>
> Thanks for the report and analysis.
>
> ReorderBufferTruncateTXN is used both after streaming and when discarding
> already-aborted transactions at eviction.  Streaming callers mark the
> top-level xact before truncating.  The abort-discard path does not mark
> it.  The attached patch marks a subxact as streamed only when the
> top-level xact already is.  A later abort then does not emit stream_abort
> for XIDs that were never sent downstream.
>
> Patch with regress tests attached.
>
> чт, 13 авг. 2026 г. в 20:30, PG Bug reporting form <noreply@postgresql.org
> >:
>
>> The following bug has been logged on the website:
>>
>> Bug reference:      19616
>> Logged by:          Tyler Smart
>> Email address:      tyler@smarts.io
>> PostgreSQL version: 18.4
>> Operating system:   Linux (Docker; also Google Cloud SQL)
>> Description:
>>
>> Since PostgreSQL 18, pgoutput can send a Stream Abort ('A') message to a
>> client that connected with proto_version 1 and never enabled streaming.
>> Protocol 1 clients do not implement the stream message set, so consumers
>> fail on it: Debezium (and tools that embed it, like Airbyte) dies with
>> "Unsupported message type: A", and because the crash repeats at the same
>> WAL
>> position on every restart, the slot stops advancing until
>> max_slot_wal_keep_size invalidates it.
>>
>> I reproduced this on 18.4 (Debian 18.4-1.pgdg13+1, official Docker image)
>> and on Cloud SQL 18.1. The identical script does not reproduce it on
>> 17.10.
>>
>> The trigger needs three conditions in one decode run:
>>
>> 1. logical_decoding_work_mem is exceeded, so eviction runs.
>> 2. The eviction candidate has already aborted in clog.
>> 3. That transaction has a subtransaction with changes still in memory.
>>
>> From reading REL_18_STABLE, the cause appears to be commit 072ee847ad4
>> ("Skip logical decoding of already-aborted transactions"). It added
>> ReorderBufferCheckAndTruncateAbortedTXN, which discards aborted
>> transactions
>> at eviction time via ReorderBufferTruncateTXN. That function marks every
>> subtransaction that still has in-memory changes as streamed
>> (ReorderBufferMaybeMarkTXNStreamed, in the subtxn loop near
>> reorderbuffer.c:1675) without checking whether the connection streams at
>> all. The top-level transaction is handled correctly, since its marking
>> happens at call sites guarded by the streaming flag. Only the
>> subtransaction
>> marking is unconditional.
>>
>> When decoding later reaches the abort record, ReorderBufferAbort sees
>> rbtxn_is_streamed on the subtransaction and invokes the stream_abort
>> callback (near reorderbuffer.c:3092). pgoutput_stream_abort is guarded
>> only
>> by assertions, so production builds write 'A' onto a proto_version 1
>> stream. I expect a cassert build to fail Assert(rbtxn_is_streamed(toptxn))
>> there instead, since the top-level transaction is not marked, though I
>> have
>> not verified that.
>>
>> client that connected with proto_version 1 and never enabled streaming.
>> Protocol 1 clients do not implement the stream message set, so consumers
>> fail on it: Debezium (and tools that embed it, like Airbyte) dies with
>> "Unsupported message type: A", and because the crash repeats at the same
>> WAL
>> position on every restart, the slot stops advancing until
>> max_slot_wal_keep_size invalidates it.
>>
>> I reproduced this on 18.4 (Debian 18.4-1.pgdg13+1, official Docker image)
>> and on Cloud SQL 18.1. The identical script does not reproduce it on
>> 17.10.
>>
>> The trigger needs three conditions in one decode run:
>>
>> 1. logical_decoding_work_mem is exceeded, so eviction runs.
>> 2. The eviction candidate has already aborted in clog.
>> 3. That transaction has a subtransaction with changes still in memory.
>>
>> From reading REL_18_STABLE, the cause appears to be commit 072ee847ad4
>> ("Skip logical decoding of already-aborted transactions"). It added
>> ReorderBufferCheckAndTruncateAbortedTXN, which discards aborted
>> transactions
>> at eviction time via ReorderBufferTruncateTXN. That function marks every
>> subtransaction that still has in-memory changes as streamed
>> (ReorderBufferMaybeMarkTXNStreamed, in the subtxn loop near
>> reorderbuffer.c:1675) without checking whether the connection streams at
>> all. The top-level transaction is handled correctly, since its marking
>> happens at call sites guarded by the streaming flag. Only the
>> subtransaction
>> marking is unconditional.
>>
>> When decoding later reaches the abort record, ReorderBufferAbort sees
>> rbtxn_is_streamed on the subtransaction and invokes the stream_abort
>> callback (near reorderbuffer.c:3092). pgoutput_stream_abort is guarded
>> only
>> by assertions, so production builds write 'A' onto a proto_version 1
>> stream. I expect a cassert build to fail Assert(rbtxn_is_streamed(toptxn))
>> there instead, since the top-level transaction is not marked, though I
>> have
>> not verified that.
>>
>> Self-contained reproduction (the SQL decoding interface acts as a
>> non-streaming client, so no replication client is needed):
>>
>>   docker run -d -e POSTGRES_PASSWORD=pw postgres:18 -c wal_level=logical
>>
>>   CREATE TABLE t(id int, filler text);
>>   CREATE PUBLICATION pub FOR TABLE t;
>>   SELECT pg_create_logical_replication_slot('s', 'pgoutput');
>>
>>   BEGIN;
>>   SAVEPOINT sp;
>>   INSERT INTO t VALUES (0, 'subtransaction-change');
>>   RELEASE SAVEPOINT sp;
>>   INSERT INTO t SELECT g, repeat('x', 1000) FROM generate_series(1, 5000)
>> g;
>>   ROLLBACK;
>>
>>   INSERT INTO t VALUES (1, 'after');
>>
>>   SET logical_decoding_work_mem = '64kB';
>>   SELECT chr(get_byte(data,0)) AS msgtype, count(*)
>>   FROM pg_logical_slot_peek_binary_changes('s', NULL, NULL,
>>        'proto_version','1','publication_names','pub')
>>   GROUP BY 1 ORDER BY 2 DESC;
>>
>> Actual output on 18.4:
>>
>>    msgtype | count
>>   ---------+-------
>>    B       |     1
>>    R       |     1
>>    C       |     1
>>    I       |     1
>>    A       |     1
>>
>> Expected: no A row. A proto_version 1 client must never receive stream
>> messages, and an aborted transaction should produce no output at all.
>> PG 17.10 produces the expected output with the same script, as does 18.4
>> when logical_decoding_work_mem is raised enough that eviction never fires.
>>
>> Impact: any protocol 1 consumer on a busy PG 18 server can hit this with a
>> single canceled or deadlocked transaction that used savepoints, decoded
>> while the buffer is past logical_decoding_work_mem. We hit it in
>> production
>> through Debezium, where the retry loop pinned the slot until Postgres
>> invalidated it. Raising logical_decoding_work_mem only lowers the
>> probability.
>>
>> Suggested direction: ReorderBufferTruncateTXN should mark subtransactions
>> as streamed only when truncating on behalf of streaming, the same way the
>> top-level marking is already gated, or the abort-discard path should skip
>> the marking entirely.
>>
>>
>>
>>
>>

-- 
Regards,
Rachitskiy Andrey

^ permalink  raw  reply  [nested|flat] 15+ messages in thread

* RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming
  2026-08-11 22:58 BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming PG Bug reporting form <noreply@postgresql.org>
  2026-08-13 17:56 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-13 23:03   ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
@ 2026-08-14 03:03     ` Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 04:56       ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  0 siblings, 1 reply; 15+ messages in thread

From: Hayato Kuroda (Fujitsu) @ 2026-08-14 03:03 UTC (permalink / raw)
  To: Andrey Rachitskiy <pl0h0yp1@gmail.com>; tyler@smarts.io <tyler@smarts.io>; +Cc: Masahiko Sawada <sawada.mshk@gmail.com>; pgsql-bugs@lists.postgresql.org <pgsql-bugs@lists.postgresql.org>

Dear Tyler, Andrey,
(Adding Sawada-san in CC)

Good catch and thanks for the report. I confirmed that your reproducer causes an
Assert failure for the debug build. See:

```
(gdb) bt
#0  ReorderBufferMaybeMarkTXNStreamed (rb=0x3385430, txn=0x33a55a8)
    at ../postgres/src/backend/replication/logical/reorderbuffer.c:2154
#1  0x0000000000973d05 in ReorderBufferTruncateTXN (rb=0x3385430, txn=0x33a5440, 
    txn_prepared=false) at ../postgres/src/backend/replication/logical/reorderbuffer.c:1677
#2  0x00000000009740ec in ReorderBufferCheckAndTruncateAbortedTXN (rb=0x3385430, txn=0x33a5440)
    at ../postgres/src/backend/replication/logical/reorderbuffer.c:1816
#3  0x0000000000977aa1 in ReorderBufferCheckMemoryLimit (rb=0x3385430)
    at ../postgres/src/backend/replication/logical/reorderbuffer.c:3985
#4  0x0000000000972345 in ReorderBufferQueueChange (rb=0x3385430, xid=696, lsn=24970056, 
    change=0x33a8be0, toast_insert=false)
...
```

Few comments for the code:

```
+ * A top-level transaction is always marked.  A subtransaction is marked only
+ * when it has changes and its top-level transaction is already streamed.
```

The last sentence can be "its top-level transaction is already marked as streamed."

```
+       /*
+        * A subtransaction is marked only when it has changes, and only when its
+        * top-level transaction has already been marked as streamed.  We never
+        * stream XIDs of empty subxacts, and we must not send an abort for an XID
+        * the downstream has never heard of.
         *
-        * We do it this way because of aborts - we don't want to send aborts for
-        * XIDs the downstream is not aware of. And of course, it always knows
-        * about the top-level xact (we send the XID in all messages), but we
-        * never stream XIDs of empty subxacts.
+        * The top-level check matters because ReorderBufferTruncateTXN is also
+        * used to discard already-aborted transactions at eviction, where the
+        * top-level xact is not streamed.  Marking a subxact there would make a
+        * later abort emit stream_abort to a client that never enabled streaming.
         */
-       if (rbtxn_is_toptxn(txn) || (txn->nentries_mem != 0))
+       if (txn->nentries_mem != 0 && rbtxn_is_streamed(rbtxn_get_toptxn(txn)))
```

I feel the code comment might be too detail: second paragraph is not needed
for me.

Best regards,
Hayato Kuroda
FUJITSU LIMITED



^ permalink  raw  reply  [nested|flat] 15+ messages in thread

* Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming
  2026-08-11 22:58 BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming PG Bug reporting form <noreply@postgresql.org>
  2026-08-13 17:56 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-13 23:03   ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 03:03     ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
@ 2026-08-14 04:56       ` Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 06:10         ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  0 siblings, 1 reply; 15+ messages in thread

From: Andrey Rachitskiy @ 2026-08-14 04:56 UTC (permalink / raw)
  To: Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>; +Cc: tyler@smarts.io <tyler@smarts.io>; Masahiko Sawada <sawada.mshk@gmail.com>; pgsql-bugs@lists.postgresql.org <pgsql-bugs@lists.postgresql.org>

Dear Kuroda-san!

Thanks for the review.

I addressed both comments in the attached v3.


пт, 14 авг. 2026 г. в 08:03, Hayato Kuroda (Fujitsu) <
kuroda.hayato@fujitsu.com>:

> Dear Tyler, Andrey,
> (Adding Sawada-san in CC)
>
> Good catch and thanks for the report. I confirmed that your reproducer
> causes an
> Assert failure for the debug build. See:
>
> ```
> (gdb) bt
> #0  ReorderBufferMaybeMarkTXNStreamed (rb=0x3385430, txn=0x33a55a8)
>     at ../postgres/src/backend/replication/logical/reorderbuffer.c:2154
> #1  0x0000000000973d05 in ReorderBufferTruncateTXN (rb=0x3385430,
> txn=0x33a5440,
>     txn_prepared=false) at
> ../postgres/src/backend/replication/logical/reorderbuffer.c:1677
> #2  0x00000000009740ec in ReorderBufferCheckAndTruncateAbortedTXN
> (rb=0x3385430, txn=0x33a5440)
>     at ../postgres/src/backend/replication/logical/reorderbuffer.c:1816
> #3  0x0000000000977aa1 in ReorderBufferCheckMemoryLimit (rb=0x3385430)
>     at ../postgres/src/backend/replication/logical/reorderbuffer.c:3985
> #4  0x0000000000972345 in ReorderBufferQueueChange (rb=0x3385430, xid=696,
> lsn=24970056,
>     change=0x33a8be0, toast_insert=false)
> ...
> ```
>
> Few comments for the code:
>
> ```
> + * A top-level transaction is always marked.  A subtransaction is marked
> only
> + * when it has changes and its top-level transaction is already streamed.
> ```
>
> The last sentence can be "its top-level transaction is already marked as
> streamed."
>
> ```
> +       /*
> +        * A subtransaction is marked only when it has changes, and only
> when its
> +        * top-level transaction has already been marked as streamed.  We
> never
> +        * stream XIDs of empty subxacts, and we must not send an abort
> for an XID
> +        * the downstream has never heard of.
>          *
> -        * We do it this way because of aborts - we don't want to send
> aborts for
> -        * XIDs the downstream is not aware of. And of course, it always
> knows
> -        * about the top-level xact (we send the XID in all messages), but
> we
> -        * never stream XIDs of empty subxacts.
> +        * The top-level check matters because ReorderBufferTruncateTXN is
> also
> +        * used to discard already-aborted transactions at eviction, where
> the
> +        * top-level xact is not streamed.  Marking a subxact there would
> make a
> +        * later abort emit stream_abort to a client that never enabled
> streaming.
>          */
> -       if (rbtxn_is_toptxn(txn) || (txn->nentries_mem != 0))
> +       if (txn->nentries_mem != 0 &&
> rbtxn_is_streamed(rbtxn_get_toptxn(txn)))
> ```
>
> I feel the code comment might be too detail: second paragraph is not needed
> for me.
>
> Best regards,
> Hayato Kuroda
> FUJITSU LIMITED
>
>

-- 
Regards,
Rachitskiy Andrey

Attachments:

  [text/x-patch] v3-0001-Don-t-mark-discarded-aborted-subxacts-as-streamed.patch (6.3K, ../../CAB8bMiuRPBfvMxsf-gUay_XKtaT0xKR9T0LNRaGmfok7YhMHtA@mail.gmail.com/3-v3-0001-Don-t-mark-discarded-aborted-subxacts-as-streamed.patch)
  download | inline diff:
From c2f7b1d3e5a69b0c8f4d2e1a3b7c9e5d0f8a2b46 Mon Sep 17 00:00:00 2001
From: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Date: Fri, 14 Aug 2026 09:27:00 +0500
Subject: [PATCH v3] Don't mark discarded aborted subxacts as streamed.

ReorderBufferTruncateTXN is also used when discarding already-aborted
transactions at eviction.  Marking every subxact with in-memory changes
as streamed in that path made a later abort emit stream_abort to clients
that never enabled streaming.

Mark a subxact as streamed only when it has changes and its top-level
transaction is already marked as streamed.  The streaming call sites mark
the top-level xact first, so this keeps their behavior.  The abort-discard
path never marks the top-level xact, so its subxacts are left unmarked.

BUG #19616
Reported-by: Tyler Smart <tyler@smarts.io>
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Reviewed-by: Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
Discussion: https://www.postgresql.org/message-id/19616-f6153af509910853%40postgresql.org
---
 contrib/test_decoding/expected/spill.out        | 35 +++++++++++++++++++
 contrib/test_decoding/sql/spill.sql             | 18 ++++++++++
 src/backend/replication/logical/reorderbuffer.c | 36 +++++++++++--------
 3 files changed, 75 insertions(+), 14 deletions(-)

diff --git a/contrib/test_decoding/expected/spill.out b/contrib/test_decoding/expected/spill.out
index 10734bdb6af..1b56615af01 100644
--- a/contrib/test_decoding/expected/spill.out
+++ b/contrib/test_decoding/expected/spill.out
@@ -247,6 +247,41 @@ GROUP BY 1 ORDER BY 1;
  'serialize-nested-subbig-subbigabort-subbig-3 |  5000 | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subbigabort-subbig-3:5001' | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subbigabort-subbig-3:10000'
 (2 rows)
 
+-- Aborted xact discarded at eviction, with a subxact still in memory.
+-- proto_version 1 must not see Stream Abort ('A').
+CREATE PUBLICATION spill_pub FOR TABLE spill_test;
+SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_pgoutput', 'pgoutput');
+ ?column? 
+----------
+ init
+(1 row)
+
+BEGIN;
+SAVEPOINT s;
+INSERT INTO spill_test VALUES ('subtransaction-change');
+RELEASE SAVEPOINT s;
+INSERT INTO spill_test SELECT repeat('x', 1000) FROM generate_series(1, 5000) g(i);
+ROLLBACK;
+INSERT INTO spill_test VALUES ('after-abort');
+SELECT chr(get_byte(data, 0)) AS msgtype, count(*)
+FROM pg_logical_slot_peek_binary_changes('regression_slot_pgoutput', NULL, NULL,
+     'proto_version', '1', 'publication_names', 'spill_pub')
+GROUP BY 1 ORDER BY 1;
+ msgtype | count 
+---------+-------
+ B       |     1
+ C       |     1
+ I       |     1
+ R       |     1
+(4 rows)
+
+SELECT pg_drop_replication_slot('regression_slot_pgoutput');
+ pg_drop_replication_slot 
+--------------------------
+ 
+(1 row)
+
+DROP PUBLICATION spill_pub;
 DROP TABLE spill_test;
 SELECT pg_drop_replication_slot('regression_slot');
  pg_drop_replication_slot 
diff --git a/contrib/test_decoding/sql/spill.sql b/contrib/test_decoding/sql/spill.sql
index e638cacd3f9..3f0b77ac269 100644
--- a/contrib/test_decoding/sql/spill.sql
+++ b/contrib/test_decoding/sql/spill.sql
@@ -174,6 +174,24 @@ SELECT (regexp_split_to_array(data, ':'))[4] COLLATE "C", COUNT(*), (array_agg(d
 FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT'
 GROUP BY 1 ORDER BY 1;
 
+-- Aborted xact discarded at eviction, with a subxact still in memory.
+-- proto_version 1 must not see Stream Abort ('A').
+CREATE PUBLICATION spill_pub FOR TABLE spill_test;
+SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_pgoutput', 'pgoutput');
+BEGIN;
+SAVEPOINT s;
+INSERT INTO spill_test VALUES ('subtransaction-change');
+RELEASE SAVEPOINT s;
+INSERT INTO spill_test SELECT repeat('x', 1000) FROM generate_series(1, 5000) g(i);
+ROLLBACK;
+INSERT INTO spill_test VALUES ('after-abort');
+SELECT chr(get_byte(data, 0)) AS msgtype, count(*)
+FROM pg_logical_slot_peek_binary_changes('regression_slot_pgoutput', NULL, NULL,
+     'proto_version', '1', 'publication_names', 'spill_pub')
+GROUP BY 1 ORDER BY 1;
+SELECT pg_drop_replication_slot('regression_slot_pgoutput');
+DROP PUBLICATION spill_pub;
+
 DROP TABLE spill_test;
 
 SELECT pg_drop_replication_slot('regression_slot');
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 6aed6346366..107c2db68c1 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -2132,26 +2132,34 @@ ReorderBufferSaveTXNSnapshot(ReorderBuffer *rb, ReorderBufferTXN *txn,
 }
 
 /*
- * Mark the given transaction as streamed if it's a top-level transaction
- * or has changes.
+ * Mark the given transaction as streamed, if appropriate.
+ *
+ * A top-level transaction is always marked.  A subtransaction is marked
+ * only when it has changes and its top-level transaction is already
+ * marked as streamed.
  */
 static void
 ReorderBufferMaybeMarkTXNStreamed(ReorderBuffer *rb, ReorderBufferTXN *txn)
 {
 	/*
-	 * The top-level transaction, is marked as streamed always, even if it
-	 * does not contain any changes (that is, when all the changes are in
-	 * subtransactions).
-	 *
-	 * For subtransactions, we only mark them as streamed when there are
-	 * changes in them.
-	 *
-	 * We do it this way because of aborts - we don't want to send aborts for
-	 * XIDs the downstream is not aware of. And of course, it always knows
-	 * about the top-level xact (we send the XID in all messages), but we
-	 * never stream XIDs of empty subxacts.
+	 * The top-level transaction is marked as streamed always, even if it does
+	 * not contain any changes (that is, when all the changes are in
+	 * subtransactions).  The downstream always knows about it, since we send
+	 * its XID in every message.
+	 */
+	if (rbtxn_is_toptxn(txn))
+	{
+		txn->txn_flags |= RBTXN_IS_STREAMED;
+		return;
+	}
+
+	/*
+	 * A subtransaction is marked only when it has changes, and only when its
+	 * top-level transaction has already been marked as streamed.  We never
+	 * stream XIDs of empty subxacts, and we must not send an abort for an XID
+	 * the downstream has never heard of.
 	 */
-	if (rbtxn_is_toptxn(txn) || (txn->nentries_mem != 0))
+	if (txn->nentries_mem != 0 && rbtxn_is_streamed(rbtxn_get_toptxn(txn)))
 		txn->txn_flags |= RBTXN_IS_STREAMED;
 }
 
-- 
2.53.0


^ permalink  raw  reply  [nested|flat] 15+ messages in thread

* RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming
  2026-08-11 22:58 BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming PG Bug reporting form <noreply@postgresql.org>
  2026-08-13 17:56 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-13 23:03   ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 03:03     ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 04:56       ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
@ 2026-08-14 06:10         ` Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 06:44           ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  0 siblings, 1 reply; 15+ messages in thread

From: Hayato Kuroda (Fujitsu) @ 2026-08-14 06:10 UTC (permalink / raw)
  To: Andrey Rachitskiy <pl0h0yp1@gmail.com>; +Cc: tyler@smarts.io <tyler@smarts.io>; Masahiko Sawada <sawada.mshk@gmail.com>; pgsql-bugs@lists.postgresql.org <pgsql-bugs@lists.postgresql.org>

Dear Andrey,

Thanks for the update. One more comment for the test.

```
+-- Aborted xact discarded at eviction, with a subxact still in memory.
+-- proto_version 1 must not see Stream Abort ('A').
```

I think we should describe bit more what happened there. How about like:

-- bug #19616
-- pgoutput protocol compatibility could be broken for an aborted xact
-- discarded at spill eviction while a subxact remained in memory.
-- Stream Abort ('A'), valid only since protocol version 2, could be seen
-- with protocol version 1.

BTW,  our community tries not to do top-post reply. See the guideline:

https://wiki.postgresql.org/wiki/Mailing_Lists

Best regards,
Hayato Kuroda
FUJITSU LIMITED



^ permalink  raw  reply  [nested|flat] 15+ messages in thread

* Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming
  2026-08-11 22:58 BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming PG Bug reporting form <noreply@postgresql.org>
  2026-08-13 17:56 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-13 23:03   ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 03:03     ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 04:56       ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 06:10         ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
@ 2026-08-14 06:44           ` Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 10:09             ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 12:33             ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Fujii Masao <masao.fujii@gmail.com>
  0 siblings, 2 replies; 15+ messages in thread

From: Andrey Rachitskiy @ 2026-08-14 06:44 UTC (permalink / raw)
  To: Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>; +Cc: tyler@smarts.io <tyler@smarts.io>; Masahiko Sawada <sawada.mshk@gmail.com>; pgsql-bugs@lists.postgresql.org <pgsql-bugs@lists.postgresql.org>

> I think we should describe bit more what happened there. How about like:
>
> -- bug #19616
> -- pgoutput protocol compatibility could be broken for an aborted xact
> -- discarded at spill eviction while a subxact remained in memory.
> -- Stream Abort ('A'), valid only since protocol version 2, could be seen
> -- with protocol version 1.
>
>
I have updated the patch with the suggested changes.


> BTW,  our community tries not to do top-post reply.
>

Dear Kuroda-San,
Thank you for pointing this out. I will keep that in mind for future
messages.

-- 
Regards,
Rachitskiy Andrey

Attachments:

  [text/x-patch] v3-0001-Don-t-mark-discarded-aborted-subxacts-as-streamed.patch (6.6K, ../../CAB8bMiuXDh8no3AX+FWURu8_bcu1fY6AyRt5EbFVU8BB=rNyeg@mail.gmail.com/3-v3-0001-Don-t-mark-discarded-aborted-subxacts-as-streamed.patch)
  download | inline diff:
From 1aef8eb15107e55fafe1ed8e9ff459990acaa6fa Mon Sep 17 00:00:00 2001
From: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Date: Fri, 14 Aug 2026 09:27:00 +0500
Subject: [PATCH v3] Don't mark discarded aborted subxacts as streamed.

ReorderBufferTruncateTXN is also used when discarding already-aborted
transactions at eviction.  Marking every subxact with in-memory changes
as streamed in that path made a later abort emit stream_abort to clients
that never enabled streaming.

Mark a subxact as streamed only when it has changes and its top-level
transaction is already marked as streamed.  The streaming call sites mark
the top-level xact first, so this keeps their behavior.  The abort-discard
path never marks the top-level xact, so its subxacts are left unmarked.

BUG #19616
Reported-by: Tyler Smart <tyler@smarts.io>
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Reviewed-by: Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
Discussion: https://www.postgresql.org/message-id/19616-f6153af509910853%40postgresql.org
---
 contrib/test_decoding/expected/spill.out      | 38 +++++++++++++++++++
 contrib/test_decoding/sql/spill.sql           | 21 ++++++++++
 .../replication/logical/reorderbuffer.c       | 36 +++++++++++-------
 3 files changed, 81 insertions(+), 14 deletions(-)

diff --git a/contrib/test_decoding/expected/spill.out b/contrib/test_decoding/expected/spill.out
index 10734bdb6af..9411dbec38f 100644
--- a/contrib/test_decoding/expected/spill.out
+++ b/contrib/test_decoding/expected/spill.out
@@ -247,6 +247,44 @@ GROUP BY 1 ORDER BY 1;
  'serialize-nested-subbig-subbigabort-subbig-3 |  5000 | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subbigabort-subbig-3:5001' | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subbigabort-subbig-3:10000'
 (2 rows)
 
+-- bug #19616
+-- pgoutput protocol compatibility could be broken for an aborted xact
+-- discarded at spill eviction while a subxact remained in memory.
+-- Stream Abort ('A'), valid only since protocol version 2, could be seen
+-- with protocol version 1.
+CREATE PUBLICATION spill_pub FOR TABLE spill_test;
+SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_pgoutput', 'pgoutput');
+ ?column? 
+----------
+ init
+(1 row)
+
+BEGIN;
+SAVEPOINT s;
+INSERT INTO spill_test VALUES ('subtransaction-change');
+RELEASE SAVEPOINT s;
+INSERT INTO spill_test SELECT repeat('x', 1000) FROM generate_series(1, 5000) g(i);
+ROLLBACK;
+INSERT INTO spill_test VALUES ('after-abort');
+SELECT chr(get_byte(data, 0)) AS msgtype, count(*)
+FROM pg_logical_slot_peek_binary_changes('regression_slot_pgoutput', NULL, NULL,
+     'proto_version', '1', 'publication_names', 'spill_pub')
+GROUP BY 1 ORDER BY 1;
+ msgtype | count 
+---------+-------
+ B       |     1
+ C       |     1
+ I       |     1
+ R       |     1
+(4 rows)
+
+SELECT pg_drop_replication_slot('regression_slot_pgoutput');
+ pg_drop_replication_slot 
+--------------------------
+ 
+(1 row)
+
+DROP PUBLICATION spill_pub;
 DROP TABLE spill_test;
 SELECT pg_drop_replication_slot('regression_slot');
  pg_drop_replication_slot 
diff --git a/contrib/test_decoding/sql/spill.sql b/contrib/test_decoding/sql/spill.sql
index e638cacd3f9..1ffa313f7c8 100644
--- a/contrib/test_decoding/sql/spill.sql
+++ b/contrib/test_decoding/sql/spill.sql
@@ -174,6 +174,27 @@ SELECT (regexp_split_to_array(data, ':'))[4] COLLATE "C", COUNT(*), (array_agg(d
 FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT'
 GROUP BY 1 ORDER BY 1;
 
+-- bug #19616
+-- pgoutput protocol compatibility could be broken for an aborted xact
+-- discarded at spill eviction while a subxact remained in memory.
+-- Stream Abort ('A'), valid only since protocol version 2, could be seen
+-- with protocol version 1.
+CREATE PUBLICATION spill_pub FOR TABLE spill_test;
+SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_pgoutput', 'pgoutput');
+BEGIN;
+SAVEPOINT s;
+INSERT INTO spill_test VALUES ('subtransaction-change');
+RELEASE SAVEPOINT s;
+INSERT INTO spill_test SELECT repeat('x', 1000) FROM generate_series(1, 5000) g(i);
+ROLLBACK;
+INSERT INTO spill_test VALUES ('after-abort');
+SELECT chr(get_byte(data, 0)) AS msgtype, count(*)
+FROM pg_logical_slot_peek_binary_changes('regression_slot_pgoutput', NULL, NULL,
+     'proto_version', '1', 'publication_names', 'spill_pub')
+GROUP BY 1 ORDER BY 1;
+SELECT pg_drop_replication_slot('regression_slot_pgoutput');
+DROP PUBLICATION spill_pub;
+
 DROP TABLE spill_test;
 
 SELECT pg_drop_replication_slot('regression_slot');
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 6aed6346366..c3ef053f27b 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -2132,26 +2132,34 @@ ReorderBufferSaveTXNSnapshot(ReorderBuffer *rb, ReorderBufferTXN *txn,
 }
 
 /*
- * Mark the given transaction as streamed if it's a top-level transaction
- * or has changes.
+ * Mark the given transaction as streamed, if appropriate.
+ *
+ * A top-level transaction is always marked.  A subtransaction is marked
+ * only when it has changes and its top-level transaction is already
+ * marked as streamed.
  */
 static void
 ReorderBufferMaybeMarkTXNStreamed(ReorderBuffer *rb, ReorderBufferTXN *txn)
 {
 	/*
-	 * The top-level transaction, is marked as streamed always, even if it
-	 * does not contain any changes (that is, when all the changes are in
-	 * subtransactions).
-	 *
-	 * For subtransactions, we only mark them as streamed when there are
-	 * changes in them.
-	 *
-	 * We do it this way because of aborts - we don't want to send aborts for
-	 * XIDs the downstream is not aware of. And of course, it always knows
-	 * about the top-level xact (we send the XID in all messages), but we
-	 * never stream XIDs of empty subxacts.
+	 * The top-level transaction is marked as streamed always, even if it does
+	 * not contain any changes (that is, when all the changes are in
+	 * subtransactions).  The downstream always knows about it, since we send
+	 * its XID in every message.
+	 */
+	if (rbtxn_is_toptxn(txn))
+	{
+		txn->txn_flags |= RBTXN_IS_STREAMED;
+		return;
+	}
+
+	/*
+	 * A subtransaction is marked only when it has changes, and only when its
+	 * top-level transaction has already been marked as streamed.  We never
+	 * stream XIDs of empty subxacts, and we must not send an abort for an XID
+	 * the downstream has never heard of.
 	 */
-	if (rbtxn_is_toptxn(txn) || (txn->nentries_mem != 0))
+	if (txn->nentries_mem != 0 && rbtxn_is_streamed(rbtxn_get_toptxn(txn)))
 		txn->txn_flags |= RBTXN_IS_STREAMED;
 }
 
-- 
2.53.0



^ permalink  raw  reply  [nested|flat] 15+ messages in thread

* RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming
  2026-08-11 22:58 BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming PG Bug reporting form <noreply@postgresql.org>
  2026-08-13 17:56 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-13 23:03   ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 03:03     ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 04:56       ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 06:10         ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 06:44           ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
@ 2026-08-14 10:09             ` Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  1 sibling, 0 replies; 15+ messages in thread

From: Hayato Kuroda (Fujitsu) @ 2026-08-14 10:09 UTC (permalink / raw)
  To: Andrey Rachitskiy <pl0h0yp1@gmail.com>; +Cc: tyler@smarts.io <tyler@smarts.io>; Masahiko Sawada <sawada.mshk@gmail.com>; pgsql-bugs@lists.postgresql.org <pgsql-bugs@lists.postgresql.org>

Dear Andrey,

Thanks for the update. For now the patch LGTM.
Let’s see how others say.

> Dear Kuroda-San,
> Thank you for pointing this out. I will keep that in mind for future messages.

FYI, your style is good :-).

Best regards,
Hayato Kuroda
FUJITSU LIMITED



^ permalink  raw  reply  [nested|flat] 15+ messages in thread

* Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming
  2026-08-11 22:58 BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming PG Bug reporting form <noreply@postgresql.org>
  2026-08-13 17:56 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-13 23:03   ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 03:03     ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 04:56       ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 06:10         ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 06:44           ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
@ 2026-08-14 12:33             ` Fujii Masao <masao.fujii@gmail.com>
  2026-08-14 14:34               ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  1 sibling, 1 reply; 15+ messages in thread

From: Fujii Masao @ 2026-08-14 12:33 UTC (permalink / raw)
  To: Andrey Rachitskiy <pl0h0yp1@gmail.com>; +Cc: Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>; tyler@smarts.io <tyler@smarts.io>; Masahiko Sawada <sawada.mshk@gmail.com>; pgsql-bugs@lists.postgresql.org <pgsql-bugs@lists.postgresql.org>

On Fri, Aug 14, 2026 at 3:45 PM Andrey Rachitskiy <pl0h0yp1@gmail.com> wrote:
> I have updated the patch with the suggested changes.

Thanks for updating the patch! LGTM.

One minor comment: ReorderBufferMaybeMarkTXNStreamed() seems to rely on
the assumption that it is never called for a top-level transaction when
streaming is disabled. If that's correct, how about documenting this
assumption in the function comment and/or adding an assertion such as:

      if (rbtxn_is_toptxn(txn))
      {
          Assert(ReorderBufferCanStream(rb));
          txn->txn_flags |= RBTXN_IS_STREAMED;

Regards,

-- 
Fujii Masao






^ permalink  raw  reply  [nested|flat] 15+ messages in thread

* Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming
  2026-08-11 22:58 BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming PG Bug reporting form <noreply@postgresql.org>
  2026-08-13 17:56 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-13 23:03   ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 03:03     ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 04:56       ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 06:10         ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 06:44           ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 12:33             ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Fujii Masao <masao.fujii@gmail.com>
@ 2026-08-14 14:34               ` Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 22:15                 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Masahiko Sawada <sawada.mshk@gmail.com>
  0 siblings, 1 reply; 15+ messages in thread

From: Andrey Rachitskiy @ 2026-08-14 14:34 UTC (permalink / raw)
  To: Fujii Masao <masao.fujii@gmail.com>; +Cc: Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>; tyler@smarts.io <tyler@smarts.io>; Masahiko Sawada <sawada.mshk@gmail.com>; pgsql-bugs@lists.postgresql.org <pgsql-bugs@lists.postgresql.org>

пт, 14 авг. 2026 г. в 17:33, Fujii Masao <masao.fujii@gmail.com>:

> One minor comment: ReorderBufferMaybeMarkTXNStreamed() seems to rely on
> the assumption that it is never called for a top-level transaction when
> streaming is disabled. If that's correct, how about documenting this
> assumption in the function comment and/or adding an assertion such as:
>
>       if (rbtxn_is_toptxn(txn))
>       {
>           Assert(ReorderBufferCanStream(rb));
>           txn->txn_flags |= RBTXN_IS_STREAMED;
>
> Dear Fujii-san,

Thanks for the review.
v4 with assert and comment, in attachment.

-- 
Regards,
Rachitskiy Andrey

Attachments:

  [text/x-patch] v4-0001-Don-t-mark-discarded-aborted-subxacts-as-streamed.patch (6.7K, ../../CAB8bMisGEQ02trVibY5GGwfp9NdEZC-dJARNKd25AVnzLeogNQ@mail.gmail.com/3-v4-0001-Don-t-mark-discarded-aborted-subxacts-as-streamed.patch)
  download | inline diff:
From 9935501a4d794b599b557e4eb030c663700d1336 Mon Sep 17 00:00:00 2001
From: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Date: Fri, 14 Aug 2026 09:27:00 +0500
Subject: [PATCH v4] Don't mark discarded aborted subxacts as streamed.

ReorderBufferTruncateTXN is also used when discarding already-aborted
transactions at eviction.  Marking every subxact with in-memory changes
as streamed in that path made a later abort emit stream_abort to clients
that never enabled streaming.

Mark a subxact as streamed only when it has changes and its top-level
transaction is already marked as streamed.  The streaming call sites mark
the top-level xact first, so this keeps their behavior.  The abort-discard
path never marks the top-level xact, so its subxacts are left unmarked.

BUG #19616
Reported-by: Tyler Smart <tyler@smarts.io>
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Reviewed-by: Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://www.postgresql.org/message-id/19616-f6153af509910853%40postgresql.org
---
 contrib/test_decoding/expected/spill.out      | 38 +++++++++++++++++++
 contrib/test_decoding/sql/spill.sql           | 21 ++++++++++
 .../replication/logical/reorderbuffer.c       | 38 ++++++++++++-------
 3 files changed, 83 insertions(+), 14 deletions(-)

diff --git a/contrib/test_decoding/expected/spill.out b/contrib/test_decoding/expected/spill.out
index 10734bdb6af..9411dbec38f 100644
--- a/contrib/test_decoding/expected/spill.out
+++ b/contrib/test_decoding/expected/spill.out
@@ -247,6 +247,44 @@ GROUP BY 1 ORDER BY 1;
  'serialize-nested-subbig-subbigabort-subbig-3 |  5000 | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subbigabort-subbig-3:5001' | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subbigabort-subbig-3:10000'
 (2 rows)
 
+-- bug #19616
+-- pgoutput protocol compatibility could be broken for an aborted xact
+-- discarded at spill eviction while a subxact remained in memory.
+-- Stream Abort ('A'), valid only since protocol version 2, could be seen
+-- with protocol version 1.
+CREATE PUBLICATION spill_pub FOR TABLE spill_test;
+SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_pgoutput', 'pgoutput');
+ ?column? 
+----------
+ init
+(1 row)
+
+BEGIN;
+SAVEPOINT s;
+INSERT INTO spill_test VALUES ('subtransaction-change');
+RELEASE SAVEPOINT s;
+INSERT INTO spill_test SELECT repeat('x', 1000) FROM generate_series(1, 5000) g(i);
+ROLLBACK;
+INSERT INTO spill_test VALUES ('after-abort');
+SELECT chr(get_byte(data, 0)) AS msgtype, count(*)
+FROM pg_logical_slot_peek_binary_changes('regression_slot_pgoutput', NULL, NULL,
+     'proto_version', '1', 'publication_names', 'spill_pub')
+GROUP BY 1 ORDER BY 1;
+ msgtype | count 
+---------+-------
+ B       |     1
+ C       |     1
+ I       |     1
+ R       |     1
+(4 rows)
+
+SELECT pg_drop_replication_slot('regression_slot_pgoutput');
+ pg_drop_replication_slot 
+--------------------------
+ 
+(1 row)
+
+DROP PUBLICATION spill_pub;
 DROP TABLE spill_test;
 SELECT pg_drop_replication_slot('regression_slot');
  pg_drop_replication_slot 
diff --git a/contrib/test_decoding/sql/spill.sql b/contrib/test_decoding/sql/spill.sql
index e638cacd3f9..1ffa313f7c8 100644
--- a/contrib/test_decoding/sql/spill.sql
+++ b/contrib/test_decoding/sql/spill.sql
@@ -174,6 +174,27 @@ SELECT (regexp_split_to_array(data, ':'))[4] COLLATE "C", COUNT(*), (array_agg(d
 FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT'
 GROUP BY 1 ORDER BY 1;
 
+-- bug #19616
+-- pgoutput protocol compatibility could be broken for an aborted xact
+-- discarded at spill eviction while a subxact remained in memory.
+-- Stream Abort ('A'), valid only since protocol version 2, could be seen
+-- with protocol version 1.
+CREATE PUBLICATION spill_pub FOR TABLE spill_test;
+SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_pgoutput', 'pgoutput');
+BEGIN;
+SAVEPOINT s;
+INSERT INTO spill_test VALUES ('subtransaction-change');
+RELEASE SAVEPOINT s;
+INSERT INTO spill_test SELECT repeat('x', 1000) FROM generate_series(1, 5000) g(i);
+ROLLBACK;
+INSERT INTO spill_test VALUES ('after-abort');
+SELECT chr(get_byte(data, 0)) AS msgtype, count(*)
+FROM pg_logical_slot_peek_binary_changes('regression_slot_pgoutput', NULL, NULL,
+     'proto_version', '1', 'publication_names', 'spill_pub')
+GROUP BY 1 ORDER BY 1;
+SELECT pg_drop_replication_slot('regression_slot_pgoutput');
+DROP PUBLICATION spill_pub;
+
 DROP TABLE spill_test;
 
 SELECT pg_drop_replication_slot('regression_slot');
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 6aed6346366..6567dd52aed 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -2132,26 +2132,36 @@ ReorderBufferSaveTXNSnapshot(ReorderBuffer *rb, ReorderBufferTXN *txn,
 }
 
 /*
- * Mark the given transaction as streamed if it's a top-level transaction
- * or has changes.
+ * Mark the given transaction as streamed, if appropriate.
+ *
+ * A top-level transaction is always marked.  A subtransaction is marked
+ * only when it has changes and its top-level transaction is already
+ * marked as streamed.
  */
 static void
 ReorderBufferMaybeMarkTXNStreamed(ReorderBuffer *rb, ReorderBufferTXN *txn)
 {
 	/*
-	 * The top-level transaction, is marked as streamed always, even if it
-	 * does not contain any changes (that is, when all the changes are in
-	 * subtransactions).
-	 *
-	 * For subtransactions, we only mark them as streamed when there are
-	 * changes in them.
-	 *
-	 * We do it this way because of aborts - we don't want to send aborts for
-	 * XIDs the downstream is not aware of. And of course, it always knows
-	 * about the top-level xact (we send the XID in all messages), but we
-	 * never stream XIDs of empty subxacts.
+	 * The top-level transaction is marked as streamed always, even if it does
+	 * not contain any changes (that is, when all the changes are in
+	 * subtransactions).  The downstream always knows about it, since we send
+	 * its XID in every message.
+	 */
+	if (rbtxn_is_toptxn(txn))
+	{
+		/* We only reach here when streaming is supported. */
+		Assert(ReorderBufferCanStream(rb));
+		txn->txn_flags |= RBTXN_IS_STREAMED;
+		return;
+	}
+
+	/*
+	 * A subtransaction is marked only when it has changes, and only when its
+	 * top-level transaction has already been marked as streamed.  We never
+	 * stream XIDs of empty subxacts, and we must not send an abort for an XID
+	 * the downstream has never heard of.
 	 */
-	if (rbtxn_is_toptxn(txn) || (txn->nentries_mem != 0))
+	if (txn->nentries_mem != 0 && rbtxn_is_streamed(rbtxn_get_toptxn(txn)))
 		txn->txn_flags |= RBTXN_IS_STREAMED;
 }
 
-- 
2.53.0



^ permalink  raw  reply  [nested|flat] 15+ messages in thread

* Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming
  2026-08-11 22:58 BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming PG Bug reporting form <noreply@postgresql.org>
  2026-08-13 17:56 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-13 23:03   ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 03:03     ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 04:56       ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 06:10         ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 06:44           ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 12:33             ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Fujii Masao <masao.fujii@gmail.com>
  2026-08-14 14:34               ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
@ 2026-08-14 22:15                 ` Masahiko Sawada <sawada.mshk@gmail.com>
  2026-08-15 02:27                   ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  0 siblings, 1 reply; 15+ messages in thread

From: Masahiko Sawada @ 2026-08-14 22:15 UTC (permalink / raw)
  To: Andrey Rachitskiy <pl0h0yp1@gmail.com>; +Cc: Fujii Masao <masao.fujii@gmail.com>; Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>; tyler@smarts.io <tyler@smarts.io>; pgsql-bugs@lists.postgresql.org <pgsql-bugs@lists.postgresql.org>

On Fri, Aug 14, 2026 at 7:35 AM Andrey Rachitskiy <pl0h0yp1@gmail.com> wrote:
>
>
> пт, 14 авг. 2026 г. в 17:33, Fujii Masao <masao.fujii@gmail.com>:
>>
>> One minor comment: ReorderBufferMaybeMarkTXNStreamed() seems to rely on
>> the assumption that it is never called for a top-level transaction when
>> streaming is disabled. If that's correct, how about documenting this
>> assumption in the function comment and/or adding an assertion such as:
>>
>>       if (rbtxn_is_toptxn(txn))
>>       {
>>           Assert(ReorderBufferCanStream(rb));
>>           txn->txn_flags |= RBTXN_IS_STREAMED;
>>
> Dear Fujii-san,
>
> Thanks for the review.
> v4 with assert and comment, in attachment.

Thank you for creating the patch.

IIUC it's not only a protocol compatibility issue but also an issue
that streaming messages are sent even with the streaming mode being
disabled. Therefore, it can be hit even with the latest protocol
version if subscribers disable the streaming option. While the fix
looks good to me, I have a few review comments on the regression test
part:

+-- bug #19616
+-- pgoutput protocol compatibility could be broken for an aborted xact
+-- discarded at spill eviction while a subxact remained in memory.
+-- Stream Abort ('A'), valid only since protocol version 2, could be seen
+-- with protocol version 1.
...
+SELECT chr(get_byte(data, 0)) AS msgtype, count(*)
+FROM pg_logical_slot_peek_binary_changes('regression_slot_pgoutput',
NULL, NULL,
+     'proto_version', '1', 'publication_names', 'spill_pub')
+GROUP BY 1 ORDER BY 1;

I'm not sure we should test the case against proto_version=1 for the
reason I mentioned above. I think we can use proto_version=4 and
streaming=0 instead. Also, the comment needs to be adjusted.

stream.sql would be more suitable to put this test.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com






^ permalink  raw  reply  [nested|flat] 15+ messages in thread

* Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming
  2026-08-11 22:58 BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming PG Bug reporting form <noreply@postgresql.org>
  2026-08-13 17:56 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-13 23:03   ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 03:03     ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 04:56       ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 06:10         ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 06:44           ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 12:33             ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Fujii Masao <masao.fujii@gmail.com>
  2026-08-14 14:34               ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 22:15                 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Masahiko Sawada <sawada.mshk@gmail.com>
@ 2026-08-15 02:27                   ` Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-17 20:46                     ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Masahiko Sawada <sawada.mshk@gmail.com>
  0 siblings, 1 reply; 15+ messages in thread

From: Andrey Rachitskiy @ 2026-08-15 02:27 UTC (permalink / raw)
  To: Masahiko Sawada <sawada.mshk@gmail.com>; +Cc: Fujii Masao <masao.fujii@gmail.com>; Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>; tyler@smarts.io <tyler@smarts.io>; pgsql-bugs@lists.postgresql.org <pgsql-bugs@lists.postgresql.org>

сб, 15 авг. 2026 г. в 03:15, Masahiko Sawada <sawada.mshk@gmail.com>:

> While the fix
> looks good to me, I have a few review comments on the regression test
> part:
>
> +-- bug #19616
> +-- pgoutput protocol compatibility could be broken for an aborted xact
> +-- discarded at spill eviction while a subxact remained in memory.
> +-- Stream Abort ('A'), valid only since protocol version 2, could be seen
> +-- with protocol version 1.
> ...
> +SELECT chr(get_byte(data, 0)) AS msgtype, count(*)
> +FROM pg_logical_slot_peek_binary_changes('regression_slot_pgoutput',
> NULL, NULL,
> +     'proto_version', '1', 'publication_names', 'spill_pub')
> +GROUP BY 1 ORDER BY 1;
>
> I'm not sure we should test the case against proto_version=1 for the
> reason I mentioned above. I think we can use proto_version=4 and
> streaming=0 instead. Also, the comment needs to be adjusted.
>
> stream.sql would be more suitable to put this test.
>
> Dear Sawada-san,

You are right, and these remarks have been addressed in v5 of the patch.

The spurious Stream Abort is not really about the protocol version.
So the real invariant is that a client which did not enable streaming
must not receive streaming messages. proto_version=1 only tested a
special case.  proto_version=4 with streaming=0 exercises the
invariant directly. I confirmed the reformulated test still catches the bug.
I moved the test to stream.sql as you suggested, keeping the structure of
the already-reviewed comment.

-- 
Regards,
Rachitskiy Andrey

Attachments:

  [text/x-patch] v5-0001-Don-t-mark-discarded-aborted-subxacts-as-streamed.patch (6.6K, ../../CAB8bMiuU56wc+3Pt2hmLLPCM1+EFR93cJqFH-yELo08TuRcXRA@mail.gmail.com/3-v5-0001-Don-t-mark-discarded-aborted-subxacts-as-streamed.patch)
  download | inline diff:
From ebda7d2b12ce4860ffc1e0d6e091fa14e824c0c3 Mon Sep 17 00:00:00 2001
From: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Date: Fri, 14 Aug 2026 09:27:00 +0500
Subject: [PATCH v5] Don't mark discarded aborted subxacts as streamed.

ReorderBufferTruncateTXN is also used when discarding already-aborted
transactions at eviction.  Marking every subxact with in-memory changes
as streamed in that path made a later abort emit stream_abort to clients
that never enabled streaming.

Mark a subxact as streamed only when it has changes and its top-level
transaction is already marked as streamed.  The streaming call sites mark
the top-level xact first, so this keeps their behavior.  The abort-discard
path never marks the top-level xact, so its subxacts are left unmarked.

BUG #19616
Reported-by: Tyler Smart <tyler@smarts.io>
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Reviewed-by: Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Discussion: https://www.postgresql.org/message-id/19616-f6153af509910853%40postgresql.org
---
 contrib/test_decoding/expected/stream.out     | 40 +++++++++++++++++++
 contrib/test_decoding/sql/stream.sql          | 23 +++++++++++
 .../replication/logical/reorderbuffer.c       | 38 +++++++++++-------
 3 files changed, 87 insertions(+), 14 deletions(-)

diff --git a/contrib/test_decoding/expected/stream.out b/contrib/test_decoding/expected/stream.out
index 9879e02ca84..7a92019933e 100644
--- a/contrib/test_decoding/expected/stream.out
+++ b/contrib/test_decoding/expected/stream.out
@@ -141,3 +141,43 @@ SELECT pg_drop_replication_slot('regression_slot');
  
 (1 row)
 
+-- bug #19616
+-- pgoutput protocol compatibility could be broken for an aborted xact
+-- discarded at eviction while a subxact remained in memory.
+-- Stream Abort ('A'), a streaming-only message, could then reach a client
+-- that did not enable streaming.
+CREATE TABLE stream_abort_test(data text);
+CREATE PUBLICATION stream_pub FOR TABLE stream_abort_test;
+SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_pgoutput', 'pgoutput');
+ ?column? 
+----------
+ init
+(1 row)
+
+BEGIN;
+SAVEPOINT s;
+INSERT INTO stream_abort_test VALUES ('subtransaction-change');
+RELEASE SAVEPOINT s;
+INSERT INTO stream_abort_test SELECT repeat('x', 1000) FROM generate_series(1, 5000) g(i);
+ROLLBACK;
+INSERT INTO stream_abort_test VALUES ('after-abort');
+SELECT chr(get_byte(data, 0)) AS msgtype, count(*)
+FROM pg_logical_slot_peek_binary_changes('regression_slot_pgoutput', NULL, NULL,
+     'proto_version', '4', 'streaming', 'off', 'publication_names', 'stream_pub')
+GROUP BY 1 ORDER BY 1;
+ msgtype | count 
+---------+-------
+ B       |     1
+ C       |     1
+ I       |     1
+ R       |     1
+(4 rows)
+
+SELECT pg_drop_replication_slot('regression_slot_pgoutput');
+ pg_drop_replication_slot 
+--------------------------
+ 
+(1 row)
+
+DROP PUBLICATION stream_pub;
+DROP TABLE stream_abort_test;
diff --git a/contrib/test_decoding/sql/stream.sql b/contrib/test_decoding/sql/stream.sql
index f1269403e0a..ff40910d313 100644
--- a/contrib/test_decoding/sql/stream.sql
+++ b/contrib/test_decoding/sql/stream.sql
@@ -67,3 +67,26 @@ RESET debug_logical_replication_streaming;
 
 DROP TABLE stream_test;
 SELECT pg_drop_replication_slot('regression_slot');
+
+-- bug #19616
+-- pgoutput protocol compatibility could be broken for an aborted xact
+-- discarded at eviction while a subxact remained in memory.
+-- Stream Abort ('A'), a streaming-only message, could then reach a client
+-- that did not enable streaming.
+CREATE TABLE stream_abort_test(data text);
+CREATE PUBLICATION stream_pub FOR TABLE stream_abort_test;
+SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_pgoutput', 'pgoutput');
+BEGIN;
+SAVEPOINT s;
+INSERT INTO stream_abort_test VALUES ('subtransaction-change');
+RELEASE SAVEPOINT s;
+INSERT INTO stream_abort_test SELECT repeat('x', 1000) FROM generate_series(1, 5000) g(i);
+ROLLBACK;
+INSERT INTO stream_abort_test VALUES ('after-abort');
+SELECT chr(get_byte(data, 0)) AS msgtype, count(*)
+FROM pg_logical_slot_peek_binary_changes('regression_slot_pgoutput', NULL, NULL,
+     'proto_version', '4', 'streaming', 'off', 'publication_names', 'stream_pub')
+GROUP BY 1 ORDER BY 1;
+SELECT pg_drop_replication_slot('regression_slot_pgoutput');
+DROP PUBLICATION stream_pub;
+DROP TABLE stream_abort_test;
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 6aed6346366..6567dd52aed 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -2132,26 +2132,36 @@ ReorderBufferSaveTXNSnapshot(ReorderBuffer *rb, ReorderBufferTXN *txn,
 }
 
 /*
- * Mark the given transaction as streamed if it's a top-level transaction
- * or has changes.
+ * Mark the given transaction as streamed, if appropriate.
+ *
+ * A top-level transaction is always marked.  A subtransaction is marked
+ * only when it has changes and its top-level transaction is already
+ * marked as streamed.
  */
 static void
 ReorderBufferMaybeMarkTXNStreamed(ReorderBuffer *rb, ReorderBufferTXN *txn)
 {
 	/*
-	 * The top-level transaction, is marked as streamed always, even if it
-	 * does not contain any changes (that is, when all the changes are in
-	 * subtransactions).
-	 *
-	 * For subtransactions, we only mark them as streamed when there are
-	 * changes in them.
-	 *
-	 * We do it this way because of aborts - we don't want to send aborts for
-	 * XIDs the downstream is not aware of. And of course, it always knows
-	 * about the top-level xact (we send the XID in all messages), but we
-	 * never stream XIDs of empty subxacts.
+	 * The top-level transaction is marked as streamed always, even if it does
+	 * not contain any changes (that is, when all the changes are in
+	 * subtransactions).  The downstream always knows about it, since we send
+	 * its XID in every message.
+	 */
+	if (rbtxn_is_toptxn(txn))
+	{
+		/* We only reach here when streaming is supported. */
+		Assert(ReorderBufferCanStream(rb));
+		txn->txn_flags |= RBTXN_IS_STREAMED;
+		return;
+	}
+
+	/*
+	 * A subtransaction is marked only when it has changes, and only when its
+	 * top-level transaction has already been marked as streamed.  We never
+	 * stream XIDs of empty subxacts, and we must not send an abort for an XID
+	 * the downstream has never heard of.
 	 */
-	if (rbtxn_is_toptxn(txn) || (txn->nentries_mem != 0))
+	if (txn->nentries_mem != 0 && rbtxn_is_streamed(rbtxn_get_toptxn(txn)))
 		txn->txn_flags |= RBTXN_IS_STREAMED;
 }
 
-- 
2.53.0



^ permalink  raw  reply  [nested|flat] 15+ messages in thread

* Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming
  2026-08-11 22:58 BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming PG Bug reporting form <noreply@postgresql.org>
  2026-08-13 17:56 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-13 23:03   ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 03:03     ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 04:56       ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 06:10         ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 06:44           ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 12:33             ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Fujii Masao <masao.fujii@gmail.com>
  2026-08-14 14:34               ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 22:15                 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Masahiko Sawada <sawada.mshk@gmail.com>
  2026-08-15 02:27                   ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
@ 2026-08-17 20:46                     ` Masahiko Sawada <sawada.mshk@gmail.com>
  2026-08-18 08:29                       ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  0 siblings, 1 reply; 15+ messages in thread

From: Masahiko Sawada @ 2026-08-17 20:46 UTC (permalink / raw)
  To: Andrey Rachitskiy <pl0h0yp1@gmail.com>; +Cc: Fujii Masao <masao.fujii@gmail.com>; Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>; tyler@smarts.io <tyler@smarts.io>; pgsql-bugs@lists.postgresql.org <pgsql-bugs@lists.postgresql.org>

On Fri, Aug 14, 2026 at 7:27 PM Andrey Rachitskiy <pl0h0yp1@gmail.com> wrote:
>
>
>
> сб, 15 авг. 2026 г. в 03:15, Masahiko Sawada <sawada.mshk@gmail.com>:
>>
>> While the fix
>> looks good to me, I have a few review comments on the regression test
>> part:
>>
>> +-- bug #19616
>> +-- pgoutput protocol compatibility could be broken for an aborted xact
>> +-- discarded at spill eviction while a subxact remained in memory.
>> +-- Stream Abort ('A'), valid only since protocol version 2, could be seen
>> +-- with protocol version 1.
>> ...
>> +SELECT chr(get_byte(data, 0)) AS msgtype, count(*)
>> +FROM pg_logical_slot_peek_binary_changes('regression_slot_pgoutput',
>> NULL, NULL,
>> +     'proto_version', '1', 'publication_names', 'spill_pub')
>> +GROUP BY 1 ORDER BY 1;
>>
>> I'm not sure we should test the case against proto_version=1 for the
>> reason I mentioned above. I think we can use proto_version=4 and
>> streaming=0 instead. Also, the comment needs to be adjusted.
>>
>> stream.sql would be more suitable to put this test.
>>
> Dear Sawada-san,
>
> You are right, and these remarks have been addressed in v5 of the patch.
>
> The spurious Stream Abort is not really about the protocol version.
> So the real invariant is that a client which did not enable streaming
> must not receive streaming messages. proto_version=1 only tested a
> special case.  proto_version=4 with streaming=0 exercises the
> invariant directly. I confirmed the reformulated test still catches the bug.
> I moved the test to stream.sql as you suggested, keeping the structure of
> the already-reviewed comment.

Thank you for updating the patch!

We can verify that stream_abort callback is not called when the
streaming mode is disabled, and we can use test_decoding for the
regression test at the end of the day. Which is simpler. I've updated
the regression test part accordingly and the commit message. Please
review it.

Regards,

-- 
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com

Attachments:

  [text/x-patch] v6-0001-Don-t-mark-discarded-aborted-subxacts-as-streamed.patch (7.4K, ../../CAD21AoBveDnemY2a2gGoAzXO7qXpXpXv0FNExnbNZEgDNO5r+g@mail.gmail.com/2-v6-0001-Don-t-mark-discarded-aborted-subxacts-as-streamed.patch)
  download | inline diff:
From 2df8b326ea3a2739270dbba7a804907790f2cd82 Mon Sep 17 00:00:00 2001
From: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Date: Fri, 14 Aug 2026 09:27:00 +0500
Subject: [PATCH v6] Don't mark discarded aborted subxacts as streamed.

Commit 072ee847ad4 taught logical decoding to discard the changes of a
transaction that is already known to be aborted when it is picked for
eviction. That path reuses ReorderBufferTruncateTXN(), which marks
every subtransaction that still has in-memory changes as
streamed. Since nothing is streamed in that path, and the top-level
transaction is never marked, a subtransaction ends up flagged as
streamed even though the output plugin has never seen it. Decoding the
subsequent abort record then makes ReorderBufferAbort() invoke the
stream_abort callback for that subtransaction.

For pgoutput this sends a Stream Abort ('A') message to a subscriber
that requested streaming = off, and it does so regardless of the
negotiated protocol version, so even a client speaking a version that
predates transaction streaming receives a message it cannot
parse. test_decoding dereferences a NULL pointer and crashes, since it
allocates its per-transaction state in the begin or stream start
callback, neither of which runs for a transaction discarded as
aborted.

This commit fixes this by marking a subtransaction as streamed only
when it has changes and its top-level transaction is already marked as
streamed. All streaming call sites mark the top-level transaction
before truncating it, so their behavior is unchanged, while the
abort-discard path never marks the top-level transaction and therefore
now leaves its subtransactions unmarked.

Backpatch to v18, where commit 072ee847ad4 was introduced.

Bug: #19616
Reported-by: Tyler Smart <tyler@smarts.io>
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Discussion: https://www.postgresql.org/message-id/19616-f6153af509910853%40postgresql.org
Backpatch-through: 18
---
 contrib/test_decoding/expected/stream.out     | 23 +++++++++++
 contrib/test_decoding/sql/stream.sql          | 17 +++++++++
 .../replication/logical/reorderbuffer.c       | 38 ++++++++++++-------
 3 files changed, 64 insertions(+), 14 deletions(-)

diff --git a/contrib/test_decoding/expected/stream.out b/contrib/test_decoding/expected/stream.out
index 9879e02ca84..0ec5c933610 100644
--- a/contrib/test_decoding/expected/stream.out
+++ b/contrib/test_decoding/expected/stream.out
@@ -134,6 +134,29 @@ SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL,
 (1 row)
 
 RESET debug_logical_replication_streaming;
+-- bug #19616
+--
+-- An aborted top-level transaction that is discarded at eviction must not
+-- leave its subtransactions marked as streamed.  Otherwise, decoding its
+-- abort record invokes the stream abort callback for a subtransaction the
+-- output plugin has never seen, even though streaming was never requested.
+-- The trailing committed transaction is required to flush the ROLLBACK
+-- record; without it decoding would stop before reaching the abort.
+BEGIN;
+SAVEPOINT s;
+INSERT INTO stream_test VALUES ('subxact-change');
+RELEASE SAVEPOINT s;
+INSERT INTO stream_test SELECT 'toplevel-change' || g.i FROM generate_series(1, 5000) g(i);
+ROLLBACK;
+INSERT INTO stream_test VALUES ('after-abort');
+SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+                                    data                                    
+----------------------------------------------------------------------------
+ BEGIN
+ table public.stream_test: INSERT: data[text]:'after-abort' i[integer]:null
+ COMMIT
+(3 rows)
+
 DROP TABLE stream_test;
 SELECT pg_drop_replication_slot('regression_slot');
  pg_drop_replication_slot 
diff --git a/contrib/test_decoding/sql/stream.sql b/contrib/test_decoding/sql/stream.sql
index f1269403e0a..5e45a8e9b64 100644
--- a/contrib/test_decoding/sql/stream.sql
+++ b/contrib/test_decoding/sql/stream.sql
@@ -65,5 +65,22 @@ COMMIT;
 SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
 RESET debug_logical_replication_streaming;
 
+-- bug #19616
+--
+-- An aborted top-level transaction that is discarded at eviction must not
+-- leave its subtransactions marked as streamed.  Otherwise, decoding its
+-- abort record invokes the stream abort callback for a subtransaction the
+-- output plugin has never seen, even though streaming was never requested.
+-- The trailing committed transaction is required to flush the ROLLBACK
+-- record; without it decoding would stop before reaching the abort.
+BEGIN;
+SAVEPOINT s;
+INSERT INTO stream_test VALUES ('subxact-change');
+RELEASE SAVEPOINT s;
+INSERT INTO stream_test SELECT 'toplevel-change' || g.i FROM generate_series(1, 5000) g(i);
+ROLLBACK;
+INSERT INTO stream_test VALUES ('after-abort');
+SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+
 DROP TABLE stream_test;
 SELECT pg_drop_replication_slot('regression_slot');
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 6aed6346366..6567dd52aed 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -2132,26 +2132,36 @@ ReorderBufferSaveTXNSnapshot(ReorderBuffer *rb, ReorderBufferTXN *txn,
 }
 
 /*
- * Mark the given transaction as streamed if it's a top-level transaction
- * or has changes.
+ * Mark the given transaction as streamed, if appropriate.
+ *
+ * A top-level transaction is always marked.  A subtransaction is marked
+ * only when it has changes and its top-level transaction is already
+ * marked as streamed.
  */
 static void
 ReorderBufferMaybeMarkTXNStreamed(ReorderBuffer *rb, ReorderBufferTXN *txn)
 {
 	/*
-	 * The top-level transaction, is marked as streamed always, even if it
-	 * does not contain any changes (that is, when all the changes are in
-	 * subtransactions).
-	 *
-	 * For subtransactions, we only mark them as streamed when there are
-	 * changes in them.
-	 *
-	 * We do it this way because of aborts - we don't want to send aborts for
-	 * XIDs the downstream is not aware of. And of course, it always knows
-	 * about the top-level xact (we send the XID in all messages), but we
-	 * never stream XIDs of empty subxacts.
+	 * The top-level transaction is marked as streamed always, even if it does
+	 * not contain any changes (that is, when all the changes are in
+	 * subtransactions).  The downstream always knows about it, since we send
+	 * its XID in every message.
+	 */
+	if (rbtxn_is_toptxn(txn))
+	{
+		/* We only reach here when streaming is supported. */
+		Assert(ReorderBufferCanStream(rb));
+		txn->txn_flags |= RBTXN_IS_STREAMED;
+		return;
+	}
+
+	/*
+	 * A subtransaction is marked only when it has changes, and only when its
+	 * top-level transaction has already been marked as streamed.  We never
+	 * stream XIDs of empty subxacts, and we must not send an abort for an XID
+	 * the downstream has never heard of.
 	 */
-	if (rbtxn_is_toptxn(txn) || (txn->nentries_mem != 0))
+	if (txn->nentries_mem != 0 && rbtxn_is_streamed(rbtxn_get_toptxn(txn)))
 		txn->txn_flags |= RBTXN_IS_STREAMED;
 }
 
-- 
2.55.0



^ permalink  raw  reply  [nested|flat] 15+ messages in thread

* Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming
  2026-08-11 22:58 BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming PG Bug reporting form <noreply@postgresql.org>
  2026-08-13 17:56 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-13 23:03   ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 03:03     ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 04:56       ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 06:10         ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 06:44           ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 12:33             ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Fujii Masao <masao.fujii@gmail.com>
  2026-08-14 14:34               ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 22:15                 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Masahiko Sawada <sawada.mshk@gmail.com>
  2026-08-15 02:27                   ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-17 20:46                     ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Masahiko Sawada <sawada.mshk@gmail.com>
@ 2026-08-18 08:29                       ` Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-18 19:00                         ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Masahiko Sawada <sawada.mshk@gmail.com>
  0 siblings, 1 reply; 15+ messages in thread

From: Andrey Rachitskiy @ 2026-08-18 08:29 UTC (permalink / raw)
  To: Masahiko Sawada <sawada.mshk@gmail.com>; +Cc: Fujii Masao <masao.fujii@gmail.com>; Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>; tyler@smarts.io <tyler@smarts.io>; pgsql-bugs@lists.postgresql.org <pgsql-bugs@lists.postgresql.org>

вт, 18 авг. 2026 г. в 01:46, Masahiko Sawada <sawada.mshk@gmail.com>:

> Please review it.
>
> Dear Sawada-san,

Thanks for the review. LGTM

-- 
Regards,
Rachitskiy Andrey

^ permalink  raw  reply  [nested|flat] 15+ messages in thread

* Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming
  2026-08-11 22:58 BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming PG Bug reporting form <noreply@postgresql.org>
  2026-08-13 17:56 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-13 23:03   ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 03:03     ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 04:56       ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 06:10         ` RE: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
  2026-08-14 06:44           ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 12:33             ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Fujii Masao <masao.fujii@gmail.com>
  2026-08-14 14:34               ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-14 22:15                 ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Masahiko Sawada <sawada.mshk@gmail.com>
  2026-08-15 02:27                   ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
  2026-08-17 20:46                     ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Masahiko Sawada <sawada.mshk@gmail.com>
  2026-08-18 08:29                       ` Re: BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming Andrey Rachitskiy <pl0h0yp1@gmail.com>
@ 2026-08-18 19:00                         ` Masahiko Sawada <sawada.mshk@gmail.com>
  0 siblings, 0 replies; 15+ messages in thread

From: Masahiko Sawada @ 2026-08-18 19:00 UTC (permalink / raw)
  To: Andrey Rachitskiy <pl0h0yp1@gmail.com>; +Cc: Fujii Masao <masao.fujii@gmail.com>; Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>; tyler@smarts.io <tyler@smarts.io>; pgsql-bugs@lists.postgresql.org <pgsql-bugs@lists.postgresql.org>

On Tue, Aug 18, 2026 at 1:29 AM Andrey Rachitskiy <pl0h0yp1@gmail.com> wrote:
>
>
>
> вт, 18 авг. 2026 г. в 01:46, Masahiko Sawada <sawada.mshk@gmail.com>:
>>
>> Please review it.
>>
> Dear Sawada-san,
>
> Thanks for the review. LGTM

Thank you for reviewing the patch. Pushed.

Regards,

-- 
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com






^ permalink  raw  reply  [nested|flat] 15+ messages in thread


end of thread, other threads:[~2026-08-18 19:00 UTC | newest]

Thread overview: 15+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-08-11 22:58 BUG #19616: pgoutput sends stream abort ('A') to clients that did not enable streaming PG Bug reporting form <noreply@postgresql.org>
2026-08-13 17:56 ` Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-08-13 23:03   ` Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-08-14 03:03     ` Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
2026-08-14 04:56       ` Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-08-14 06:10         ` Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
2026-08-14 06:44           ` Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-08-14 10:09             ` Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
2026-08-14 12:33             ` Fujii Masao <masao.fujii@gmail.com>
2026-08-14 14:34               ` Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-08-14 22:15                 ` Masahiko Sawada <sawada.mshk@gmail.com>
2026-08-15 02:27                   ` Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-08-17 20:46                     ` Masahiko Sawada <sawada.mshk@gmail.com>
2026-08-18 08:29                       ` Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-08-18 19:00                         ` Masahiko Sawada <sawada.mshk@gmail.com>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox