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 1ve07a-007gwk-0L for pgsql-performance@arkaria.postgresql.org; Fri, 09 Jan 2026 00:12:55 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1ve07Y-004znb-2S for pgsql-performance@arkaria.postgresql.org; Fri, 09 Jan 2026 00:12:53 +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.96) (envelope-from ) id 1ve07Y-004znS-0x for pgsql-performance@lists.postgresql.org; Fri, 09 Jan 2026 00:12:53 +0000 Received: from mail-pg1-x535.google.com ([2607:f8b0:4864:20::535]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1ve07X-004ykg-0g for pgsql-performance@lists.postgresql.org; Fri, 09 Jan 2026 00:12:51 +0000 Received: by mail-pg1-x535.google.com with SMTP id 41be03b00d2f7-bc17d39ccd2so1652635a12.3 for ; Thu, 08 Jan 2026 16:12:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1767917569; x=1768522369; darn=lists.postgresql.org; h=content-transfer-encoding:subject:from:content-language:to :user-agent:mime-version:date:message-id:from:to:cc:subject:date :message-id:reply-to; bh=O89/ObIEc1iQX03iqGqN0c35jz8VrYK2rB6sFRpEa4s=; b=SiM4TrG6+7Mbir4x63GuhWBVORneAq/+h434wwmlT9KNHJ2fJVFgXZm3vPlUK+1KkM KI29JlxYqti+LuL3+gNrhF2S/68WKBEw28YcyhJ1iuJGPPUrtuJQ5X3SisKZHTIbmSUL rjMgjVqxapwgpJsuIAkJB9BUOUB/sBRhfSNsKZpLSlqgN4fUTFtUIUcXS59ito744UR8 Ml2QLFi0dcN7IPyJuRsH4s617nVAkYdHT4ady6oBggIUmjuNha21INpup8BJ1UdFVRKX 1mw53mgB4UfzLajqBB/4EOE48miTBj/D5e13w+ZPHqKI58sy3g3o6qKNVLNAC7XiJuV5 FbSQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767917569; x=1768522369; h=content-transfer-encoding:subject:from:content-language:to :user-agent:mime-version:date:message-id:x-gm-gg:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=O89/ObIEc1iQX03iqGqN0c35jz8VrYK2rB6sFRpEa4s=; b=vISiiILOooGusyibSfjpRmF4bTy0NQAA/cM005G2a1GVAil+nFCn78Wr5+Ybhf/oou RkDnIhkSxOSE69hwMr4Q5S9+A01Vz9o8Jf9Fs+9eNKKhOnVWsBc8UTp4xRW/sT5v7oVW WS2YlzoFUA919MZczhBvsSHLwFtoM0LaDKa8ZA/wU5D1up+VCq7hcLnb6V4Uio4H/8QF EX4dPzATrqN6UUeq9UZOAx9lEdFgWqN3s/eHENMoQcJHAk3sLZHcJ/DA5pQUkj/q7NWH PI3V7YF7cu1RHVUAsmufLoKfPTd3r/yI4bzOwD+ri3fc6cCO5EfHNbbby2LUtjKcCOKw 2JVg== X-Gm-Message-State: AOJu0YxcATdt3y2LogCn/xKqO4RvNQziqZmixeU5MNBp4/+/YDy35Qa0 2kDsjvnMRXdQmoYjwuhmnMTRQ0pedrZxhbrl1cHHueAa1+ar88SNS0w7OmVf4A== X-Gm-Gg: AY/fxX7xSvPWZQH5vmBixWEPCQSiCdINMQS9mzsPAMC/UK2K8i4MxQFWT161bK5WlFM wwQtmhiydyeUiCsLOy9mUWMisEeGcczBO8e1zK8DcQiW/loiinWnLftgChfWhHC+7rsg2EVTBsl Ash9WLuxtNZR+JJ/Y9sEESfUOuZpszSnXz87Uf02p1PWKGk1AZ7eF/OLgCrvtfOfq2jCt+jWE6h zHLzjwdUVH+w7WuhjQhm3LirJS2+kFBqgXX6oBsnWEo8zHl/zdnyzkqxN3z66ETAjU+3eTm+LC9 Gexa7Z5rtVR6Zfo9HU8IRWew/qnAdhV/GNbkDLmsRFnXlg5jgPnH9PaV3hyGPd14YQk58yy/9kZ HQHVdppiCpRDFR01FzPFDk+gyV3TEKKHofM2R5821AoZQ9vU6ZJMYFxrE0KvZ47h9VWYmuHWZ85 V4YC8icOYhVNYlcVuIVN7UbUJDdkLZnIjPjSPjs4O1ZB2YXFXNTfunBliFozLNIT0Mv8mAZoypu chf X-Google-Smtp-Source: AGHT+IEOAds8qPWO428tiZLRJB9SxX14Dnw93P4ZyXWKJYAX7dBY7nN/O88bAWb070Ag7/pPURF08Q== X-Received: by 2002:a17:90b:3a90:b0:330:7ff5:2c58 with SMTP id 98e67ed59e1d1-34f68b4c514mr9089070a91.7.1767917569311; Thu, 08 Jan 2026 16:12:49 -0800 (PST) Received: from ?IPV6:2404:4400:414b:1700:b092:c6d0:7613:982b? ([2404:4400:414b:1700:b092:c6d0:7613:982b]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-34f5f7c4141sm8909817a91.6.2026.01.08.16.12.47 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 08 Jan 2026 16:12:48 -0800 (PST) Message-ID: <5874da50-c9c3-4e66-907a-a5b564eabd85@gmail.com> Date: Fri, 9 Jan 2026 13:12:45 +1300 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird To: pgsql-performance@lists.postgresql.org Content-Language: en-US From: Mark Kirkwood Subject: Another unexpected planner choice in simple JOIN Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk I have encountered another one of these. However managed to work out what the issue was myself this time! However I figured it might be interesting/useful for folk generally. Same schema as before, with a few parameter changes to 16devel: $ cat postgresql.auto.conf # Do not edit this file manually! # It will be overwritten by the ALTER SYSTEM command. parallel_setup_cost = '30000' shared_buffers = '2GB' max_wal_size = '4GB' I don't think any of these actually effect this new query: test0=# EXPLAIN ANALYZE SELECT t0.id0, t1.val                 FROM tab0 AS t0                 JOIN tab1 AS t1 ON (t0.id0 = t1.id0)                 WHERE t0.id0 < 1000;     QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------------------  Gather  (cost=30041.99..2379226.30 rows=990000 width=98) (actual time=0.512..6261.801 rows=1000000.00 loops=1)    Workers Planned: 2    Workers Launched: 2    Buffers: shared hit=18285 read=1705870    ->  Hash Join  (cost=41.99..2250226.30 rows=412500 width=98) (actual time=158.814..4363.611 rows=333333.33 loops=3)          Hash Cond: (t1.id0 = t0.id0)          Buffers: shared hit=18285 read=1705870          ->  Parallel Seq Scan on tab1 t1 (cost=0.00..2140804.67 rows=41666667 width=98) (actual time=0.055..1830.481 rows=33333333.33 loops=3)                Buffers: shared hit=18268 read=1705870          ->  Hash  (cost=29.62..29.62 rows=990 width=4) (actual time=0.234..0.235 rows=1000.00 loops=3)                Buckets: 1024  Batches: 1  Memory Usage: 44kB                Buffers: shared hit=17                ->  Index Only Scan using tab0_pkey on tab0 t0  (cost=0.29..29.62 rows=990 width=4) (actual time=0.029..0.119 rows=1000.00 loops=3)                      Index Cond: (id0 < 1000)                      Heap Fetches: 0                      Index Searches: 3                      Buffers: shared hit=17  Planning:    Buffers: shared hit=222  Planning Time: 1.031 ms  Execution Time: 6293.675 ms (21 rows) This seems like a pretty horrible plan....I'm wondering why it is not eliminating the vast majority of rows from tab0 1st in some way. After some head scratching I wondered if *in this case* I really needed to tell the planner I was on NVMe: test0=# SET random_page_cost=1; SET test0=# EXPLAIN ANALYZE SELECT t0.id0, t1.val                 FROM tab0 AS t0                 JOIN tab1 AS t1 ON (t0.id0 = t1.id0)                 WHERE t0.id0 < 1000;     QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------------------  Gather  (cost=30000.00..1668544.54 rows=990000 width=98) (actual time=0.198..163.617 rows=1000000.00 loops=1)    Workers Planned: 1    Workers Launched: 1    Buffers: shared hit=32141    ->  Nested Loop  (cost=0.00..1539544.54 rows=582353 width=98) (actual time=0.027..110.626 rows=500000.00 loops=2)          Buffers: shared hit=32141          ->  Parallel Seq Scan on tab0 t0 (cost=0.00..2375.29 rows=582 width=4) (actual time=0.005..2.689 rows=500.00 loops=2)                Filter: (id0 < 1000)                Rows Removed by Filter: 49500                Buffers: shared hit=1640          ->  Index Scan using tab1_id0_hash on tab1 t1 (cost=0.00..2608.85 rows=3233 width=98) (actual time=0.003..0.153 rows=1000.00 loops=1000)                Index Cond: (id0 = t0.id0)                Index Searches: 1000                Buffers: shared hit=30501  Planning:    Buffers: shared hit=9  Planning Time: 0.189 ms  Execution Time: 191.870 ms (18 rows) That is more like it! So ahem, note to self: remember to tell the planner you are using storage where random access is just (or nearly) as fast as sequential. Now I can get the planner to use the index on tab0 by making parallel operation more expensive: test0=# SET parallel_setup_cost=1000000; SET test0=# EXPLAIN ANALYZE SELECT t0.id0, t1.val                 FROM tab0 AS t0                 JOIN tab1 AS t1 ON (t0.id0 = t1.id0)                 WHERE t0.id0 < 1000;  QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------------  Nested Loop  (cost=0.29..2614793.04 rows=990000 width=98) (actual time=0.027..205.270 rows=1000000.00 loops=1)    Buffers: shared hit=30505    ->  Index Only Scan using tab0_pkey on tab0 t0 (cost=0.29..20.62 rows=990 width=4) (actual time=0.014..0.147 rows=1000.00 loops=1)          Index Cond: (id0 < 1000)          Heap Fetches: 0          Index Searches: 1          Buffers: shared hit=5    ->  Index Scan using tab1_id0_hash on tab1 t1 (cost=0.00..2608.85 rows=3233 width=98) (actual time=0.003..0.143 rows=1000.00 loops=1000)          Index Cond: (id0 = t0.id0)          Index Searches: 1000          Buffers: shared hit=30500  Planning:    Buffers: shared hit=9  Planning Time: 0.182 ms  Execution Time: 232.360 ms (15 rows) But the parallel plan is better! I guess one question to consider is: is the slightly faster parallel plan way more expensive in terms of resources? It seems likely that it is, so if you had a system that needed to run many instances of this type of query would you be better off discouraging parallel execution of it? Anyway, I found this an interesting exercise, hopefully others might too. regards Mark