Subject: Re: pg_recvlogical: Prevent flushed data from being re-sent after restarting replication

From: Fujii Masao <[email-masked]>
Date: Sat, 10 Jan 2026 09:44:36 +0900

On Sat, Jan 10, 2026 at 12:53 AM Mircea Cadariu
<[email-masked]> wrote:
> Thanks for the updated patch set!
>
> I applied them and it builds correctly and the test passes on my laptop.

Thanks for the test! Barring any objections, I will commit the patches.

> About termination on Windows, I've noticed the following in file 006_logical_decoding.pl:
>
> # some Windows Perls at least don't like IPC::Run's start/kill_kill regime.
> skip "Test fails on Windows perl", 2 if $Config{osname} eq 'MSWin32';
>
> It seems that previously, in a similar situation the decision was to skip it on Windows.
>
> If we adopt the same approach for this test it should be fine, the production code in the patch is not Windows-specific.

Yes, I also noticed that Windows-specific handling while investigating
the test failure. Since IMO it's worth verifying that pg_recvlogical behaves
as expected on Windows, I'd prefer the approach taken in the v4 patch.

> Alternatively if we keep the approach in v4, we can consider updating 006_logical_decoding.pl as well so they're consistent.

That's possible. But TBH I'm not sure how much effort is justified here.
The test uses pg_recvlogical to activate the slot and doesn't really test
pg_recvlogical itself. It's unclear how valuable it is to additionally run
this test on Windows...

Regards,

-- 
Fujii Masao

---

From: "Dewei Dai" <[email-masked]>
Date: Sun, 11 Jan 2026 17:48:50 +0800 (CST)

Hi Fujii，

I applied the V4 patch and tested it on a CentOS 7 x86_64 platform. The test steps are as follows:

1. Create a table:

`create table test_id(id integer);`

2. Create a function to close the connection:

`create or replace function test_f(id integer) returns integer as $$

declare

 var1 integer;

begin

  SELECT active_pid into var1 FROM pg_replication_slots WHERE slot_name = 'reconnect_test';

  perform pg_terminate_backend(var1);

  return 1;

end; $$ language plpgsql;`

3. Execute the command to receive logs:

`./pg_recvlogical --create-slot --slot reconnect_test --dbname postgres --start --file decoding.out --fsync-interval 200 --status-interval 100 --verbose`

4. Execute the following shell script:

`while true

do

 ./psql -d postgres<<EOF

select test_f(1);

\q

EOF

done`

5. Execute data insertion using psql:

`insert into test_id values(1);

insert into test_id values(2);`

6. `tail -f decoding.out`

I found duplicate insert statements in the file.

I don't know if this is a problem.
Additionally, I tried moving the two lines involving `Stream LogicalLog` outside the loop
 in the `main` function, and then it worked correctly.
`output_written_lsn = InvalidXLogRecPtr;`
`output_fsync_lsn = InvalidXLogRecPtr;`

---

From: Fujii Masao <[email-masked]>
Date: Wed, 14 Jan 2026 10:14:28 +0900

> `output_fsync_lsn = InvalidXLogRecPtr;`

Thanks for the test and the investigation!

I was able to reproduce the issue as well. It occurs when the pg_recvlogical
connection is terminated before it has received any messages. The problematic
sequence is roughly:

1. The pg_recvlogical connection is terminated after running for some time.
2. StreamLogicalLog() is called again and initializes
output_written_lsn to InvalidXLogRecPtr.
3. pg_recvlogical reconnects and starts replication from valid startpos.
4. The connection is terminated again.
5. StreamLogicalLog() exits and OutputFsync() sets startpos to
output_written_lsn (i.e., InvalidXLogRecPtr).

As a result, the next StreamLogicalLog() starts replication with
startpos = InvalidXLogRecPtr, which can cause the server to resend
already-streamed data and lead to duplicate output.

The root cause is that StreamLogicalLog() reinitializes output_written_lsn and
output_fsync_lsn on every call. As you suggested, removing that initialization
fixes the issue.

I’ve updated the 0001 patch accordingly.

Attached is the updated version of the patches.

Regards,

-- 
Fujii Masao