Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vMFf3-005BLI-2i for pgsql-hackers@arkaria.postgresql.org; Fri, 21 Nov 2025 01:10:06 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vMFf1-005HpA-0b for pgsql-hackers@arkaria.postgresql.org; Fri, 21 Nov 2025 01:10:03 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vMFf0-005Hp1-2F for pgsql-hackers@lists.postgresql.org; Fri, 21 Nov 2025 01:10:03 +0000 Received: from mail-pl1-x62d.google.com ([2607:f8b0:4864:20::62d]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1vMFey-000cEl-0X for pgsql-hackers@lists.postgresql.org; Fri, 21 Nov 2025 01:10:02 +0000 Received: by mail-pl1-x62d.google.com with SMTP id d9443c01a7336-297ef378069so14009275ad.3 for ; Thu, 20 Nov 2025 17:10:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763687400; x=1764292200; darn=lists.postgresql.org; h=to:references:message-id:content-transfer-encoding:cc:date :in-reply-to:from:subject:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=xNuUzDEuvNLIJQunZp7TAyJwRefQKF/OenHNJe4XScE=; b=LlaC7SxzzI8RGAIaRtY2PhivPtzkH3sk1p+IoiTJTSFSkidteD7yn/tQBfBV2fn56S 2BJeiiIOXPtRPoY3gsBab1AdolXJe2AJZXb1RnB04c7RpBHP8UAxXMLfI/S8Ssu27uUm LUW0HZraQTyZHDJdu1vmODdvtkgeTPtCS05DRKPm/dAg+eB0scIFF4eUCzmDNC1WlI/f iNH8Nw6GT09tg9ZgI8J2yxO1MUUf2baDwFWqmohQm33yD8ffrk/ShIaknFfigpoIjVGS MxV1F7zo7lVsQzLjsFYdCM0miHcV5fxb0dkGmQRMDji3+QQpR9GV21GdwM0SYRR3arnl yWlA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763687400; x=1764292200; h=to:references:message-id:content-transfer-encoding:cc:date :in-reply-to:from:subject:mime-version:x-gm-gg:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=xNuUzDEuvNLIJQunZp7TAyJwRefQKF/OenHNJe4XScE=; b=w92HdnfPwR0ch1/9Q3wVOwOgyQuBIpXP5JohSbmOiHpklpXE6HpRfRfNfMqhwRpUFR iULH5KGuatojpY7Dbn4ygsm0H5hsS0pvxGxzTLojodK6VRfDoroNur4uD0HZDIXvEtdr 6axk174O+b7wx/ykAadSBnKIWF84emrLy9ie4/P4FEQNKR5qfPiMqbVyoqSGzE3vkQO4 h/LEfzXCte2JFAMeKg4pX8Bi+e0TwtNXMCMSG72NZqZinz8pTlvj91xZHNkxDe4dYpDK 1RHN1THXIxBzObevUJP5Gn4KWSLJbcI0ejSbN0TyU00YSpGgGFlXEf1uatuywi0kY88x KBQg== X-Forwarded-Encrypted: i=1; AJvYcCW4b+bFLMeB+SheZdtojbLgUmJuyuyO+DFqa08B6LkP+CeCE4agOH/+S76cgi9ordl6XXGPGqH+sXzlo48a@lists.postgresql.org X-Gm-Message-State: AOJu0YwQlm+UXeWglxbWqQitg4PgE0QCMh2Jl2SqEP2G/vwrkG6DjXPw kxnauzGGOGBWkrws+SVU6K05S0Y0fKhKZE15HYdK9oFr0CzKOYpaGyfE X-Gm-Gg: ASbGncvcTF1vW3e9M6caNElND9rMyxinqUJwpmJnZp6JNaxtsbe5rzTBSXZVb5m6R2F cxAn1ChKPZQRmFTZH/S3XS2NqHv5fiUdSEQ5PUCl1rf+9R16LGnwJpj8DdbEJeSyqiWy0eHpe4x 8oWcJBC18tcwnECp+Rrd/cv7GShNQOhvmH/4D6473Xfjf2T02Qg3wHzNeF8Ow0uclmWqdw//Y9e tUtZcgyWQ6Lwxc6BJQYlmz9CFiguEM+vx3juXO39TxnBoUnsyuxdYkqIREV6EzOJ/csqb5Af+lZ LuFQXIsJ/fVri8Bb4zj5AzCt65J7h5MLaZ8F0fhUAClloUiEtawmYy2miTZSsVi0RMuqRZ5A28V 9MdTjNTN7qqV4TuxRr8vNBf7iA51UlZZHeXdkN2s4mmfkNFIIrCjR0fUYAP59luWcU/puEGfcic rvZ+y6KiAxqcyrfOoQNw== X-Google-Smtp-Source: AGHT+IGUoVZ0N7Z7e+RrS2rjPppPBu6VyMbMXIpy5BfVJnUIxbtIBi91ruyausvgw1czOXX83gxzbA== X-Received: by 2002:a17:903:1746:b0:298:3a2f:2333 with SMTP id d9443c01a7336-29b6bf37ef0mr6524735ad.31.1763687399765; Thu, 20 Nov 2025 17:09:59 -0800 (PST) Received: from smtpclient.apple ([203.10.98.27]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-34726696b52sm3766432a91.1.2025.11.20.17.09.56 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 20 Nov 2025 17:09:58 -0800 (PST) Content-Type: text/plain; charset=utf-8 Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3826.700.81\)) Subject: Re: eliminate xl_heap_visible to reduce WAL (and eventually set VM on-access) From: Chao Li In-Reply-To: Date: Fri, 21 Nov 2025 09:09:21 +0800 Cc: Kirill Reshke , Andres Freund , Robert Haas , Andrey Borodin , PostgreSQL Hackers , Heikki Linnakangas Content-Transfer-Encoding: quoted-printable Message-Id: <0AC177F5-5E26-45EE-B273-357C51212AC5@gmail.com> References: <2wk7jo4m4qwh5sn33pfgerdjfujebbccsmmlownybddbh6nawl@mdyyqpqzxjek> To: Melanie Plageman X-Mailer: Apple Mail (2.3826.700.81) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk > On Nov 21, 2025, at 01:19, Melanie Plageman = wrote: >=20 > On Wed, Nov 19, 2025 at 6:13=E2=80=AFPM Melanie Plageman > wrote: >>=20 >> Since it is passed into one of the helpers, I think I agree. Attached >> v21 has this change. >=20 > I've committed the first three patches. Attached v22 is the remaining > patches which set the VM in heap_page_prune_and_freeze() for vacuum > and then allow on-access pruning to also set the VM. >=20 I just started reviewing 0001 yesterday and got a few comments. However, = it was late, I didn=E2=80=99t have enough time to wrap up, so I decided = to review a few more today and send the comments together. As you have = pushed 0001-0003, I=E2=80=99d still raise my comment for them now, and I = will review the rest of commits next week. 1 - pushed 0001 ``` /* * Report the number of tuples reclaimed to = pgstats. This is @@ -419,60 +425,44 @@ heap_page_will_freeze(Relation relation, Buffer = buffer, * also need to account for a reduction in the length of the line = pointer * array following array truncation by us. * - * If the HEAP_PRUNE_FREEZE option is set, we will also freeze tuples = if it's - * required in order to advance relfrozenxid / relminmxid, or if it's - * considered advantageous for overall system performance to do so now. = The - * 'cutoffs', 'presult', 'new_relfrozen_xid' and 'new_relmin_mxid' = arguments - * are required when freezing. When HEAP_PRUNE_FREEZE option is set, = we also - * set presult->all_visible and presult->all_frozen on exit, to = indicate if - * the VM bits can be set. They are always set to false when the - * HEAP_PRUNE_FREEZE option is not set, because at the moment only = callers - * that also freeze need that information. - * - * vistest is used to distinguish whether tuples are DEAD or = RECENTLY_DEAD - * (see heap_prune_satisfies_vacuum). - * - * options: - * MARK_UNUSED_NOW indicates that dead items can be set LP_UNUSED = during - * pruning. + * params contains the input parameters used to control freezing and = pruning + * behavior. See the definition of PruneFreezeParams for more on what = each + * parameter does. * - * FREEZE indicates that we will also freeze tuples, and will return - * 'all_visible', 'all_frozen' flags to the caller. - * - * cutoffs contains the freeze cutoffs, established by VACUUM at the = beginning - * of vacuuming the relation. Required if HEAP_PRUNE_FREEZE option is = set. - * cutoffs->OldestXmin is also used to determine if dead tuples are - * HEAPTUPLE_RECENTLY_DEAD or HEAPTUPLE_DEAD. + * If the HEAP_PAGE_PRUNE_FREEZE option is set in params, we will = freeze + * tuples if it's required in order to advance relfrozenxid / = relminmxid, or + * if it's considered advantageous for overall system performance to do = so + * now. The 'params.cutoffs', 'presult', 'new_relfrozen_xid' and + * 'new_relmin_mxid' arguments are required when freezing. When + * HEAP_PAGE_PRUNE_FREEZE option is passed, we also set = presult->all_visible + * and presult->all_frozen on exit, to indicate if the VM bits can be = set. + * They are always set to false when the HEAP_PAGE_PRUNE_FREEZE option = is not + * passed, because at the moment only callers that also freeze need = that + * information. * * presult contains output parameters needed by callers, such as the = number of * tuples removed and the offsets of dead items on the page after = pruning. * heap_page_prune_and_freeze() is responsible for initializing it. = Required * by all callers. * - * reason indicates why the pruning is performed. It is included in = the WAL - * record for debugging and analysis purposes, but otherwise has no = effect. - * * off_loc is the offset location required by the caller to use in = error * callback. * * new_relfrozen_xid and new_relmin_mxid must provided by the caller if = the - * HEAP_PRUNE_FREEZE option is set. On entry, they contain the oldest = XID and - * multi-XID seen on the relation so far. They will be updated with = oldest - * values present on the page after pruning. After processing the = whole - * relation, VACUUM can use these values as the new = relfrozenxid/relminmxid - * for the relation. + * HEAP_PAGE_PRUNE_FREEZE option is set in params. On entry, they = contain the + * oldest XID and multi-XID seen on the relation so far. They will be = updated + * with oldest values present on the page after pruning. After = processing the + * whole relation, VACUUM can use these values as the new + * relfrozenxid/relminmxid for the relation. */ void -heap_page_prune_and_freeze(Relation relation, Buffer buffer, - GlobalVisState = *vistest, - int options, - struct VacuumCutoffs = *cutoffs, +heap_page_prune_and_freeze(PruneFreezeParams *params, PruneFreezeResult = *presult, - PruneReason reason, OffsetNumber = *off_loc, TransactionId = *new_relfrozen_xid, MultiXactId = *new_relmin_mxid) { ``` For this function interface change, I got a concern. The old function = comment says "cutoffs contains the freeze cutoffs =E2=80=A6. Required if = HEAP_PRUNE_FREEZE option is set.=E2=80=9D, meaning that cutoffs is only = useful and must be set when HEAP_PRUNE_FREEZE is set. But the new = comment seems to have lost this indication. And in the old function interface, cutoffs sat right next to options, = readers are easy to notice: * when options is 0, cutoffs is null ``` heap_page_prune_and_freeze(relation, buffer, = vistest, 0, = NULL, &presult, PRUNE_ON_ACCESS, &dummy_off_loc, NULL, NULL); ``` * when options has HEAP_PAGE_PRUNE_FREEZE, cutoffs is passed in ``` prune_options =3D HEAP_PAGE_PRUNE_FREEZE; if (vacrel->nindexes =3D=3D 0) prune_options |=3D HEAP_PAGE_PRUNE_MARK_UNUSED_NOW; heap_page_prune_and_freeze(rel, buf, vacrel->vistest, = prune_options, = &vacrel->cutoffs, &presult, PRUNE_VACUUM_SCAN, = &vacrel->offnum, = &vacrel->NewRelfrozenXid, &vacrel->NewRelminMxid); ``` So, the change doesn=E2=80=99t break anything, but makes code a little = bit harder to read. So, my suggestion is to add an assert in = heap_page_prune_and_freeze, something like: ``` Assert(!(params->options & HEAP_PAGE_PRUNE_FREEZE) || params->cutoffs !=3D= NULL); ``` 2 - pushed 0001 ``` + PruneFreezeParams params =3D {.relation =3D rel,.buffer =3D buf, + .reason =3D PRUNE_VACUUM_SCAN,.options =3D = HEAP_PAGE_PRUNE_FREEZE, + .cutoffs =3D &vacrel->cutoffs,.vistest =3D = vacrel->vistest + }; ``` Using a designated initializer is not wrong, but makes future = maintenance harder, because when a new field is added, this initializer = will leave the new field uninitiated. =46rom my impression, I don=E2=80=99= t remember I ever see a designated initializer in PG code. I only = remember 3 ways I have seen: * use an initialize function to set every fields individually * palloc0 to set all 0, then set non-zero fields individually * {0} to set all 0, then set non-zero fields individually 3 - pushed 0002 ``` prstate->all_visible =3D false; + prstate->all_frozen =3D false; ``` Nit: Now setting the both fields to false repeat in 6 places. Maybe add = a static inline function, say PruneClearVisibilityFlags(), may improve = maintainability. 4 - pushed 0003 ``` + * opporunistically freeze, to indicate if the VM bits can be set. = They are ``` Typo: opporunistically, missed a =E2=80=9Ct=E2=80=9D. I=E2=80=99d stop here today, and continue reviewing rest commits in next = week. Best regards, -- Chao Li (Evan) HighGo Software Co., Ltd. https://www.highgo.com/