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 1k5eXv-0002gh-K0 for pgsql-hackers@arkaria.postgresql.org; Wed, 12 Aug 2020 00:23:11 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1k5eXu-0004F8-Ek for pgsql-hackers@arkaria.postgresql.org; Wed, 12 Aug 2020 00:23:10 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1k5eUp-0000dr-NA for pgsql-hackers@lists.postgresql.org; Wed, 12 Aug 2020 00:19:59 +0000 Received: from mail-qv1-xf2e.google.com ([2607:f8b0:4864:20::f2e]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1k5eUm-00033t-Qj for pgsql-hackers@lists.postgresql.org; Wed, 12 Aug 2020 00:19:59 +0000 Received: by mail-qv1-xf2e.google.com with SMTP id j10so256139qvo.13 for ; Tue, 11 Aug 2020 17:19:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=2ndquadrant-com.20150623.gappssmtp.com; s=20150623; h=date:from:to:cc:subject:message-id:mime-version:content-disposition :content-transfer-encoding:in-reply-to:user-agent; bh=BAjXZfddfLYuPUaMsqeiVHN+Plba4pwSI+uGn2645dk=; b=TnL9c7OYBHR1Cl1ouJJL9cWsX6YtumvskKgfiE54P5zSzrYR062xay5Cf2LEF+M1at 2r4AyYKi74eYslxeoBJF0SO+EmLLz6k2kajcHzXZScHOdzfMJxpWrLtf99dGJlGU1Kam fE0Gzyn3wDgqki2k5FmE2l/vK0suPgZqfSPn8K8FE0ZXRmNcbj8cMD1u58vs8ZV2PTy5 a/1cFlQJ6J/EDNR8xry+oHqKADMC90gwbMz/5hzXgRHQ5IkrV/Tok8ZSLQZMFEUmkMgc 9NR2M5LgYUUNJJwK+QMXuSNKX3HKVG/uK19AQALyiTxLDetRoZEVbHh/cp6pXG8c52sf CfQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition:content-transfer-encoding:in-reply-to :user-agent; bh=BAjXZfddfLYuPUaMsqeiVHN+Plba4pwSI+uGn2645dk=; b=Y2ymRPVF8n25MAnx7nB+1iHilzzL3uh6cFPylKkhXfGE+bHd0AoYHWQR3hJ56+Nkud Gtf/J+/YfWME9VIRWHZ3vAW6WYR0rXcN3s5H+9i0YIsKRqQPEdHWaS1TmO1KIKJaD1Kh +RO1xD3LBNJ5qc9T9OoTbLX2M5JqexCXpJDfUUn/ZA7H8gWVI+iCoUObVfYro3iQXAGZ IWeCaNh6QAxf+Etb942AeOWDs4jHtpAvmQGUn/JrVW+tmOMyCzFab/SWUlPBzc8C/9e8 lVQlRFIytZlScbQkHiC5CYOoqekcvy3Qbaii9UXaqN7n1/ITC2DecdW8iZQCdbmBG1Aq scoA== X-Gm-Message-State: AOAM532XIvlcN42uvSvHke22K/aJob2En2kMsQMGQ0eBGsrWZeGPyx9Y Yls7t702SmtqF7zX8/JK6uclOFRI+x62YluP4DOv2RtBxNakQ/AfoJWrVef9E10fbPu1ldzJOPY M2aYrzeX1tVc8cC/TltfM4/qWmkGe3o/I3w4LpSNC/5j17g2ASp8vdoQfeuR8KKxXZv/V5kBmXH 1et07weHhPLRBp72Kq X-Google-Smtp-Source: ABdhPJzf5d8aI2YE7O/QXZeWfRCyjjvPMLTqOy5XZVj2BJPe5HCFnpGqOWuIE+CUy+g11WAR10N83A== X-Received: by 2002:a05:6214:612:: with SMTP id z18mr4221464qvw.25.1597191594610; Tue, 11 Aug 2020 17:19:54 -0700 (PDT) Received: from perhan.alvh.no-ip.org ([190.95.19.47]) by smtp.gmail.com with ESMTPSA id f4sm474793qtp.38.2020.08.11.17.19.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 11 Aug 2020 17:19:53 -0700 (PDT) Received: by perhan.alvh.no-ip.org (Postfix, from userid 1000) id 2A0CD2A1528; Tue, 11 Aug 2020 20:19:52 -0400 (-04) Date: Tue, 11 Aug 2020 20:19:52 -0400 From: Alvaro Herrera To: Anastasia Lubennikova Cc: PostgreSQL Hackers Subject: Re: [BUG] Error in BRIN summarization Message-ID: <20200812001952.GA2341@alvherre.pgsql> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="vtzGhvizbBRQ85DL" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <602d8487-f0b2-5486-0088-0f372b2549fa@postgrespro.ru> User-Agent: Mutt/1.10.1 (2018-07-13) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk --vtzGhvizbBRQ85DL Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: 8bit On 2020-Jul-23, Anastasia Lubennikova wrote: > This error is caused by the problem with root_offsets array bounds. It > occurs if a new HOT tuple was inserted after we've collected root_offsets, > and thus we don't have root_offset for tuple's offnum. Concurrent insertions > are possible, because brin_summarize_new_values() only holds ShareUpdateLock > on table and no lock (only pin) on the page. Excellent detective work, thanks. > The draft fix is in the attachments. It saves root_offsets_size and checks > that we only access valid fields. I think this is more complicated than necessary. It seems easier to solve this problem by just checking whether the given root pointer is set to InvalidOffsetNumber, which is already done in the existing coding of heap_get_root_tuples (only they spell it "0" rather than InvalidOffsetNumber, which I propose to change). AFAIR this should only happen in the 'anyvisible' mode, so I added that in an assert. -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services --vtzGhvizbBRQ85DL Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="hot-summarization.patch" diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 267a6ee25a..08252bc0c0 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -1324,6 +1324,12 @@ heapam_index_build_range_scan(Relation heapRelation, * buffer continuously while visiting the page, so no pruning * operation can occur either. * + * In the case where we don't hold ShareUpdateExclusiveLock on the + * table, it's possible for some HOT tuples to appear that we didn't + * know about when we first read the page. To handle that case, we + * re-obtain the list of root offsets when a HOT tuple points to a + * root item that we don't know about. + * * Also, although our opinions about tuple liveness could change while * we scan the page (due to concurrent transaction commits/aborts), * the chain root locations won't, so this info doesn't need to be @@ -1625,6 +1631,23 @@ heapam_index_build_range_scan(Relation heapRelation, offnum = ItemPointerGetOffsetNumber(&heapTuple->t_self); + /* + * If a HOT tuple points to a root that we don't know + * about, obtain root items afresh. If that still fails, + * report it as corruption. + */ + if (root_offsets[offnum - 1] == InvalidOffsetNumber) + { + Page page = BufferGetPage(hscan->rs_cbuf); + + /* This can only happen in 'anyvisible' mode */ + Assert(anyvisible); + + LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE); + heap_get_root_tuples(page, root_offsets); + LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK); + } + if (!OffsetNumberIsValid(root_offsets[offnum - 1])) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c index 256df4de10..7e3d44dfd6 100644 --- a/src/backend/access/heap/pruneheap.c +++ b/src/backend/access/heap/pruneheap.c @@ -732,7 +732,7 @@ heap_page_prune_execute(Buffer buffer, * root_offsets[k - 1] = j. * * The passed-in root_offsets array must have MaxHeapTuplesPerPage entries. - * We zero out all unused entries. + * Unused entries are filled with InvalidOffsetNumber (zero). * * The function must be called with at least share lock on the buffer, to * prevent concurrent prune operations. @@ -747,7 +747,8 @@ heap_get_root_tuples(Page page, OffsetNumber *root_offsets) OffsetNumber offnum, maxoff; - MemSet(root_offsets, 0, MaxHeapTuplesPerPage * sizeof(OffsetNumber)); + MemSet(root_offsets, InvalidOffsetNumber, + MaxHeapTuplesPerPage * sizeof(OffsetNumber)); maxoff = PageGetMaxOffsetNumber(page); for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum)) --vtzGhvizbBRQ85DL--