Subject: Call EndCopyFrom() after initial table sync in logical replication

From: Shinya Kato <[email-masked]>
Date: Mon, 4 May 2026 16:58:12 +0900

Hi hackers,

While reading the logical replication initial table sync code, I
noticed that copy_table() calls BeginCopyFrom() and CopyFrom() but
never calls the matching EndCopyFrom().

EndCopyFrom() calls pgstat_progress_end_command(), which resets
st_progress_command to PROGRESS_COMMAND_INVALID. Without that call,
the backend status entry continues to report an active COPY operation
while the tablesync worker proceeds to WAL catchup. As a result,
pg_stat_progress_copy shows a stale entry for the entire WAL catchup
phase.

Attached patch adds EndCopyFrom(cstate) immediately after
CopyFrom(cstate) returns.

--
Best regards,
Shinya Kato
NTT OSS Center

---

From: Fujii Masao <[email-masked]>
Date: Fri, 8 May 2026 00:04:27 +0900

> CopyFrom(cstate) returns.

Thanks for the patch! It looks good to me.
Barring any objections, I will commit the patch.

Regards,

-- 
Fujii Masao

---

From: "cca5507" <[email-masked]>
Date: Fri, 8 May 2026 10:34:56 +0800

Hi,

Maybe we want to add "free_parsestate(pstate);" after the "EndCopyFrom()" as well?

--
Regards,
ChangAo Chen

---

From: Chao Li <[email-masked]>
Date: Fri, 8 May 2026 11:32:50 +0800

> On May 8, 2026, at 10:34, cca5507 <[email-masked]> wrote:
> 
> Hi,
> 

I agree. While here, looks like attnamelist can also be freed.

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/

---

From: Fujii Masao <[email-masked]>
Date: Fri, 8 May 2026 13:21:31 +0900

> Maybe we want to add "free_parsestate(pstate);" after the "EndCopyFrom()" as well?

What actual issue could occur if free_parsestate() is not called there?

Since pstate->p_target_relation does not seem to be used afterward,
omitting free_parsestate() appears mostly harmless to me. Bascailly
calling free_parsestate() after make_parsestate() seems intuitive,
but from a quick grep I found several places that call make_parsestate()
without a corresponding free_parsestate().

Regards,

-- 
Fujii Masao

---

From: "cca5507" <[email-masked]>
Date: Fri, 8 May 2026 12:46:14 +0800

> without a corresponding free_parsestate().

Yeah, I agree that it's harmless. I just noticed the comment above make_parsestate():

    Caller should eventually release the ParseState via free_parsestate().

Not sure whether it's worth to fix all of these places.

--
Regards,
ChangAo Chen

---

From: Chao Li <[email-masked]>
Date: Fri, 8 May 2026 13:09:38 +0800

> On May 8, 2026, at 12:21, Fujii Masao <[email-masked]> wrote:
> 

I don’t think this is a serious leak. In this path, pstate and attnamelist are allocated in CurTransactionContext, and the transaction is committed immediately after copy_table() finishes, so that memory is reclaimed at transaction end. Explicitly freeing them would be mostly for code readability, not to fix a memory leak. So, I am okay to not free them.

While tracing the code, I noticed another issue that is probably more worth addressing. copy_table() currently does:
```
    copybuf = makeStringInfo();
```

But copybuf is only used by copy_read_data(), and there it's really just acting as a small state holder for data, len, and cursor, rather than as a normal growable StringInfo. That means we do not need to allocate a StringInfo object or its backing buffer at all.

It would be cleaner to use a plain StringInfoData and simply reinitialize or zero it in copy_table(). See the attached diff for the proposed change.

David Rowley has made several cleanup changes in this area to prefer stack-allocated StringInfoData, for example a63bbc811d41b3567eb37fe2636e660a852dbbf2. This change seems consistent with that direction.

Best regards,

---

From: Shinya Kato <[email-masked]>
Date: Sat, 9 May 2026 03:05:20 +0900

On Fri, May 8, 2026, 14:10 Chao Li <[email-masked]> wrote:

>
> I don’t think this is a serious leak. In this path, pstate and attnamelist
> are allocated in CurTransactionContext, and the transaction is committed
> immediately after copy_table() finishes, so that memory is reclaimed at
> transaction end. Explicitly freeing them would be mostly for code
> readability, not to fix a memory leak. So, I am okay to not free them.
>

I agree that no additional memory cleanup is needed here.

> While tracing the code, I noticed another issue that is probably more
> worth addressing. copy_table() currently does:
> ```
> But copybuf is only used by copy_read_data(), and there it's really just
> acting as a small state holder for data, len, and cursor, rather than as a
> normal growable StringInfo. That means we do not need to allocate a
> StringInfo object or its backing buffer at all.
>
> It would be cleaner to use a plain StringInfoData and simply reinitialize
> or zero it in copy_table(). See the attached diff for the proposed change.
>
> David Rowley has made several cleanup changes in this area to prefer
> stack-allocated StringInfoData, for example
> a63bbc811d41b3567eb37fe2636e660a852dbbf2. This change seems consistent with
> that direction.
>

Thanks for the suggestion.

The copybuf change looks worthwhile, but perhaps it’s better discussed in a
separate thread.

--
Shinya Kato

---

From: Chao Li <[email-masked]>
Date: Sat, 9 May 2026 13:57:10 +0800

> On May 9, 2026, at 02:05, Shinya Kato <[email-masked]> wrote:
> 
> 
> 
> The copybuf change looks worthwhile, but perhaps it’s better discussed in a separate thread.
> 

Sound fair. Let me post it to a separate thread.

Best regards,

---

From: Fujii Masao <[email-masked]>
Date: Wed, 13 May 2026 11:49:25 +0900

> Barring any objections, I will commit the patch.

I've pushed the patch. Thanks!

Regards,

-- 
Fujii Masao

---

From: Shinya Kato <[email-masked]>
Date: Wed, 13 May 2026 14:04:04 +0900

> I've pushed the patch. Thanks!

Thanks for committing this, Fujii-san!

-- 
Best regards,
Shinya Kato
NTT OSS Center