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 1vyWQd-000BMd-1o for pgsql-hackers@arkaria.postgresql.org; Fri, 06 Mar 2026 14:45:23 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vyWQb-005TOj-38 for pgsql-hackers@arkaria.postgresql.org; Fri, 06 Mar 2026 14:45:22 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vyWQb-005TOb-1x for pgsql-hackers@lists.postgresql.org; Fri, 06 Mar 2026 14:45:22 +0000 Received: from mail-ed1-x535.google.com ([2a00:1450:4864:20::535]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1vyWQZ-00000001CNm-3RKl for pgsql-hackers@postgresql.org; Fri, 06 Mar 2026 14:45:21 +0000 Received: by mail-ed1-x535.google.com with SMTP id 4fb4d7f45d1cf-660d2e48383so6350112a12.1 for ; Fri, 06 Mar 2026 06:45:19 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1772808318; cv=none; d=google.com; s=arc-20240605; b=RQEXnjDYpATa1bHINLjDOmFsl2wbm4OMT8Cl3/JZ9JNFsYJ20WNLqgjl9cxE02YsAF 196Cb0Y2cEh0qoYS6qXgn6bOhFzpxoRkMRspnkjQLGe45tgvqpEzHDS2hWkwLsfNKAOe C6kQ463CKuskA5cs4A8+ActipDgUXm7RBD6KS9CPFVzlvVMJFsYNRVeBNGls2zUTLzua ACPas8uA8R+2xznSood+D4976IYIcWyyABd54OznbrxfZwZ1/QjjaeAvVMNTiNqmF9y1 9WBnrFUyeujmLC51JyXFLbhqXzSHSqH5HjHDjxRJBKYFz+bs3wqsEgO7YYhgKSPaFJMT 7ZEw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=w4HlSip7P66OCFAHmqR0/Kq5JpV0v7WCJ75obumWSdA=; fh=PQnQyned+zQRniNstuAzQ1bvl5rypYoJZQ71n1RKYL0=; b=ft1KtW9YhlVNW/CL95y/7WCXRPWHu6rSHugaCLjYFqxYiAZRt2Kw6jzsaSVa1ZT/W3 7lavmW2Ly5J0ZUThzNB5QExwuunBSUTqc/UIdd0ydSsGZvvidNqW3ROqQYECDwrl73vS 9cMgKlVmAWAys+dXrWrjFeHjafXv7eYfeVfUJHDDyZLJDWCbEU4X510hj+ocRlvZRKHL TUsC/A0mabTLOrE2RnNObquJOdmuboXZrsYLAjO04voejRx3OXLpLR0xPE50AhP0IVxt L7JZD4TY1l6VUP3eFyTus5ENkOWHaf7xcraK4kYo2HNhrSek0iz4dyZXj9X52JqfIbI/ 3s5Q==; darn=postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772808318; x=1773413118; darn=postgresql.org; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=w4HlSip7P66OCFAHmqR0/Kq5JpV0v7WCJ75obumWSdA=; b=YvRe9jbobVYsyRlKQYRk0E7cSTEwotSQEdIfguIBvoBK+8s5t3P8CTQc4fU/mRD19c OKHhDCa1di38sXvUGzrmuxIBD8ZnMgleKd8QlHjgp9FC3NCkuuFuVImBu1i96qb15omW lotWuLmjAqGoBtfoXUmV4uwzfJqta1epuhpc2zljaN1ox2Js1xbQVjKs+o188dzb8AhL oQ9Ptl2byE445mh+9IqsM0c3pxWOuPrGY2KQyshZ2NXyg6eaDY3LmQKUaKtsRvdIL9sg ElOOVdnCRHD9ZWTm4KijMHgKfQQEg4PFmo0Ya9/rM8TXeu+Drocsl42ZO75O5M3Y3VXj JgKw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772808318; x=1773413118; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=w4HlSip7P66OCFAHmqR0/Kq5JpV0v7WCJ75obumWSdA=; b=dBzLWOa/bT6kSZCg2J/IuhrOtt2EaozDn+n+G+WiM35bWnltPSyQvxz2S5X58aDqAn Jg9BTm2luq+36jzKvFr9+NWh4W64vINpV1wEOg2jE6iCj0v69EtmrIu8pIkzdGWRDYR6 NXzJEZJjGS2kEGfwtI16M241pMbHJ7pImG+Bts/VQ2Mrp4F/f/xdH2LGiTx5a5QQbPXC f78CeSSi7FKRhU11fJFAQnv7qQ0/XWH16ymtMur4mB7cZu6BPHy4joRCFvO7htw3l5Xx dCUQ6YQKecNPfdP57sG9kYA1Z7Nfkq/S/Oh4GmvrCtdTHyYuZ0luDe1ST10qe6wy/dOd EVrg== X-Forwarded-Encrypted: i=1; AJvYcCXiNdRTKi+d9sxThaBxogCBF7J8x9X5YlmSjAP3JPYlhF7kBqh4you77pXHa3er2zObgOmvlFReGYvmtCPI@postgresql.org X-Gm-Message-State: AOJu0YyAUg5waDzCy411pxed92iNjUC2X5o7P6QGRi67dJzorldpJ5Ha MmYfnWegKfwY+Ke4mvMx79nLch/SlRgDdUZVSS8cBqjCF3CtVV4aHc/IgXXxicG1IkFkmwXfFaN cyCJ5Bnp24eGaGplE18Tj1mUfOVY+xt1R106t X-Gm-Gg: ATEYQzzs1C76tQSGWkhSRwWsibXtDQXjbcznmqWakVfySksAjcMmgOz/r+m42PCPaJe 16hqDwx4QOJPx8rTkyKCzKfLXVVL1mshlaKCQBRNCHq/dzk2pSQ+g6+BstTsn8dU+x+mRIU3kNv Ekk+t9/XqnszKe5kkaROvBybLKJuvpkKpdRRwJVX4qHmGOVxx8xV/t+Ee9IXDHyUDg3Am8e/e/6 yFPizVS/ZnT4S/6qSAAJc3XyN6Q9LhI9y7r3ldvwr/PvdhpkixYBVAX4nBCMwDaiTrJNHYTmpHM Oa8v8uSOcw== X-Received: by 2002:a05:6402:2683:b0:660:976f:31e with SMTP id 4fb4d7f45d1cf-6619d45a938mr1231033a12.7.1772808318251; Fri, 06 Mar 2026 06:45:18 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: From: Pavel Borisov Date: Fri, 6 Mar 2026 18:45:06 +0400 X-Gm-Features: AaiRm50aTzEs3uCnk-iNo8quhIdEJuG5EI64ezLRmjsEbo55ZBoFLky2B9rNnX0 Message-ID: Subject: Re: Odd code around ginScanToDelete To: Alexander Korotkov Cc: Andres Freund , pgsql-hackers@postgresql.org Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Hi, Andres and Alexander! On Sat, 21 Feb 2026 at 13:55, Alexander Korotkov wro= te: > > On Wed, Feb 4, 2026 at 12:32=E2=80=AFAM Alexander Korotkov wrote: > > > > On Tue, Feb 3, 2026 at 6:26=E2=80=AFPM Andres Freund wrote: > > > > > > While looking at converting more places to UnlockReleaseBuffer(), in = the > > > course of making UnlockReleaseBuffer() faster than the two separate > > > operations, I found this code: > > > > > > static bool > > > ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, > > > DataPageDeleteStack *parent, OffsetNu= mber myoff) > > > ... > > > > > > if (!meDelete) > > > { > > > if (BufferIsValid(me->leftBuffer)) > > > UnlockReleaseBuffer(me->leftBuffer); > > > me->leftBuffer =3D buffer; > > > } > > > else > > > { > > > if (!isRoot) > > > LockBuffer(buffer, GIN_UNLOCK); > > > > > > ReleaseBuffer(buffer); > > > } > > > > > > if (isRoot) > > > ReleaseBuffer(buffer); > > > > > > > > > Which sure looks like it'd release buffer twice if isRoot is set? I = guess > > > that's not reachable, because presumably the root page will always go= down the > > > !meDelete path. But it sure made me wonder if there's a hard to reach= bug. > > > > Yes, it's not possible to have meDelete set for root, because > > me->leftBuffer is always InvalidBuffer for the root. So the branch > > handling meDelete case should better do Assert(!isRoot). > > > This code was introduced in > > > commit e14641197a5 > > > Author: Alexander Korotkov > > > Date: 2019-11-19 23:07:36 +0300 > > > > > > Fix deadlock between ginDeletePage() and ginStepRight() > > > > > > I didn't trace it further to see if it existed before that in some fa= shion. > > > > Yes. I think generally this area needs to be reworked to become more > > clear, and have vast more comments. It was wrong from my side trying > > to fix bugs there without reworking it into something more > > appropriate. I'm planning to put work on this during this week. > > > > > There's another oddity here: ginScanToDelete() requires that the root= page has > > > been locked by the caller already, but will afaict re-read the root p= age? But > > > then have code to avoid locking it again, because that would not have= worked? > > > Seems odd. > > > > > > It seems a bit odd for me that caller already have locked buffer, but > > passes BlockNumber making us re-read the buffer. But I'm not sure > > that's the same as your point. Could you, please, elaborate more on > > this? > > Here is the refactoring patch. Sorry for the delay. Hi, Andres and Alexander! I've looked into the patch v1. Overall, it looks good to me. Some thoughts: Is it worth/possible in recursive calls of ginScanToDelete() to free allocated myStackItem->child after processing all children of the current level, when they are not needed anymore? Previously to this patch, palloc-ed "me" variable also was't freed at recursion levels. Could limiting the maximum recursion level be useful? In the comment to myStackItem before ginScanToDelete(), it might be worth adding that after processing all pages on the current level, myStackItem is not needed anymore. > > Yes, it's not possible to have meDelete set for root, because > > me->leftBuffer is always InvalidBuffer for the root. So the branch > > handling meDelete case should better do Assert(!isRoot). Looks like this additional Assert is not in patch v1. In the root call of ginScanToDelete(gvs, &root); we can add Assert checking that its return result is false: - ginScanToDelete(gvs, &root); + deleted =3D ginScanToDelete(gvs, &root); +. Assert(!deleted); /* Root page is never deleted */ Additionally, it could be good to rename all vacuum functions related to posting tree pages only, to include "Posting" (e.g., ginDeletePage -> ginDeletePostingPage). The same is for the functions only for the entry tree. It is already named this way in many places (e.g. ginVacuumPostingTreeLeaves). It could be good to extend this to all relevant functions. Several small proposals on wording: "rightmost non-deleted page to its left" -> "closest non-deleted sibling page to its left" "each entry tracks the buffer of the page" -> "each entry tracks the buffers of the page" (as two buffers are mentioned) "must already be pinned" -> "must already have been pinned" Best regards, Pavel Borisov Supabase