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 1rkL3B-00C1gZ-NM for pgsql-bugs@arkaria.postgresql.org; Wed, 13 Mar 2024 09:37:30 +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 1rkL3A-00GdKP-4Y for pgsql-bugs@arkaria.postgresql.org; Wed, 13 Mar 2024 09:37:28 +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 1rkL39-00GdJ1-IW for pgsql-bugs@lists.postgresql.org; Wed, 13 Mar 2024 09:37:28 +0000 Received: from mail-wr1-x42c.google.com ([2a00:1450:4864:20::42c]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1rkL35-004GhI-Qz for pgsql-bugs@lists.postgresql.org; Wed, 13 Mar 2024 09:37:26 +0000 Received: by mail-wr1-x42c.google.com with SMTP id ffacd0b85a97d-33e959d8bc0so3027840f8f.1 for ; Wed, 13 Mar 2024 02:37:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aiven.io; s=google; t=1710322643; x=1710927443; darn=lists.postgresql.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=64dY0OXb37oUZMaN9FtGuyFv/isjWOKwD+tiUtrYopk=; b=o1CcUFOS4A+b3OmKGzZWy5aJwaUpH/BO9cDdMPqh8MNQ+FBT8ArbAUitCaKqzPa61L xUEkjC1sBQiArikAwtnazuqHpoBOcnkGOylmhQ7ca2WL5COUN7eT9SYtzquSGGCwzmNO Vkq4WidHjhx9ny//0hiC5bgXN4VdzvZwV8KSg= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1710322643; x=1710927443; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=64dY0OXb37oUZMaN9FtGuyFv/isjWOKwD+tiUtrYopk=; b=CaFRSy08B242gNsYcDyMhLWCvoXzDKtNyz63KhT85pJHxdsoTILeJIElmLpV1OrwdV 7eIUzc13G20RhErVj6OJQu4f57Be2Gr+XMVhNAz8BgbUDsM9TJ4s7Ef4v1S2DAl/K9HX 8yohj60v+/BUWKknXn68P5Irt+XYxNeQWc+9uc57LriHzBf/HgicOVdNSBxdehw5LaTK NqRjcQ6Vflqtwt9JJ8BqR/9hC1RWmMzH7HyC/Z2rhwK2Jd8WbR9HWsSbK9WKSo28zEpT ocGQ+xS3OZxau78sAO5ihRcpw5XEVlHeGFjJ3JXeaHl8bFd8ZEcQpWVX1tD5DFBZDQ+u jz4A== X-Forwarded-Encrypted: i=1; AJvYcCWl4DQ555d3a5b6ygLD7M8VsMyc3cJxDKH08rcZcpTESD0APz0gKmPbK/MZ2UIPgJnYk3VyprT/xk0eut/H2Fc9BQW7XUKyTWXUnnCaEjDH X-Gm-Message-State: AOJu0Yzag3OhPH9EqG2qA1ihaxphCvhbnBlCB2h6cCUDzoYuc7j91tSF a1WxsZ++QqFG6t+4vWtF835VvdrY+lWTj2cFrDGzgSHWAQK4z6ehd9mKjk3WxaUaZNV+mpjZ73I 5s+U= X-Google-Smtp-Source: AGHT+IH3eBGFYLbgEhTZT+71ytTU+KSENw7NDulsqApMTxy7hlc+M69yk3WgJlj4htQapXpYV0ZNKQ== X-Received: by 2002:adf:f50b:0:b0:33e:3a4:6c1a with SMTP id q11-20020adff50b000000b0033e03a46c1amr1343146wro.30.1710322642808; Wed, 13 Mar 2024 02:37:22 -0700 (PDT) Received: from aivenlaptop.localnet ([45.13.105.93]) by smtp.gmail.com with ESMTPSA id m18-20020adffa12000000b0033d8ce120f2sm11184250wrr.95.2024.03.13.02.37.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 13 Mar 2024 02:37:22 -0700 (PDT) From: Ronan Dunklau To: Noah Misch Cc: Michael Paquier , pgsql-bugs Subject: Re: FSM Corruption (was: Could not read block at end of the relation) Date: Wed, 13 Mar 2024 10:37:21 +0100 Message-ID: <3225659.5fSG56mABF@aivenlaptop> In-Reply-To: <20240312220807.05.nmisch@google.com> References: <1970118.yKVeVyVuyW@aivenlaptop> <20240312220807.05.nmisch@google.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="nextPart5934870.MhkbZ0Pkbq" Content-Transfer-Encoding: 7Bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk This is a multi-part message in MIME format. --nextPart5934870.MhkbZ0Pkbq Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Le mardi 12 mars 2024, 23:08:07 CET Noah Misch a =C3=A9crit : > > 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: >=20 > - 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 relati= on > size is stale. Those pages remain unused until VACUUM re-records them in > the FSM. >=20 > 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>=3Dcached, like this: >=20 > return((BlockNumberIsValid(smgr->smgr_cached_nblocks[MAIN_FORKNUM])=20 && > blknumber < smgr- >smgr_cached_nblocks[MAIN_FORKNUM]) || > blknumber < RelationGetNumberOfBlocks(rel)); >=20 > How do you see it? That still leaves us with an avoidable lseek in B2, B= 3, > ... B11, but that feels tolerable. I don't know how to do better without > switching to the ReadBufferMode approach. Hi Noah and thanks for your feedback. That makes sense. I'm a bit worried about triggering additional lseeks when= we=20 in fact have other free pages in the map, that would pass the test. I'm not= =20 sure how relevant it is given the way we search the FSM with fp_next_slot=20 though... To address that, I've given a bit of thought about enabling / disabling the= =20 auto-repair behaviour with a flag in GetPageWithFreeSpace to distinguish th= e=20 cases where we know we have a somewhat up-to-date value compared to the cas= e=20 where we don't (as in, for heap, try without repair, then get an uptodate=20 value to try the last block, and if we need to look at the FSM again then a= sk=20 for it to be repaired) but it brings way too much complexity and would need= =20 careful thought for each AM.=20 So please find attached a patch with the change you propose. Do you have a link to the benchmark you mention though to evaluate the patc= h=20 against it ? =20 >=20 > > > The patch currently adds an lseek() whenever the FSM finds a block. I > > > see > > > relevant-looking posts from mailing list searches with subsets of the= se > > > terms: RelationGetNumberOfBlocks lseek benchmark overhead. I bet we > > > should > > > reduce this cost add. Some ideas: > > >=20 > > > - No need for a system call if the FSM-returned value is low enough w= ith > > >=20 > > > > respect to any prior RelationGetNumberOfBlocks() call. > >=20 > > 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 > > >=20 > > > ReadBufferExtended() returns NULL after a 0-byte read, with all oth= er > > >=20 > > > 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. > >=20 > > 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 FS= M, > > 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. >=20 > 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.) >=20 > > --- a/src/backend/storage/freespace/freespace.c > > +++ b/src/backend/storage/freespace/freespace.c > >=20 > > + if (addr.level =3D=3D FSM_BOTTOM_LEVEL) { > > + BlockNumber blkno =3D=20 fsm_get_heap_blk(addr, slot); > > + Page page; > > + /* > > + * If the buffer is past the end=20 of the relation, > > + * update the page and restarts=20 from the root > > + */ > > + if (fsm_does_block_exist(rel,=20 blkno)) > > + { > > + ReleaseBuffer(buf); > > + return blkno; > > + } > > + page =3D BufferGetPage(buf); > > + LockBuffer(buf,=20 BUFFER_LOCK_EXCLUSIVE); > > + fsm_set_avail(page, slot, 0); > > + MarkBufferDirtyHint(buf, false); >=20 > I wondered if we should zero all slots on the same FSM page that correspo= nd > to main fork blocks past the end of the main fork. The usual "advancenex= t" > 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. That was my reasoning as well. Paying a small performance penalty in the=20 unlikely case we hit a corruption and get done with it seems correct. >=20 > Thanks, > nm --nextPart5934870.MhkbZ0Pkbq Content-Disposition: attachment; filename="v3-0001-Detect-FSM-corruption-and-repair-it.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="x-UTF_8J"; name="v3-0001-Detect-FSM-corruption-and-repair-it.patch"