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.96) (envelope-from ) id 1vTY6d-004Lg5-39 for pgsql-bugs@arkaria.postgresql.org; Thu, 11 Dec 2025 04:16:44 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vTY6c-002DKI-2A for pgsql-bugs@arkaria.postgresql.org; Thu, 11 Dec 2025 04:16:43 +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.96) (envelope-from ) id 1vTY6c-002DKA-1D for pgsql-bugs@lists.postgresql.org; Thu, 11 Dec 2025 04:16:43 +0000 Received: from mail-ed1-x52d.google.com ([2a00:1450:4864:20::52d]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1vTY6a-00061Y-1k for pgsql-bugs@lists.postgresql.org; Thu, 11 Dec 2025 04:16:42 +0000 Received: by mail-ed1-x52d.google.com with SMTP id 4fb4d7f45d1cf-6419aaced59so844989a12.0 for ; Wed, 10 Dec 2025 20:16:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1765426594; x=1766031394; darn=lists.postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=kp5Z9Id5/kYzBjlQMp8oAq0zELRp6Prs179kCavtZAg=; b=JQ48BMKhu2zGcAwMXH/9JNdOHkA3drYrr+f9ZQz8uRl9JH9VkqpcGWQuzgWG5eLKBy K60Ny25XCYWb6PW0h3Cx/VWmOik5QP4BDSkUOLDWIfss0r5Kn1Uuf18Dz5hkT6u2tROu cT+XqLsp6wZJSUiSQ6H5u+yWv8LZLmVI70iTFLA/F5DjlczLLH4dDW1eFoNJYFsu4dwa U+PgIdk9ChXLpvQ0E5vhGFI/aCrvlC1oJ2U76fIC3IXHfxzuCB8BnSxVZWDLOEKrquAn 2IcGeKJkYzP4iwVjTKFvOiaTQFSW9Gli5N78AKoMyHCek20bwcrr2DahoUfnZptFePPF Z3bw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1765426594; x=1766031394; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=kp5Z9Id5/kYzBjlQMp8oAq0zELRp6Prs179kCavtZAg=; b=YMPtpx9kZhmef8lJ6zmQlFf9QDuXcD8IaQo/vnYefN29Cfuu1HcAAY+KdA1KYnFXxw LheoqRQM5HOgHV99zKouPpu1TuC318yWreQ/1/U2LSZk0lTDCH3n/VKGVoM7lu0R0PcG ECLwuapMNwhl9cATbjxhO6QUealxj7zK46va8BuN30Jiz4/EkhRBtXj0rTrznfeyX13x 1HSz2mAyfc4t+ZxrPh2X171jvi6Gtt2mhu1iCxeHxlWbS/6vMRQHyaLkJw7xMoEYU6rC oRuj1JsiFN7LOWOkwg+fBM9hwhMrUfBJbY7Zxl7OYQVXHGAyUPx1C9TzbDmA39+dSlBK ClUw== X-Forwarded-Encrypted: i=1; AJvYcCWyNasn10t42BUqZnWqI3fzalaw81mnrbV9rdD+uO9c91OHqkX6ozqnu7fMzBQaq5vtT3qVfPXeI3Dx@lists.postgresql.org X-Gm-Message-State: AOJu0YxqBjWAqfbKe1oBRp48s/aTR7RrvYeDcWt6S03dbp0jKjovBQjG NA5oraBhLmd5aF+C8D4B0wgsJBL4ezY0LYzwqGibBrb28vCkxrxcLHF3Zt457of4OEBSiS2MnmR u7BB4mDJlGwM8P4+wEj6qAxs3e/LPHT8= X-Gm-Gg: AY/fxX4wHDXJ+H9RGSTvYCACtYCq8+wM3ZnqVTit6xLKdDQhNy3CosdRIe4Fj7ge1JT w67Ej0Xu7YjTa8uUfCvNdLGmJMWUrK/dgqtRVUczFpdbmHEbhSZLE2OZ8w/AKP+yCZ/WaMOQYeR hDVu7HWiNvwYlPrx5aSBocn3qDYi54lM4dnAr3hl0Of63ryNjqFY11iJ6OBgRxZfro4Ht9YduOd xQY23dcFkKn1oO6GprZHKiBkOpoJ2DjC+YZSEv71qecOS139nvVKhpqvp9gWofl59DyoTxqMexT 5EaNb7atUzkhuLA1t0nfIld8ok4IWsj08A5B4bPgDl03Zq8LUArsFmTyIWM+iTG9ChG5rTI= X-Google-Smtp-Source: AGHT+IGXUnI1LorsM30zEmDC03IFbSn61MIFrwWT+dl7JZqlF1qwDe4ZaGhL6fbGuqpu7mtKCIfdr705y2R9m0iYGvo= X-Received: by 2002:a05:6402:13d1:b0:647:53a0:18e4 with SMTP id 4fb4d7f45d1cf-6496d5b02a3mr4226377a12.27.1765426594244; Wed, 10 Dec 2025 20:16:34 -0800 (PST) MIME-Version: 1.0 References: <19006-80fcaaf69000377e@postgresql.org> In-Reply-To: From: Xuneng Zhou Date: Thu, 11 Dec 2025 12:16:19 +0800 X-Gm-Features: AQt7F2rFbKsz_FpCZIVsxmHCi33LXWNZBhBczIRQYx3ob5iRRDsbim0WFDzjSHE Message-ID: Subject: Re: BUG #19006: Assert(BufferIsPinned) in BufferGetBlockNumber() is triggered for forwarded buffer To: Thomas Munro Cc: exclusion@gmail.com, pgsql-bugs@lists.postgresql.org, Michael Paquier , Tom Lane , nathandbossart@gmail.com Content-Type: text/plain; charset="UTF-8" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Hi, I've reviewed patch v2 carefully and found no correctness problems. Although the performance benefit it brings might be intangible, the new interface is more robust and simpler than the current one. It does save a lot of bookkeeping: 1) Initialize slots to InvalidBuffer: Before calling StartReadBuffers, it had to zero out buffer slots: while (stream->initialized_buffers < buffer_index + nblocks) stream->buffers[stream->initialized_buffers++] = InvalidBuffer; 2) Scan for forwarded buffers: After the call, it had to scan the array to count how many buffers were forwarded: forwarded = 0; while (nblocks + forwarded < requested_nblocks && stream->buffers[buffer_index + nblocks + forwarded] != InvalidBuffer) forwarded++; stream->forwarded_buffers = forwarded; 3) Clear consumed slots: When returning a buffer to the consumer, it had to clear the slot: stream->buffers[oldest_buffer_index] = InvalidBuffer; This could be fragile because: - read_stream.c must maintain the invariant that unused slots are InvalidBuffer. - The scanning loop is O(n) and "layer-violating" (it's re-discovering information that StartReadBuffers already knew). - Missing a = InvalidBuffer assignment anywhere would cause silent corruption. With the new interface -- Make npinned an explicit in/out parameter. 1) No initialization needed: StartReadBuffers doesn't look at uninitialized slots. It only reads buffers[0..npinned-1]. 2) No scanning needed: The count is returned directly: *npinned = actual_npinned - *nblocks; // Buffer manager tells you the count 3) No clearing needed: The "is it forwarded?" check is now i < actual_npinned, not buffers[i] != InvalidBuffer. As for the xxx questions, here're some thoughts: XXX is the single-buffer specialization unaffected due to dead code elimination, as expected? - The single-buffer specialization stays unchanged: StartReadBuffer() still passes npinned=0 with allow_forwarding=false, so the extra parameter is dead code there and the compiler will inline/strip it just as before. XXX unused queue entries could potentially be electrified with wipe_mem/VALGRIND_MAKE_MEM_NOACCESS, though existing bufmgr.c assertions are very likely to reveal any fencepost bugs already - The queue-entry sanitization step removed should not affect behavior, since StartReadBuffers() now relies on npinned rather than InvalidBuffer sentinels. Leaving stale values in the array is likely harmless. If desired, using wipe_mem or VALGRIND_MAKE_MEM_NOACCESS would simply provide additional debugging hardening. XXX perhaps we don't need quite so many _npinned <= _nblocks assertions - I think this makes sense. In my experience, reading through the read_stream module already requires a fair amount of effort and focus. There are many edge cases to watch, and the complexity is amplified by managing two arrays, pointer arithmetic across function calls, and numerous assertions to validate those operations. Removing assertions that are not strictly necessary can make the code more readable and easier to follow, without weakening its defensive properties. // In read_stream_start_pending_read(): // REMOVE this pre-call assertion: - Assert(stream->pending_read_npinned <= stream->pending_read_nblocks); // Entry in StartReadBuffersImpl (bufmgr.c): - Assert(*npinned <= *nblocks); // KEEP this post-call assertion: Assert(stream->pending_read_nblocks >= stream->pending_read_npinned); The pre-call assertion in read_stream_start_pending_read looks less needed since the entry assertion in StartReadBuffersImpl catches exactly a subset of bugs that the entry assertion catches, with no intervening code that could obscure the failure or change program state. -- Best, Xuneng