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.94.2) (envelope-from ) id 1v6FMQ-001aFI-7a for pgsql-hackers@arkaria.postgresql.org; Tue, 07 Oct 2025 21:36:47 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1v6FMN-0044bN-S0 for pgsql-hackers@arkaria.postgresql.org; Tue, 07 Oct 2025 21:36:40 +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.94.2) (envelope-from ) id 1v6FMM-0044an-S2 for pgsql-hackers@lists.postgresql.org; Tue, 07 Oct 2025 21:36:40 +0000 Received: from fout-a3-smtp.messagingengine.com ([103.168.172.146]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1v6FMJ-000vVL-36 for pgsql-hackers@postgresql.org; Tue, 07 Oct 2025 21:36:39 +0000 Received: from phl-compute-03.internal (phl-compute-03.internal [10.202.2.43]) by mailfout.phl.internal (Postfix) with ESMTP id 3998EEC0075; Tue, 7 Oct 2025 17:36:33 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-03.internal (MEProxy); Tue, 07 Oct 2025 17:36:33 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=burd.me; h=cc:cc :content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:subject :subject:to:to; s=fm2; t=1759872993; x=1759959393; bh=SVZClbLTUM qEdxr56VaYMw/4zS4nShehk2QD7+W+0nw=; b=NVD17yGk7U3j0YXPmxK9uZ1E+D mw9bJxvHrOIVd+7RJK3pQFsmXltHEC3j5ur8PsN2Z8bIXOvC9DlVM3/Rd4AhG+oH 7MkVVnolunFEhEvGVMc/I9NbBcB9YsJBj6qBmOKTXNkvzHhzCZPeDoj08GJbZFQo qEG4fT2yUDt/rfi+OhiMDV0WNVlUHbhGcmFRn02lgUuLJcq3Oy9aM7YgpaYQlo7J JS3oIVj+eJDy5wKKNm/TUhwYAu5iBWoBBDtzeaVKQ/lqA8IOhU8HtZvxEVXnK4Mj LWqpHdRijk/aX9XWSVrrjzY/M6Matqx4aowZ09yPr5OgH56Mg2XCOrBYf6ng== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm2; t= 1759872993; x=1759959393; bh=SVZClbLTUMqEdxr56VaYMw/4zS4nShehk2Q D7+W+0nw=; b=Zphagn8jVMXUpGc5drWebmqxjP0Vzli9aI5beRspGYTZBmWNB7p +Bu8OufYOJWs15sbbc/ODn+ftJYEr5rEobcDghR/bITNRczLPakokkfJlDpGoNjv 6Z3llnQWE1kLl3hzjsP6G80VMYKBB81fgnOV0Cdvgc0WomPONbMeiBITRozlprbb YDWrq1YnRWgchKXYRszBeyZCTQXxFFut7fvRtwFi/hcpr5goLUtAYz8mqkjeqfcB wNDSisBJVLAm1ENxNJwr5N6Upt6E0+UGbUqWe7IUY4c6rDKkgp6OurpGyIgcr4Ky 6TF7DdsBW/vxYpCgv++FGxsEiKTEQ1pRXuA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddutdduhedvucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucenucfjughrpeffhffvvefkjghfuffogggtsehmtdhmre ertdejnecuhfhrohhmpefirhgvghcuuehurhguuceoghhrvghgsegsuhhrugdrmhgvqeen ucggtffrrghtthgvrhhnpeegudduheehveekgeeliefhtdefffejgfejieeufeelleeuhe ektefggfduudeggfenucffohhmrghinhepphhgtghonhhfrdguvghvnecuvehluhhsthgv rhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepghhrvghgsegsuhhrugdrmh gvpdhnsggprhgtphhtthhopedvpdhmohguvgepshhmthhpohhuthdprhgtphhtthhopehg rhgvghgsuhhrugesrghmrgiiohhnrdgtohhmpdhrtghpthhtohepphhgshhqlhdqhhgrtg hkvghrshesphhoshhtghhrvghsqhhlrdhorhhg X-ME-Proxy: Feedback-ID: i675e48f3:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Tue, 7 Oct 2025 17:36:32 -0400 (EDT) Date: Tue, 7 Oct 2025 17:36:11 -0400 From: Greg Burd To: =?utf-8?Q?Burd=2C_Greg?= Cc: "=?utf-8?Q?pgsql-hackers=40postgresql.org?=" Message-ID: In-Reply-To: <97f0aa72-f172-4673-8b04-533f022c3149@app.fastmail.com> References: <97f0aa72-f172-4673-8b04-533f022c3149@app.fastmail.com> Subject: Re: Expanding HOT updates for expression and partial indexes X-Mailer: Mailspring MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="68e587cb_25cd3729_41f9" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --68e587cb_25cd3729_41f9 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Content-Disposition: inline On Jul 2 2025, at 2:10 pm, Greg Burd wrote: > The goal is to allow HOT updates under two new conditions: > * when an indexed expression has not changed > * when possible for a partial index This is still true. :) This patch has languished for nearly 2+ months at v17. Why? Primarily due to feedback that although the idea had merit, there was a critical flaw in the approach that made it a non-starter. The flaw was that I'd been executing expressions while holding both the pin and a lock on the buffer, which is not a great idea (self dead lock, etc.). This was pointed out to me (thanks Robert Haas!) and so I needed to re-think my approach. I put the patch aside for a while, then this past week at PGConf.dev/NYC I heard interest from a few people (Jeff Davis, Nathan Bossart) who encouraged me to move the code executing the expressions to just before acquiring the lock but after pinning the buffer. The theory being that my new code using the old/new tts to form and test the index tuples resulting from executing expressions was using the resultsRelInfo struct created during plan execution, not the information found on the page, and so was safe without the lock. This proved tricky because I had been using the modified_attrs and expr_attrs as a test to avoid exercising expressions when unnecessary. Calling HeapDetermineColumnsInfo() outside the buffer lock to get modified_attrs proved to be a problem as it examines an oldtup that is cobbled together from the elements on the page, requiring the lock I was trying to avoid. After reviewing how updates work in the executor, I discovered that during execution the new tuple slot is populated with the information from ExecBuildUpdateProjection() and the old tuple, but that most importantly for this use case that function created a bitmap of the modified columns (the columns specified in the update). This bitmap isn't the same as the one produced by HeapDetermineColumnsInfo() as the latter excludes attributes that are not changed after testing equality with the helper function heap_attr_equals() where as the former will include attributes that appear in the update but are the same value as before. This, happily, is immaterial for the purposes of my function ExecExprIndexesRequireUpdates() which simply needs to check to see if index tuples generated are unchanged. So I had all I needed to run the checks ahead of acquiring the lock on the buffer. So, this led to v18 (attached), which passes test wold including a number of new tests for the various corner cases relative to HOT updates for expressions. There is much room for improvement, and your suggestions are welcome. I'll find time to quantify the benefit of this patch for the targeted use cases and to ensure that all other cases see no regressions. I need to review the tests I've added to ensure that they are the minimal set required, that they communicate effectively their purpose, etc. For now, it's more shotgun than scalpel, .... I'll get to it. I added a reloption "expression_checks" to disable this new code path. Good idea or bad precedent? In execIndexing I special case for IsolationIsSerializable() and I can't remember why now but I do recall one isolation test failing... I'll check on this and get back to the thread. Or maybe you know why that