Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1mnbi3-0004rt-AB for pgsql-bugs@arkaria.postgresql.org; Thu, 18 Nov 2021 07:19:51 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1mnbi2-000733-8H for pgsql-bugs@arkaria.postgresql.org; Thu, 18 Nov 2021 07:19:50 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1mnbi1-00072u-6h for pgsql-bugs@lists.postgresql.org; Thu, 18 Nov 2021 07:19:49 +0000 Received: from wout4-smtp.messagingengine.com ([64.147.123.20]) by makus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1mnbhy-0003L7-Ak for pgsql-bugs@lists.postgresql.org; Thu, 18 Nov 2021 07:19:48 +0000 Received: from compute4.internal (compute4.nyi.internal [10.202.2.44]) by mailout.west.internal (Postfix) with ESMTP id DD3923201A90; Thu, 18 Nov 2021 02:19:43 -0500 (EST) Received: from mailfrontend1 ([10.202.2.162]) by compute4.internal (MEProxy); Thu, 18 Nov 2021 02:19:44 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=anarazel.de; h= date:from:to:cc:subject:message-id:references:mime-version :content-type:in-reply-to; s=fm3; bh=J4L++quVQS+bIUg8TSjt408XomS qTomO9CmbL9LxNPk=; b=JvoLUovx/xy4EbmhbgH7vTIqgYqGFBm4vmInl4ctPZ0 x96VZFD9q4fm6yfA3ELYRNCw03um4zQ/f3Z/Hbk6t7hUev22Wptp5XbRu1xzrcNj n6/kyQOcdF6O4o+3p+v4eVxOfe5im27HXFbdeVO52Zrp4OB9rJPIEaXJLPVdCX1z YCc+7Hp6E8kN8409EO/cTpMsdZz2NOKamhjI0ljseEB1Kmgqw/4Mvs/Qw9TYHzsq a4T9W64sy6Lj7l774vQYSEv7ITUZodLNEvlqMPqAg5uVljHq8V8V6iFmJNeGp3rP GV31uKh9dU4KCn4wdUtNQ1bE6qGW0fci+6sWIPvjWyg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to:x-me-proxy :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; bh=J4L++q uVQS+bIUg8TSjt408XomSqTomO9CmbL9LxNPk=; b=l5LTX4CAAVMXBovRePoBkU vBNyFekkMWaYtEOkODfWexwMGF9ITR5BZniePKwydBU4vP5SNF4lC9tYOgheNbaI 93EGv2wLZfh0PeEPqmSBMjeC/7vCLFoY15B3dFz1XP47J/8TsXWPnpYzoO+J346w iCOeukruniktFr1nSTuWCe5sxl/EMj7rZoIy85yWDwKaib5ioLIAyt8BhasZjwmi nGd7OuPEcpjDw0WalOn2bLCs3mPqvZa/2bdhK0ABRN1/Mq5yG17VuM/7ZUO3CWZj 5+MjjeJl0/94xNTP74HnocfuiCMM1/7SnhWX7aOSqEpt0XqdGFqcox1MJyi7QD5Q == X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvuddrfeehgddutdegucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepfffhvffukfhfgggtuggjsehttdertddttddvnecuhfhrohhmpeetnhgurhgv shcuhfhrvghunhguuceorghnughrvghssegrnhgrrhgriigvlhdruggvqeenucggtffrrg htthgvrhhnpedukefhkeelueegveetheelffffjeegleeuudelfeefuedtleffueejfffh ueffudenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpe grnhgurhgvshesrghnrghrrgiivghlrdguvg X-ME-Proxy: Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 18 Nov 2021 02:19:42 -0500 (EST) Date: Wed, 17 Nov 2021 23:19:41 -0800 From: Andres Freund To: Peter Geoghegan Cc: Dmitry Dolgov <9erthalion6@gmail.com>, Alexander Lakhin , Matthias van de Meent , PostgreSQL mailing lists Subject: Re: BUG #17255: Server crashes in index_delete_sort_cmp() due to race condition with vacuum Message-ID: <20211118071941.e4far2w5vufnahun@alap3.anarazel.de> References: <20211029145532.kfwqwlrdekunwoa2@localhost> <20211030213948.3i3f7cieqxzhd7kq@alap3.anarazel.de> <20211031192051.e3r2uxft3bulymrr@localhost> <08c2445c-c3c9-ba45-18d3-6399707d8306@gmail.com> <20211108172852.pvsarxoxi54uatch@localhost> <20211109150216.fgfybn35mwnkeef3@localhost> <20211110192010.ckvfzz352hsba5xf@alap3.anarazel.de> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20211110192010.ckvfzz352hsba5xf@alap3.anarazel.de> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Hi, I spent quite a bit more time trying to have a good testcase for reproducing this reliably. I didn't feel confident using testing that takes substantial amounts of time to hit bugs. On 2021-11-10 11:20:10 -0800, Andres Freund wrote: > The way this definitely breaks - I have been able to reproduce this in > isolation - is when one tuple is processed twice by heap_prune_chain(), and > the result of HeapTupleSatisfiesVacuum() changes from > HEAPTUPLE_DELETE_IN_PROGRESS to DEAD. > > Consider a page like this: > > lp 1: redirect to lp2 > lp 2: deleted by xid x, not yet committed > > and a sequence of events like this: > > 1) heap_prune_chain(rootlp = 1) > 2) commit x > 3) heap_prune_chain(rootlp = 2) > > 1) heap_prune_chain(rootlp = 1) will go to lp2, and see a > HEAPTUPLE_DELETE_IN_PROGRESS and thus not do anything. > > 3) then could, with the snapshot scalability changes, get DEAD back from > HTSV. Due to the "fuzzy" nature of the post-snapshot-scalability xid horizons, > that is possible, because we can end up rechecking the boundary condition and > seeing that now the horizon allows us to prune x / lp2. > > At that point we have a redirect tuple pointing into an unused slot. Which is > "illegal", because something independent can be inserted into that slot. This above isn't quite right - it turns out this requires additional steps to be triggerable (without debugging code to make things easier to hit). As shown above the result won't change, because the xmin horizon of x will have held the xmin horizon far enough back that GlobalVisTestShouldUpdate() won't ever recompute the horizon for x on its own. However, what *can* happen is that *another* tuple is within the "fuzzy" horizon window, and that tuple can trigger a recomputation of the horizon. Which then basically triggers the above problem. However even that is very hard to hit, because normally we won't recompute the horizon in that case either, because of * The current heuristic is that we update only if RecentXmin has changed * since the last update. If the oldest currently running transaction has not * finished, it is unlikely that recomputing the horizon would be useful. I.e. a snapshot needs to be computed between the the vacuum_set_xid_limit() and the heap_page_prune() call. Turns out, there's a window: vac_open_indexes() needs to lock the indexes. Which in turn will trigger AcceptInvalidationMessages(). Relcache invalidations then can trigger a catalog snapshot to be built, which can increase RecentXmin, and later trigger a new accurate horizon to be built. Consider a page like: lp 1: redirect to lp 3 lp 2: RECENTLY_DEAD tuple xmax x1, held back by session y's xmin horizon lp 3: DELETE_IN_PROGRESS tuple xmax x2 and the following sequence of actions: 1) vacuum_set_xid_limit() -> sets ComputeXidHorizonsResultLastXmin = RecentXmin 2) session y commits 3) vac_open_indexes() -> AcceptInvalidationMessages() -> InvalidateCatalogSnapshot() -> RelationCacheInvalidateEntry() -> GetCatalogSnapshot() -> GetSnapshotData() updates RecentXmin 4) x2 commits 5) heap_prune_chain(1) -> sees lp3 as RECENTLY_DEAD (or DELETE_IN_PROGRESS depending on timing) -> doesn't do anything 6) heap_prune_chain(2) -> sees a RECENTLY_DEAD, updates GlobalVisTestShouldUpdate() says yes, updates xid horizon to above x2 7) heap_prune_chain(3) -> uses the HeapOnly path at the beginning, sees a DEAD tuple, marks the element as unused Boom. Not sure if it's worth it, but I think this can be made into a reliably isolationtest by causing a tables index to locked exclusively, blocking vac_open_indexes(), which can then reliably schedule a new relcache inval, which in turn can compute a new RecentXmin. Might be too fragile to be worth it. As an independent issue, I don't think it can ever be safe to do catalog access with PROC_IN_VACUUM set. Which vac_open_indexes() (and likely some other things) clearly do. Without the current backend's xmin set, another backend can remove still required tuples. Which can cause us to build corrupt relcache entries, and then subsequently skip indexing some indexes, causing corruption. I think I hit this while trying to repro the above, but it's hard to be sure. > I hit a crash once in 13 with a slightly evolved version of the test (many > connections creating / dropping the partitions as in the original scenario, > using :client_id to target different tables). It's possible that my > instrumentation was the cause of that. Unfortunately it took quite a few hours > to hit the problem in 13... I wonder if this was actually the above PROC_IN_VACUUM thing. Greetings, Andres Freund