| From: | Jingtang Zhang <mrdrivingduck(at)gmail(dot)com> |
|---|---|
| To: | Yuhang Qiu <iamqyh(at)gmail(dot)com> |
| Cc: | Peter Geoghegan <pg(at)bowt(dot)ie>, Melanie Plageman <melanieplageman(at)gmail(dot)com>, PostgreSQL-development <pgsql-hackers(at)postgresql(dot)org>, pgsql-hackers(at)lists(dot)postgresql(dot)org |
| Subject: | Re: Allow aggressive VACUUM to freeze without a cleanup lock |
| Date: | 2026-08-26 17:27:20 |
| Message-ID: | CAPsk3_DoovwweFBxAHwBsgOF0+1CygHU7RhJLksTuY2qbPPYuQ@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi all,
Thanks for the reply.
> Agreed. Freezing itself only needs an exclusive lock, a cleanup lock is needed
> for page defragmentation. I also agree with Melanie that the patch should
> explain this if it is not already documented elsewhere.
Yeah. Did some research from existing information.
A cleanup lock is for reorganizing a page. Pruning can remove or
change line pointers, and defragmentation can move tuple data. An
exclusive content lock does not wait for other buffer pins, so another
backend might still hold a reference to either. That is why pruning
needs a cleanup lock [1]. The WAL replay code makes the same split:
pruning needs a cleanup lock, while freezing and VACUUM's second heap
pass only need an ordinary exclusive lock [2].
[1] https://github.com/postgres/postgres/blob/master/src/backend/access/heap/README.HOT#L228-L231
[2] https://github.com/postgres/postgres/blob/master/src/include/access/heapam_xlog.h#L330-L335
> First, the buffer lock is released and reacquired between
> `lazy_scan_noprune()` and `lazy_scan_freeze()`. The page may be changed or
> already frozen during this window, so this assertion is not always valid:
>
> Second, the code may call heap_prepare_freeze_tuple() and then discard the
> freeze plans after finding an old DEAD tuple. This function may create a new
> MultiXactId, write WAL, and advance the MultiXact state. These side effects
> remain even if the plans are discarded.
Thanks for the review. I fixed both issues you found. The freeze-only
path no longer assumes anything about the page after it reacquires the
buffer lock. It also scans the whole page before it prepares freeze
plans, so a fallback to the cleanup-lock path cannot leave MultiXact or
WAL side effects behind.
> There is also a small naming issue. lazy_scan_noprune and lazy_scan_freeze are
> quite confusing now. I think lazy_scan_noprune is not correct at this moment.
The current names describe three different paths: lazy_scan_prune()
does pruning and freezing, lazy_scan_freeze() does freezing only, and
lazy_scan_noprune() does neither. The last name predates this patch,
but I agree that it is less clear now.
> The patch adds FreezeState, HeapPageFreezeParams, and
> HeapPageFreezeResult, but they overlap with PruneState,
> PruneFreezeParams, and PruneFreezeResult. These abstractions add complexity
> without much reuse.
I also agree that the parameter and result types should share a common
freeze component. PruneFreezeParams and PruneFreezeResult have extra
pruning and VM state, but their freeze-related fields overlap with the
freeze-only types. The pruning-specific state should sit around the
common part instead of having two parallel sets of structures.
v2 patches are attached.
---
Regards,
Jingtang
| Attachment | Content-Type | Size |
|---|---|---|
| v2-0002-vacuum-Allow-freezing-without-a-cleanup-lock.patch | application/octet-stream | 16.1 KB |
| v2-0001-vacuum-Separate-heap-page-freezing-state.patch | application/octet-stream | 15.9 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Bharath Rupireddy | 2026-08-26 18:07:00 | Re: scary patch contest |
| Previous Message | Tom Lane | 2026-08-26 17:25:12 | Re: remove_useless_joins vs. bug #19560 |