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 1rkAID-00ArOB-04 for pgsql-bugs@arkaria.postgresql.org; Tue, 12 Mar 2024 22:08:17 +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 1rkAIA-003Xmk-Vr for pgsql-bugs@arkaria.postgresql.org; Tue, 12 Mar 2024 22:08:15 +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.94.2) (envelope-from ) id 1rkAIA-003Xmc-By for pgsql-bugs@lists.postgresql.org; Tue, 12 Mar 2024 22:08:15 +0000 Received: from mail-yb1-xb31.google.com ([2607:f8b0:4864:20::b31]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1rkAI6-004Bdz-MY for pgsql-bugs@lists.postgresql.org; Tue, 12 Mar 2024 22:08:13 +0000 Received: by mail-yb1-xb31.google.com with SMTP id 3f1490d57ef6-dc6dcd9124bso4419039276.1 for ; Tue, 12 Mar 2024 15:08:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=leadboat.com; s=google; t=1710281291; x=1710886091; darn=lists.postgresql.org; h=user-agent:in-reply-to:content-transfer-encoding :content-disposition:mime-version:references:message-id:subject:cc :to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=7o32//YG2p+S4AD2GZPkYOKd2R/sY5YefafQrtV8lCY=; b=NHGLazhTRdSq8MMZ48bsUgXya77hhZ7dX9PxqJOF4gXUyGjsJulrmCc5P6S6toYrWm y6E6h0mUOOS4gs3okPA/Wn0+j12GJNC6gVy7YBmZtz9eHGtlhIjApBUZC0Artn8o0FA2 qrFlJpce1nmt5h9M2snG9Kff5VFb7oc2K+7+k= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1710281291; x=1710886091; h=user-agent:in-reply-to:content-transfer-encoding :content-disposition:mime-version:references:message-id:subject:cc :to:from:date:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=7o32//YG2p+S4AD2GZPkYOKd2R/sY5YefafQrtV8lCY=; b=AkcT++P7CggzCPyS6QWLjbhRkrfJgNdsRLxA2IF0cfjaroBtIAx+W2ibTUvd1svXK3 HalNwz9wzM4z7VOloxl8cGyrEMs2hmIt0JhT0OQI/ctngiVzE5PkLIHdVUKLQPCRJsEl ugvYmzLmNFWSOGdMvWmQwUZEUBN6R9PIpu8Rnd0U4vA8V2+pbV29w+Yjw3vE6GXnukCa ssWIQ5E+zKvmoAuRWkdpXTeb/TuBuIcWyszXJGW41c5/0R0Wy5BW5BZ/oP/nXQ0fLflA YCoPvpHWUNw27tMesfH6CWQzoU7AWSA7fkxhkVtH71/X9KNaNPQP0x4PBnPycwHx8qSX wRjA== X-Forwarded-Encrypted: i=1; AJvYcCUeOSmUMklUNmgTwMPj1JUFcxsZH7Vn2y5r1pHgriApLwVYSi0tXzYI7PY7ysSgE9td5KyKmd50opptpIHTnRXbM1vutVmJbUoEdY1tQRIn X-Gm-Message-State: AOJu0YwFXiyrbd4BfLLqllAV8W9aLznkF0VsY63baPYiD4khqW2sLzBw 7rY3RLj17+h1zHBMPjF3fcgWU5HsWGyyq6DvlZVkn5EUjwK5okJZcRTJgAQqNA== X-Google-Smtp-Source: AGHT+IGPTyG73MHkCjkUKMPKDEmZvhzk2Ks/lWR1dlOqPj7OXcKuJ63RIGWZ7fu0ub1O2EF1z8sqJA== X-Received: by 2002:a05:6902:2492:b0:dc8:5e26:f501 with SMTP id ds18-20020a056902249200b00dc85e26f501mr801499ybb.61.1710281290652; Tue, 12 Mar 2024 15:08:10 -0700 (PDT) Received: from google.com ([2600:1702:a20:5750::48]) by smtp.gmail.com with ESMTPSA id h6-20020a255f46000000b00dc2328c28ebsm1868358ybm.51.2024.03.12.15.08.09 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Tue, 12 Mar 2024 15:08:10 -0700 (PDT) Date: Tue, 12 Mar 2024 15:08:07 -0700 From: Noah Misch To: Ronan Dunklau Cc: Michael Paquier , pgsql-bugs Subject: Re: FSM Corruption (was: Could not read block at end of the relation) Message-ID: <20240312220807.05.nmisch@google.com> References: <1958756.PYKUYFuaPT@aivenlaptop> <20240306184228.a7.nmisch@google.com> <1970118.yKVeVyVuyW@aivenlaptop> MIME-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <1970118.yKVeVyVuyW@aivenlaptop> User-Agent: Mutt/2.2.12 (2023-09-09) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On Thu, Mar 07, 2024 at 12:21:24PM +0100, Ronan Dunklau wrote: > Le mercredi 6 mars 2024 19:42:28 CET, vous avez écrit : > > On Wed, Mar 06, 2024 at 10:31:17AM +0100, Ronan Dunklau wrote: > > > Le mardi 5 mars 2024, 00:05:03 CET Noah Misch a écrit : > > > > I would guess this one is more risky from a performance perspective, > > > > since > > > > we'd be adding to a hotter path under RelationGetBufferForTuple(). > > > > Still, > > > > it's likely fine. > > > > > > I ended up implementing this in the attached patch. The idea is that we > > > detect if the FSM returns a page past the end of the relation, and ignore > > > it. In that case we will fallback through the extension mechanism. > > > > > > For the corrupted-FSM case it is not great performance wise, as we will > > > extend the relation in small steps every time we find a non existing > > > block in the FSM, until the actual relation size matches what is recorded > > > in the FSM. But since those seldom happen, I figured it was better to > > > keep the code really simple for a bugfix. > > > > That could be fine. Before your message, I assumed we would treat this like > > the case of a full page. That is, we'd call > > RecordAndGetPageWithFreeSpace(), which would remove the FSM entry and > > possibly return another. That could be problematic if another backend is > > extending the relation; we don't want to erase the extender's new FSM > > entry. I'm parking this topic for the moment. > > I would argue this is fine, as corruptions don't happen often, and FSM is not > an exact thing anyway. If we record a block as full, it will just be unused > until the next vacuum adds it back to the FSM with a correct value. If this happened only under FSM corruption, it would be fine. I was thinking about normal extension, with an event sequence like this: - RelationExtensionLockWaiterCount() returns 10. - Backend B1 extends by 10 pages. - B1 records 9 pages in the FSM. - B2, B3, ... B11 wake up and fetch from the FSM. In each of those backends, fsm_does_block_exist() returns false, because the cached relation size is stale. Those pages remain unused until VACUUM re-records them in the FSM. PostgreSQL added the multiple-block extension logic (commit 719c84c) from hard-won experience bottlenecking there. If fixing $SUBJECT will lose 1% of that change's benefit, that's probably okay. If it loses 20%, we should work to do better. While we could rerun the 2016 benchmark to see how the patch regresses it, there might be a simple fix. fsm_does_block_exist() could skip the cache when blknumber>=cached, like this: return((BlockNumberIsValid(smgr->smgr_cached_nblocks[MAIN_FORKNUM]) && blknumber < smgr->smgr_cached_nblocks[MAIN_FORKNUM]) || blknumber < RelationGetNumberOfBlocks(rel)); How do you see it? That still leaves us with an avoidable lseek in B2, B3, ... B11, but that feels tolerable. I don't know how to do better without switching to the ReadBufferMode approach. > > The patch currently adds an lseek() whenever the FSM finds a block. I see > > relevant-looking posts from mailing list searches with subsets of these > > terms: RelationGetNumberOfBlocks lseek benchmark overhead. I bet we should > > reduce this cost add. Some ideas: > > > > - No need for a system call if the FSM-returned value is low enough with > > > respect to any prior RelationGetNumberOfBlocks() call. > > I'm not sure what you mean by "low enough". What I chose to implement instead I just meant "less than". Specifically, if "fsm_returned_value < MAX(all RelationGetNumberOfBlocks() calls done on this relation, since last postmaster start)" then the FSM-returned value won't have the problem seen in $SUBJECT. > > - We'd be happy and efficient with a ReadBufferMode such that > > ReadBufferExtended() returns NULL after a 0-byte read, with all other > > errors handled normally. That sounds like the specification of > > RBM_NORMAL_NO_LOG. Today's implementation of RBM_NORMAL_NO_LOG isn't that, > > but perhaps one RBM could satisfy both sets of needs. > > I'm not sure about this one, this seems to have far more reaching > consequences. This would mean we don't have any cooperation from the FSM, and > would need to implement error recovery scenarios in each caller. But I admit I > haven't looked too much into it, and focused instead in seeing if the current > approach can get us to an acceptable state. I, too, am not sure. I agree that RBM approach wouldn't let you isolate the changes like the last patch does. Each GetFreeIndexPage() caller would need code for the return-NULL case. Avoiding that is nice. (If we ever do the RBM change in the future, the fsm_readbuf() and vm_readbuf() !extend cases should also use it.) > --- a/src/backend/storage/freespace/freespace.c > +++ b/src/backend/storage/freespace/freespace.c > + if (addr.level == FSM_BOTTOM_LEVEL) { > + BlockNumber blkno = fsm_get_heap_blk(addr, slot); > + Page page; > + /* > + * If the buffer is past the end of the relation, > + * update the page and restarts from the root > + */ > + if (fsm_does_block_exist(rel, blkno)) > + { > + ReleaseBuffer(buf); > + return blkno; > + } > + page = BufferGetPage(buf); > + LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); > + fsm_set_avail(page, slot, 0); > + MarkBufferDirtyHint(buf, false); I wondered if we should zero all slots on the same FSM page that correspond to main fork blocks past the end of the main fork. The usual "advancenext" behavior is poor for this case, since every later slot is invalid in same way. My current thinking: no, it's not worth the code bulk to do better. A single extension caps at 64 blocks, so the worst case is roughly locking the buffer 64 times and restarting from FSM root 64 times. For something this infrequent, that's not bad. Thanks, nm