{
  "version": 1,
  "source": "blogging/postgres_graph/PG19_GRAPHS_PART1_JSON.md",
  "note": "Supplemental data for pg19-graph-queries-part-1.mdx. mcp-call blocks render as ExoBench tool cards (verbatim tool-result JSON from a local-mode run against a PostgreSQL 19beta1 image, ExoBench server 1.3.0, correlationId bd2f90b0-d33e-4aad-8d72-8ed5c31f9a1d). chart blocks render as marketing charts. Result strings are byte-verbatim from the raw capture; input schemaTemplate/query/scalePoints are the reproduction payloads.",
  "blocks": {
    "fixedJoin": {
      "type": "mcp-call",
      "name": "benchmarkSql",
      "input": {
        "schemaTemplate": "CREATE TABLE load_balancers (id INT PRIMARY KEY);\nINSERT INTO load_balancers SELECT gs FROM generate_series(1, ${LBS}) gs;\nCREATE TABLE ingresses (id INT PRIMARY KEY);\nINSERT INTO ingresses SELECT gs FROM generate_series(1, 3*${LBS}) gs;\nCREATE TABLE clusters (id INT PRIMARY KEY);\nINSERT INTO clusters SELECT gs FROM generate_series(1, 9*${LBS}) gs;\nCREATE TABLE shard_servers (id INT PRIMARY KEY);\nINSERT INTO shard_servers SELECT gs FROM generate_series(1, 27*${LBS}) gs;\nCREATE TABLE shard_nodes (id INT PRIMARY KEY);\nINSERT INTO shard_nodes SELECT gs FROM generate_series(1, 81*${LBS}) gs;\n\nCREATE TABLE routes (id INT PRIMARY KEY, src INT REFERENCES load_balancers(id), dst INT REFERENCES ingresses(id));\nINSERT INTO routes SELECT gs, ((gs-1)/3)+1, gs FROM generate_series(1, 3*${LBS}) gs;\nCREATE TABLE dispatches (id INT PRIMARY KEY, src INT REFERENCES ingresses(id), dst INT REFERENCES clusters(id));\nINSERT INTO dispatches SELECT gs, ((gs-1)/3)+1, gs FROM generate_series(1, 9*${LBS}) gs;\nCREATE TABLE shards (id INT PRIMARY KEY, src INT REFERENCES clusters(id), dst INT REFERENCES shard_servers(id));\nINSERT INTO shards SELECT gs, ((gs-1)/3)+1, gs FROM generate_series(1, 27*${LBS}) gs;\nCREATE TABLE replicates (id INT PRIMARY KEY, src INT REFERENCES shard_servers(id), dst INT REFERENCES shard_nodes(id));\nINSERT INTO replicates SELECT gs, ((gs-1)/3)+1, gs FROM generate_series(1, 81*${LBS}) gs;\n\nCREATE INDEX ON routes(src);\nCREATE INDEX ON dispatches(src);\nCREATE INDEX ON shards(src);\nCREATE INDEX ON replicates(src);\n\nCREATE PROPERTY GRAPH infra\n  VERTEX TABLES (\n    load_balancers KEY (id) LABEL LoadBalancer PROPERTIES (id),\n    ingresses      KEY (id) LABEL Ingress PROPERTIES (id),\n    clusters       KEY (id) LABEL IndexCluster PROPERTIES (id),\n    shard_servers  KEY (id) LABEL IndexShardServer PROPERTIES (id),\n    shard_nodes    KEY (id) LABEL IndexShardNode PROPERTIES (id)\n  )\n  EDGE TABLES (\n    routes     KEY (id) SOURCE KEY (src) REFERENCES load_balancers (id) DESTINATION KEY (dst) REFERENCES ingresses (id)     LABEL routes,\n    dispatches KEY (id) SOURCE KEY (src) REFERENCES ingresses (id)      DESTINATION KEY (dst) REFERENCES clusters (id)      LABEL dispatches,\n    shards     KEY (id) SOURCE KEY (src) REFERENCES clusters (id)       DESTINATION KEY (dst) REFERENCES shard_servers (id) LABEL shards,\n    replicates KEY (id) SOURCE KEY (src) REFERENCES shard_servers (id)  DESTINATION KEY (dst) REFERENCES shard_nodes (id)   LABEL replicates\n  );\n\nVACUUM ANALYZE;",
        "query": "EXPLAIN ANALYZE\nSELECT sn.id AS node_id\nFROM load_balancers lb\nJOIN routes r      ON r.src = lb.id\nJOIN ingresses ig  ON ig.id = r.dst\nJOIN dispatches d  ON d.src = ig.id\nJOIN clusters cl   ON cl.id = d.dst\nJOIN shards s      ON s.src = cl.id\nJOIN shard_servers ss ON ss.id = s.dst\nJOIN replicates rp ON rp.src = ss.id\nJOIN shard_nodes sn ON sn.id = rp.dst\nWHERE lb.id = 1",
        "scalePoints": [
          {
            "LBS": 2500
          },
          {
            "LBS": 5000
          },
          {
            "LBS": 10000
          }
        ],
        "databaseType": "POSTGRES_19",
        "warmCache": "true",
        "version": "1.3.0"
      },
      "result": "{\"timestamp\":\"2026-07-08T21:39:45.296662125Z\",\"databaseType\":\"postgres\",\"sql\":\"EXPLAIN ANALYZE\\nSELECT sn.id AS node_id\\nFROM load_balancers lb\\nJOIN routes r      ON r.src = lb.id\\nJOIN ingresses ig  ON ig.id = r.dst\\nJOIN dispatches d  ON d.src = ig.id\\nJOIN clusters cl   ON cl.id = d.dst\\nJOIN shards s      ON s.src = cl.id\\nJOIN shard_servers ss ON ss.id = s.dst\\nJOIN replicates rp ON rp.src = ss.id\\nJOIN shard_nodes sn ON sn.id = rp.dst\\nWHERE lb.id = 1\",\"warmCacheIterations\":2,\"scalePointCount\":3,\"totalComputeSeconds\":16.581,\"results\":[{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"LBS\":2500},\"output\":\"QUERY PLAN\\nNested Loop  (cost=2.84..94.94 rows=81 width=4) (actual time=0.037..0.210 rows=81.00 loops=1)\\n  Buffers: shared hit=439\\n  ->  Nested Loop  (cost=2.42..58.61 rows=81 width=4) (actual time=0.034..0.126 rows=81.00 loops=1)\\n        Buffers: shared hit=195\\n        ->  Nested Loop  (cost=2.00..42.64 rows=27 width=8) (actual time=0.030..0.081 rows=27.00 loops=1)\\n              Buffers: shared hit=113\\n              ->  Nested Loop  (cost=1.71..33.97 rows=27 width=4) (actual time=0.027..0.053 rows=27.00 loops=1)\\n                    Buffers: shared hit=58\\n                    ->  Nested Loop  (cost=1.42..29.79 rows=9 width=8) (actual time=0.023..0.038 rows=9.00 loops=1)\\n                          Buffers: shared hit=39\\n                          ->  Nested Loop  (cost=1.13..26.94 rows=9 width=4) (actual time=0.020..0.027 rows=9.00 loops=1)\\n                                Buffers: shared hit=20\\n                                ->  Nested Loop  (cost=0.84..25.56 rows=3 width=8) (actual time=0.016..0.019 rows=3.00 loops=1)\\n                                      Buffers: shared hit=13\\n                                      ->  Nested Loop  (cost=0.56..12.66 rows=3 width=4) (actual time=0.012..0.013 rows=3.00 loops=1)\\n                                            Buffers: shared hit=6\\n                                            ->  Index Only Scan using load_balancers_pkey on load_balancers lb  (cost=0.28..4.30 rows=1 width=4) (actual time=0.006..0.007 rows=1.00 loops=1)\\n                                                  Index Cond: (id = 1)\\n                                                  Heap Fetches: 0\\n                                                  Index Searches: 1\\n                                                  Buffers: shared hit=3\\n                                            ->  Index Scan using routes_src_idx on routes r  (cost=0.28..8.34 rows=3 width=8) (actual time=0.004..0.004 rows=3.00 loops=1)\\n                                                  Index Cond: (src = 1)\\n                                                  Index Searches: 1\\n                                                  Buffers: shared hit=3\\n                                      ->  Index Only Scan using ingresses_pkey on ingresses ig  (cost=0.28..4.30 rows=1 width=4) (actual time=0.002..0.002 rows=1.00 loops=3)\\n                                            Index Cond: (id = r.dst)\\n                                            Heap Fetches: 0\\n                                            Index Searches: 3\\n                                            Buffers: shared hit=7\\n                                ->  Index Scan using dispatches_src_idx on dispatches d  (cost=0.29..0.43 rows=3 width=8) (actual time=0.002..0.002 rows=3.00 loops=3)\\n                                      Index Cond: (src = ig.id)\\n                                      Index Searches: 3\\n                                      Buffers: shared hit=7\\n                          ->  Index Only Scan using clusters_pkey on clusters cl  (cost=0.29..0.32 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=9)\\n                                Index Cond: (id = d.dst)\\n                                Heap Fetches: 0\\n                                Index Searches: 9\\n                                Buffers: shared hit=19\\n                    ->  Index Scan using shards_src_idx on shards s  (cost=0.29..0.43 rows=3 width=8) (actual time=0.001..0.001 rows=3.00 loops=9)\\n                          Index Cond: (src = cl.id)\\n                          Index Searches: 9\\n                          Buffers: shared hit=19\\n              ->  Index Only Scan using shard_servers_pkey on shard_servers ss  (cost=0.29..0.32 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=27)\\n                    Index Cond: (id = s.dst)\\n                    Heap Fetches: 0\\n                    Index Searches: 27\\n                    Buffers: shared hit=55\\n        ->  Index Scan using replicates_src_idx on replicates rp  (cost=0.42..0.56 rows=3 width=8) (actual time=0.001..0.001 rows=3.00 loops=27)\\n              Index Cond: (src = ss.id)\\n              Index Searches: 27\\n              Buffers: shared hit=82\\n  ->  Index Only Scan using shard_nodes_pkey on shard_nodes sn  (cost=0.42..0.45 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=81)\\n        Index Cond: (id = rp.dst)\\n        Heap Fetches: 0\\n        Index Searches: 81\\n        Buffers: shared hit=244\\nPlanning:\\n  Buffers: shared hit=84\\nPlanning Time: 3.340 ms\\nExecution Time: 0.252 ms\",\"runtimeDurationMs\":4,\"rowsAreCapped\":false,\"computeSeconds\":2.391},{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"LBS\":5000},\"output\":\"QUERY PLAN\\nNested Loop  (cost=2.87..95.33 rows=81 width=4) (actual time=0.026..0.207 rows=81.00 loops=1)\\n  Buffers: shared hit=439\\n  ->  Nested Loop  (cost=2.44..58.80 rows=81 width=4) (actual time=0.023..0.120 rows=81.00 loops=1)\\n        Buffers: shared hit=195\\n        ->  Nested Loop  (cost=2.02..42.76 rows=27 width=8) (actual time=0.021..0.075 rows=27.00 loops=1)\\n              Buffers: shared hit=113\\n              ->  Nested Loop  (cost=1.73..34.03 rows=27 width=4) (actual time=0.019..0.048 rows=27.00 loops=1)\\n                    Buffers: shared hit=58\\n                    ->  Nested Loop  (cost=1.43..29.83 rows=9 width=8) (actual time=0.016..0.032 rows=9.00 loops=1)\\n                          Buffers: shared hit=39\\n                          ->  Nested Loop  (cost=1.14..26.96 rows=9 width=4) (actual time=0.014..0.022 rows=9.00 loops=1)\\n                                Buffers: shared hit=20\\n                                ->  Nested Loop  (cost=0.85..25.58 rows=3 width=8) (actual time=0.012..0.016 rows=3.00 loops=1)\\n                                      Buffers: shared hit=13\\n                                      ->  Nested Loop  (cost=0.57..12.67 rows=3 width=4) (actual time=0.010..0.011 rows=3.00 loops=1)\\n                                            Buffers: shared hit=6\\n                                            ->  Index Only Scan using load_balancers_pkey on load_balancers lb  (cost=0.28..4.30 rows=1 width=4) (actual time=0.005..0.005 rows=1.00 loops=1)\\n                                                  Index Cond: (id = 1)\\n                                                  Heap Fetches: 0\\n                                                  Index Searches: 1\\n                                                  Buffers: shared hit=3\\n                                            ->  Index Scan using routes_src_idx on routes r  (cost=0.29..8.34 rows=3 width=8) (actual time=0.004..0.004 rows=3.00 loops=1)\\n                                                  Index Cond: (src = 1)\\n                                                  Index Searches: 1\\n                                                  Buffers: shared hit=3\\n                                      ->  Index Only Scan using ingresses_pkey on ingresses ig  (cost=0.29..4.30 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=3)\\n                                            Index Cond: (id = r.dst)\\n                                            Heap Fetches: 0\\n                                            Index Searches: 3\\n                                            Buffers: shared hit=7\\n                                ->  Index Scan using dispatches_src_idx on dispatches d  (cost=0.29..0.43 rows=3 width=8) (actual time=0.001..0.002 rows=3.00 loops=3)\\n                                      Index Cond: (src = ig.id)\\n                                      Index Searches: 3\\n                                      Buffers: shared hit=7\\n                          ->  Index Only Scan using clusters_pkey on clusters cl  (cost=0.29..0.32 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=9)\\n                                Index Cond: (id = d.dst)\\n                                Heap Fetches: 0\\n                                Index Searches: 9\\n                                Buffers: shared hit=19\\n                    ->  Index Scan using shards_src_idx on shards s  (cost=0.29..0.44 rows=3 width=8) (actual time=0.001..0.001 rows=3.00 loops=9)\\n                          Index Cond: (src = cl.id)\\n                          Index Searches: 9\\n                          Buffers: shared hit=19\\n              ->  Index Only Scan using shard_servers_pkey on shard_servers ss  (cost=0.29..0.32 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=27)\\n                    Index Cond: (id = s.dst)\\n                    Heap Fetches: 0\\n                    Index Searches: 27\\n                    Buffers: shared hit=55\\n        ->  Index Scan using replicates_src_idx on replicates rp  (cost=0.42..0.56 rows=3 width=8) (actual time=0.001..0.001 rows=3.00 loops=27)\\n              Index Cond: (src = ss.id)\\n              Index Searches: 27\\n              Buffers: shared hit=82\\n  ->  Index Only Scan using shard_nodes_pkey on shard_nodes sn  (cost=0.42..0.45 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=81)\\n        Index Cond: (id = rp.dst)\\n        Heap Fetches: 0\\n        Index Searches: 81\\n        Buffers: shared hit=244\\nPlanning:\\n  Buffers: shared hit=84\\nPlanning Time: 2.426 ms\\nExecution Time: 0.241 ms\",\"runtimeDurationMs\":3,\"rowsAreCapped\":false,\"computeSeconds\":4.818},{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"LBS\":10000},\"output\":\"QUERY PLAN\\nNested Loop  (cost=3.14..100.23 rows=81 width=4) (actual time=0.035..0.218 rows=81.00 loops=1)\\n  Buffers: shared hit=475\\n  ->  Nested Loop  (cost=2.71..63.50 rows=81 width=4) (actual time=0.031..0.131 rows=81.00 loops=1)\\n        Buffers: shared hit=231\\n        ->  Nested Loop  (cost=2.29..47.39 rows=27 width=8) (actual time=0.028..0.085 rows=27.00 loops=1)\\n              Buffers: shared hit=149\\n              ->  Nested Loop  (cost=1.87..35.22 rows=27 width=4) (actual time=0.025..0.054 rows=27.00 loops=1)\\n                    Buffers: shared hit=67\\n                    ->  Nested Loop  (cost=1.44..29.87 rows=9 width=8) (actual time=0.021..0.037 rows=9.00 loops=1)\\n                          Buffers: shared hit=39\\n                          ->  Nested Loop  (cost=1.15..26.98 rows=9 width=4) (actual time=0.018..0.026 rows=9.00 loops=1)\\n                                Buffers: shared hit=20\\n                                ->  Nested Loop  (cost=0.86..25.59 rows=3 width=8) (actual time=0.015..0.019 rows=3.00 loops=1)\\n                                      Buffers: shared hit=13\\n                                      ->  Nested Loop  (cost=0.57..12.67 rows=3 width=4) (actual time=0.011..0.013 rows=3.00 loops=1)\\n                                            Buffers: shared hit=6\\n                                            ->  Index Only Scan using load_balancers_pkey on load_balancers lb  (cost=0.29..4.30 rows=1 width=4) (actual time=0.005..0.006 rows=1.00 loops=1)\\n                                                  Index Cond: (id = 1)\\n                                                  Heap Fetches: 0\\n                                                  Index Searches: 1\\n                                                  Buffers: shared hit=3\\n                                            ->  Index Scan using routes_src_idx on routes r  (cost=0.29..8.34 rows=3 width=8) (actual time=0.005..0.005 rows=3.00 loops=1)\\n                                                  Index Cond: (src = 1)\\n                                                  Index Searches: 1\\n                                                  Buffers: shared hit=3\\n                                      ->  Index Only Scan using ingresses_pkey on ingresses ig  (cost=0.29..4.30 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=3)\\n                                            Index Cond: (id = r.dst)\\n                                            Heap Fetches: 0\\n                                            Index Searches: 3\\n                                            Buffers: shared hit=7\\n                                ->  Index Scan using dispatches_src_idx on dispatches d  (cost=0.29..0.43 rows=3 width=8) (actual time=0.002..0.002 rows=3.00 loops=3)\\n                                      Index Cond: (src = ig.id)\\n                                      Index Searches: 3\\n                                      Buffers: shared hit=7\\n                          ->  Index Only Scan using clusters_pkey on clusters cl  (cost=0.29..0.32 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=9)\\n                                Index Cond: (id = d.dst)\\n                                Heap Fetches: 0\\n                                Index Searches: 9\\n                                Buffers: shared hit=19\\n                    ->  Index Scan using shards_src_idx on shards s  (cost=0.42..0.56 rows=3 width=8) (actual time=0.001..0.002 rows=3.00 loops=9)\\n                          Index Cond: (src = cl.id)\\n                          Index Searches: 9\\n                          Buffers: shared hit=28\\n              ->  Index Only Scan using shard_servers_pkey on shard_servers ss  (cost=0.42..0.45 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=27)\\n                    Index Cond: (id = s.dst)\\n                    Heap Fetches: 0\\n                    Index Searches: 27\\n                    Buffers: shared hit=82\\n        ->  Index Scan using replicates_src_idx on replicates rp  (cost=0.42..0.57 rows=3 width=8) (actual time=0.001..0.001 rows=3.00 loops=27)\\n              Index Cond: (src = ss.id)\\n              Index Searches: 27\\n              Buffers: shared hit=82\\n  ->  Index Only Scan using shard_nodes_pkey on shard_nodes sn  (cost=0.42..0.45 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=81)\\n        Index Cond: (id = rp.dst)\\n        Heap Fetches: 0\\n        Index Searches: 81\\n        Buffers: shared hit=244\\nPlanning:\\n  Buffers: shared hit=92\\nPlanning Time: 2.965 ms\\nExecution Time: 0.264 ms\",\"runtimeDurationMs\":4,\"rowsAreCapped\":false,\"computeSeconds\":9.372}],\"workerId\":\"75a4a222-bb70-4ca7-b829-368ff18d07f1\",\"correlationId\":\"bd2f90b0-d33e-4aad-8d72-8ed5c31f9a1d\",\"correlationIdInstruction\":\"Echo correlationId on subsequent ExoBench tool calls. Tracking only — it does NOT preserve DB state.\",\"version\":\"1.3.0\"}",
      "isError": false,
      "caption": "Fixed 4-hop typed chain as a hand-written join, PostgreSQL 19beta1",
      "planCaptions": {
        "2.5K": "The fixed 4-hop typed chain written by hand as a chain of joins across per-type edge tables. PostgreSQL 19beta1 plans it as stacked Nested Loop joins over Index Only Scan and Index Scan, 439 shared buffer hits for 81 result rows. Execution Time 0.252 ms at 2,500 load balancers.",
        "5K": "Doubling to 5,000 load balancers changes neither the plan nor its cost: the same Nested Loop stack, the same 439 shared buffer hits, Execution Time 0.241 ms. A fixed-depth chain reads only the rows on the path, so table size does not enter into it.",
        "10K": "At 10,000 load balancers the hand-written join is still Nested Loop over Index Only Scan and Index Scan, 475 shared buffer hits, Execution Time 0.264 ms. Flat from 2,500 to 10,000 because B-tree depth, not row count, sets the work."
      }
    },
    "fixedGraph": {
      "type": "mcp-call",
      "name": "benchmarkSql",
      "input": {
        "schemaTemplate": "CREATE TABLE load_balancers (id INT PRIMARY KEY);\nINSERT INTO load_balancers SELECT gs FROM generate_series(1, ${LBS}) gs;\nCREATE TABLE ingresses (id INT PRIMARY KEY);\nINSERT INTO ingresses SELECT gs FROM generate_series(1, 3*${LBS}) gs;\nCREATE TABLE clusters (id INT PRIMARY KEY);\nINSERT INTO clusters SELECT gs FROM generate_series(1, 9*${LBS}) gs;\nCREATE TABLE shard_servers (id INT PRIMARY KEY);\nINSERT INTO shard_servers SELECT gs FROM generate_series(1, 27*${LBS}) gs;\nCREATE TABLE shard_nodes (id INT PRIMARY KEY);\nINSERT INTO shard_nodes SELECT gs FROM generate_series(1, 81*${LBS}) gs;\n\nCREATE TABLE routes (id INT PRIMARY KEY, src INT REFERENCES load_balancers(id), dst INT REFERENCES ingresses(id));\nINSERT INTO routes SELECT gs, ((gs-1)/3)+1, gs FROM generate_series(1, 3*${LBS}) gs;\nCREATE TABLE dispatches (id INT PRIMARY KEY, src INT REFERENCES ingresses(id), dst INT REFERENCES clusters(id));\nINSERT INTO dispatches SELECT gs, ((gs-1)/3)+1, gs FROM generate_series(1, 9*${LBS}) gs;\nCREATE TABLE shards (id INT PRIMARY KEY, src INT REFERENCES clusters(id), dst INT REFERENCES shard_servers(id));\nINSERT INTO shards SELECT gs, ((gs-1)/3)+1, gs FROM generate_series(1, 27*${LBS}) gs;\nCREATE TABLE replicates (id INT PRIMARY KEY, src INT REFERENCES shard_servers(id), dst INT REFERENCES shard_nodes(id));\nINSERT INTO replicates SELECT gs, ((gs-1)/3)+1, gs FROM generate_series(1, 81*${LBS}) gs;\n\nCREATE INDEX ON routes(src);\nCREATE INDEX ON dispatches(src);\nCREATE INDEX ON shards(src);\nCREATE INDEX ON replicates(src);\n\nCREATE PROPERTY GRAPH infra\n  VERTEX TABLES (\n    load_balancers KEY (id) LABEL LoadBalancer PROPERTIES (id),\n    ingresses      KEY (id) LABEL Ingress PROPERTIES (id),\n    clusters       KEY (id) LABEL IndexCluster PROPERTIES (id),\n    shard_servers  KEY (id) LABEL IndexShardServer PROPERTIES (id),\n    shard_nodes    KEY (id) LABEL IndexShardNode PROPERTIES (id)\n  )\n  EDGE TABLES (\n    routes     KEY (id) SOURCE KEY (src) REFERENCES load_balancers (id) DESTINATION KEY (dst) REFERENCES ingresses (id)     LABEL routes,\n    dispatches KEY (id) SOURCE KEY (src) REFERENCES ingresses (id)      DESTINATION KEY (dst) REFERENCES clusters (id)      LABEL dispatches,\n    shards     KEY (id) SOURCE KEY (src) REFERENCES clusters (id)       DESTINATION KEY (dst) REFERENCES shard_servers (id) LABEL shards,\n    replicates KEY (id) SOURCE KEY (src) REFERENCES shard_servers (id)  DESTINATION KEY (dst) REFERENCES shard_nodes (id)   LABEL replicates\n  );\n\nVACUUM ANALYZE;",
        "query": "EXPLAIN ANALYZE\nSELECT node_id\nFROM GRAPH_TABLE (infra\n  MATCH (lb IS LoadBalancer WHERE lb.id = 1)\n        -[IS routes]->(ig IS Ingress)\n        -[IS dispatches]->(cl IS IndexCluster)\n        -[IS shards]->(ss IS IndexShardServer)\n        -[IS replicates]->(sn IS IndexShardNode)\n  COLUMNS (sn.id AS node_id)\n)",
        "scalePoints": [
          {
            "LBS": 2500
          },
          {
            "LBS": 5000
          },
          {
            "LBS": 10000
          }
        ],
        "databaseType": "POSTGRES_19",
        "warmCache": "true",
        "version": "1.3.0"
      },
      "result": "{\"timestamp\":\"2026-07-08T21:40:39.171789467Z\",\"databaseType\":\"postgres\",\"sql\":\"EXPLAIN ANALYZE\\nSELECT node_id\\nFROM GRAPH_TABLE (infra\\n  MATCH (lb IS LoadBalancer WHERE lb.id = 1)\\n        -[IS routes]->(ig IS Ingress)\\n        -[IS dispatches]->(cl IS IndexCluster)\\n        -[IS shards]->(ss IS IndexShardServer)\\n        -[IS replicates]->(sn IS IndexShardNode)\\n  COLUMNS (sn.id AS node_id)\\n)\",\"warmCacheIterations\":2,\"scalePointCount\":3,\"totalComputeSeconds\":16.181,\"results\":[{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"LBS\":2500},\"output\":\"QUERY PLAN\\nNested Loop  (cost=2.84..94.94 rows=81 width=4) (actual time=0.066..0.410 rows=81.00 loops=1)\\n  Buffers: shared hit=439\\n  ->  Nested Loop  (cost=2.42..58.61 rows=81 width=4) (actual time=0.058..0.236 rows=81.00 loops=1)\\n        Buffers: shared hit=195\\n        ->  Nested Loop  (cost=2.00..42.64 rows=27 width=8) (actual time=0.052..0.154 rows=27.00 loops=1)\\n              Buffers: shared hit=113\\n              ->  Nested Loop  (cost=1.71..33.97 rows=27 width=4) (actual time=0.047..0.103 rows=27.00 loops=1)\\n                    Buffers: shared hit=58\\n                    ->  Nested Loop  (cost=1.42..29.79 rows=9 width=8) (actual time=0.041..0.072 rows=9.00 loops=1)\\n                          Buffers: shared hit=39\\n                          ->  Nested Loop  (cost=1.13..26.94 rows=9 width=4) (actual time=0.035..0.051 rows=9.00 loops=1)\\n                                Buffers: shared hit=20\\n                                ->  Nested Loop  (cost=0.84..25.56 rows=3 width=8) (actual time=0.030..0.038 rows=3.00 loops=1)\\n                                      Buffers: shared hit=13\\n                                      ->  Nested Loop  (cost=0.56..12.66 rows=3 width=4) (actual time=0.024..0.027 rows=3.00 loops=1)\\n                                            Buffers: shared hit=6\\n                                            ->  Index Only Scan using load_balancers_pkey on load_balancers  (cost=0.28..4.30 rows=1 width=4) (actual time=0.016..0.017 rows=1.00 loops=1)\\n                                                  Index Cond: (id = 1)\\n                                                  Heap Fetches: 0\\n                                                  Index Searches: 1\\n                                                  Buffers: shared hit=3\\n                                            ->  Index Scan using routes_src_idx on routes  (cost=0.28..8.34 rows=3 width=8) (actual time=0.006..0.007 rows=3.00 loops=1)\\n                                                  Index Cond: (src = 1)\\n                                                  Index Searches: 1\\n                                                  Buffers: shared hit=3\\n                                      ->  Index Only Scan using ingresses_pkey on ingresses  (cost=0.28..4.30 rows=1 width=4) (actual time=0.002..0.002 rows=1.00 loops=3)\\n                                            Index Cond: (id = routes.dst)\\n                                            Heap Fetches: 0\\n                                            Index Searches: 3\\n                                            Buffers: shared hit=7\\n                                ->  Index Scan using dispatches_src_idx on dispatches  (cost=0.29..0.43 rows=3 width=8) (actual time=0.003..0.003 rows=3.00 loops=3)\\n                                      Index Cond: (src = ingresses.id)\\n                                      Index Searches: 3\\n                                      Buffers: shared hit=7\\n                          ->  Index Only Scan using clusters_pkey on clusters  (cost=0.29..0.32 rows=1 width=4) (actual time=0.002..0.002 rows=1.00 loops=9)\\n                                Index Cond: (id = dispatches.dst)\\n                                Heap Fetches: 0\\n                                Index Searches: 9\\n                                Buffers: shared hit=19\\n                    ->  Index Scan using shards_src_idx on shards  (cost=0.29..0.43 rows=3 width=8) (actual time=0.002..0.003 rows=3.00 loops=9)\\n                          Index Cond: (src = clusters.id)\\n                          Index Searches: 9\\n                          Buffers: shared hit=19\\n              ->  Index Only Scan using shard_servers_pkey on shard_servers  (cost=0.29..0.32 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=27)\\n                    Index Cond: (id = shards.dst)\\n                    Heap Fetches: 0\\n                    Index Searches: 27\\n                    Buffers: shared hit=55\\n        ->  Index Scan using replicates_src_idx on replicates  (cost=0.42..0.56 rows=3 width=8) (actual time=0.002..0.002 rows=3.00 loops=27)\\n              Index Cond: (src = shard_servers.id)\\n              Index Searches: 27\\n              Buffers: shared hit=82\\n  ->  Index Only Scan using shard_nodes_pkey on shard_nodes  (cost=0.42..0.45 rows=1 width=4) (actual time=0.002..0.002 rows=1.00 loops=81)\\n        Index Cond: (id = replicates.dst)\\n        Heap Fetches: 0\\n        Index Searches: 81\\n        Buffers: shared hit=244\\nPlanning:\\n  Buffers: shared hit=84\\nPlanning Time: 5.436 ms\\nExecution Time: 0.479 ms\",\"runtimeDurationMs\":7,\"rowsAreCapped\":false,\"computeSeconds\":2.396},{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"LBS\":5000},\"output\":\"QUERY PLAN\\nNested Loop  (cost=2.87..95.33 rows=81 width=4) (actual time=0.046..0.221 rows=81.00 loops=1)\\n  Buffers: shared hit=439\\n  ->  Nested Loop  (cost=2.44..58.80 rows=81 width=4) (actual time=0.041..0.133 rows=81.00 loops=1)\\n        Buffers: shared hit=195\\n        ->  Nested Loop  (cost=2.02..42.76 rows=27 width=8) (actual time=0.037..0.088 rows=27.00 loops=1)\\n              Buffers: shared hit=113\\n              ->  Nested Loop  (cost=1.73..34.03 rows=27 width=4) (actual time=0.033..0.061 rows=27.00 loops=1)\\n                    Buffers: shared hit=58\\n                    ->  Nested Loop  (cost=1.43..29.83 rows=9 width=8) (actual time=0.030..0.045 rows=9.00 loops=1)\\n                          Buffers: shared hit=39\\n                          ->  Nested Loop  (cost=1.14..26.96 rows=9 width=4) (actual time=0.027..0.034 rows=9.00 loops=1)\\n                                Buffers: shared hit=20\\n                                ->  Nested Loop  (cost=0.85..25.58 rows=3 width=8) (actual time=0.022..0.026 rows=3.00 loops=1)\\n                                      Buffers: shared hit=13\\n                                      ->  Nested Loop  (cost=0.57..12.67 rows=3 width=4) (actual time=0.019..0.020 rows=3.00 loops=1)\\n                                            Buffers: shared hit=6\\n                                            ->  Index Only Scan using load_balancers_pkey on load_balancers  (cost=0.28..4.30 rows=1 width=4) (actual time=0.012..0.013 rows=1.00 loops=1)\\n                                                  Index Cond: (id = 1)\\n                                                  Heap Fetches: 0\\n                                                  Index Searches: 1\\n                                                  Buffers: shared hit=3\\n                                            ->  Index Scan using routes_src_idx on routes  (cost=0.29..8.34 rows=3 width=8) (actual time=0.004..0.005 rows=3.00 loops=1)\\n                                                  Index Cond: (src = 1)\\n                                                  Index Searches: 1\\n                                                  Buffers: shared hit=3\\n                                      ->  Index Only Scan using ingresses_pkey on ingresses  (cost=0.29..4.30 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=3)\\n                                            Index Cond: (id = routes.dst)\\n                                            Heap Fetches: 0\\n                                            Index Searches: 3\\n                                            Buffers: shared hit=7\\n                                ->  Index Scan using dispatches_src_idx on dispatches  (cost=0.29..0.43 rows=3 width=8) (actual time=0.002..0.002 rows=3.00 loops=3)\\n                                      Index Cond: (src = ingresses.id)\\n                                      Index Searches: 3\\n                                      Buffers: shared hit=7\\n                          ->  Index Only Scan using clusters_pkey on clusters  (cost=0.29..0.32 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=9)\\n                                Index Cond: (id = dispatches.dst)\\n                                Heap Fetches: 0\\n                                Index Searches: 9\\n                                Buffers: shared hit=19\\n                    ->  Index Scan using shards_src_idx on shards  (cost=0.29..0.44 rows=3 width=8) (actual time=0.001..0.001 rows=3.00 loops=9)\\n                          Index Cond: (src = clusters.id)\\n                          Index Searches: 9\\n                          Buffers: shared hit=19\\n              ->  Index Only Scan using shard_servers_pkey on shard_servers  (cost=0.29..0.32 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=27)\\n                    Index Cond: (id = shards.dst)\\n                    Heap Fetches: 0\\n                    Index Searches: 27\\n                    Buffers: shared hit=55\\n        ->  Index Scan using replicates_src_idx on replicates  (cost=0.42..0.56 rows=3 width=8) (actual time=0.001..0.001 rows=3.00 loops=27)\\n              Index Cond: (src = shard_servers.id)\\n              Index Searches: 27\\n              Buffers: shared hit=82\\n  ->  Index Only Scan using shard_nodes_pkey on shard_nodes  (cost=0.42..0.45 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=81)\\n        Index Cond: (id = replicates.dst)\\n        Heap Fetches: 0\\n        Index Searches: 81\\n        Buffers: shared hit=244\\nPlanning:\\n  Buffers: shared hit=84\\nPlanning Time: 5.649 ms\\nExecution Time: 0.266 ms\",\"runtimeDurationMs\":7,\"rowsAreCapped\":false,\"computeSeconds\":4.619},{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"LBS\":10000},\"output\":\"QUERY PLAN\\nNested Loop  (cost=3.14..100.23 rows=81 width=4) (actual time=0.049..0.239 rows=81.00 loops=1)\\n  Buffers: shared hit=475\\n  ->  Nested Loop  (cost=2.71..63.50 rows=81 width=4) (actual time=0.044..0.148 rows=81.00 loops=1)\\n        Buffers: shared hit=231\\n        ->  Nested Loop  (cost=2.29..47.39 rows=27 width=8) (actual time=0.040..0.101 rows=27.00 loops=1)\\n              Buffers: shared hit=149\\n              ->  Nested Loop  (cost=1.87..35.22 rows=27 width=4) (actual time=0.035..0.067 rows=27.00 loops=1)\\n                    Buffers: shared hit=67\\n                    ->  Nested Loop  (cost=1.44..29.87 rows=9 width=8) (actual time=0.031..0.048 rows=9.00 loops=1)\\n                          Buffers: shared hit=39\\n                          ->  Nested Loop  (cost=1.15..26.98 rows=9 width=4) (actual time=0.027..0.036 rows=9.00 loops=1)\\n                                Buffers: shared hit=20\\n                                ->  Nested Loop  (cost=0.86..25.59 rows=3 width=8) (actual time=0.024..0.028 rows=3.00 loops=1)\\n                                      Buffers: shared hit=13\\n                                      ->  Nested Loop  (cost=0.57..12.67 rows=3 width=4) (actual time=0.019..0.020 rows=3.00 loops=1)\\n                                            Buffers: shared hit=6\\n                                            ->  Index Only Scan using load_balancers_pkey on load_balancers  (cost=0.29..4.30 rows=1 width=4) (actual time=0.013..0.014 rows=1.00 loops=1)\\n                                                  Index Cond: (id = 1)\\n                                                  Heap Fetches: 0\\n                                                  Index Searches: 1\\n                                                  Buffers: shared hit=3\\n                                            ->  Index Scan using routes_src_idx on routes  (cost=0.29..8.34 rows=3 width=8) (actual time=0.004..0.005 rows=3.00 loops=1)\\n                                                  Index Cond: (src = 1)\\n                                                  Index Searches: 1\\n                                                  Buffers: shared hit=3\\n                                      ->  Index Only Scan using ingresses_pkey on ingresses  (cost=0.29..4.30 rows=1 width=4) (actual time=0.002..0.002 rows=1.00 loops=3)\\n                                            Index Cond: (id = routes.dst)\\n                                            Heap Fetches: 0\\n                                            Index Searches: 3\\n                                            Buffers: shared hit=7\\n                                ->  Index Scan using dispatches_src_idx on dispatches  (cost=0.29..0.43 rows=3 width=8) (actual time=0.002..0.002 rows=3.00 loops=3)\\n                                      Index Cond: (src = ingresses.id)\\n                                      Index Searches: 3\\n                                      Buffers: shared hit=7\\n                          ->  Index Only Scan using clusters_pkey on clusters  (cost=0.29..0.32 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=9)\\n                                Index Cond: (id = dispatches.dst)\\n                                Heap Fetches: 0\\n                                Index Searches: 9\\n                                Buffers: shared hit=19\\n                    ->  Index Scan using shards_src_idx on shards  (cost=0.42..0.56 rows=3 width=8) (actual time=0.001..0.002 rows=3.00 loops=9)\\n                          Index Cond: (src = clusters.id)\\n                          Index Searches: 9\\n                          Buffers: shared hit=28\\n              ->  Index Only Scan using shard_servers_pkey on shard_servers  (cost=0.42..0.45 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=27)\\n                    Index Cond: (id = shards.dst)\\n                    Heap Fetches: 0\\n                    Index Searches: 27\\n                    Buffers: shared hit=82\\n        ->  Index Scan using replicates_src_idx on replicates  (cost=0.42..0.57 rows=3 width=8) (actual time=0.001..0.001 rows=3.00 loops=27)\\n              Index Cond: (src = shard_servers.id)\\n              Index Searches: 27\\n              Buffers: shared hit=82\\n  ->  Index Only Scan using shard_nodes_pkey on shard_nodes  (cost=0.42..0.45 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=81)\\n        Index Cond: (id = replicates.dst)\\n        Heap Fetches: 0\\n        Index Searches: 81\\n        Buffers: shared hit=244\\nPlanning:\\n  Buffers: shared hit=92\\nPlanning Time: 7.145 ms\\nExecution Time: 0.287 ms\",\"runtimeDurationMs\":8,\"rowsAreCapped\":false,\"computeSeconds\":9.166}],\"workerId\":\"1ce2e09c-7c34-4e46-9324-903c4cdafb73\",\"correlationId\":\"bd2f90b0-d33e-4aad-8d72-8ed5c31f9a1d\",\"correlationIdInstruction\":\"Echo correlationId on subsequent ExoBench tool calls. Tracking only — it does NOT preserve DB state.\",\"version\":\"1.3.0\"}",
      "isError": false,
      "caption": "Fixed 4-hop typed chain as a SQL/PGQ GRAPH_TABLE pattern, PostgreSQL 19beta1",
      "planCaptions": {
        "2.5K": "The same 4-hop traversal written as SQL/PGQ, GRAPH_TABLE (infra MATCH ...) on PostgreSQL 19beta1. The plan and its cost are identical to the hand-written join, Nested Loop over Index Only Scan and Index Scan with 439 shared hits, because GRAPH_TABLE rewrites into that join. Execution Time 0.479 ms at 2,500 load balancers.",
        "5K": "GRAPH_TABLE at 5,000 load balancers: the same Nested Loop plan, 439 shared hits, Execution Time 0.266 ms against 0.241 ms for the hand-written join. Planning Time 5.649 ms against 2.426 ms is the one consistent difference between the two forms.",
        "10K": "GRAPH_TABLE at 10,000 load balancers, Execution Time 0.287 ms against 0.264 ms for the join, same Nested Loop plan over 475 shared hits. Planning Time 7.145 ms is where the pattern-matching syntax costs something on PostgreSQL 19beta1."
      }
    },
    "cliffUnbounded": {
      "type": "mcp-call",
      "name": "runSql",
      "input": {
        "schema": "CREATE TABLE node (id INT PRIMARY KEY);\nINSERT INTO node SELECT i FROM generate_series(1,15) i;\n\nCREATE TABLE edge (id SERIAL PRIMARY KEY, src INT REFERENCES node(id), dst INT REFERENCES node(id));\nINSERT INTO edge (src, dst)\nSELECT i, 2*i FROM generate_series(1,15) i WHERE 2*i<=15\nUNION ALL SELECT i, 2*i+1 FROM generate_series(1,15) i WHERE 2*i+1<=15;\n\nCREATE PROPERTY GRAPH g\n  VERTEX TABLES ( node KEY (id) LABEL node PROPERTIES (id) )\n  EDGE TABLES ( edge KEY (id) SOURCE KEY (src) REFERENCES node(id) DESTINATION KEY (dst) REFERENCES node(id) LABEL link );",
        "query": "SELECT b_id\nFROM GRAPH_TABLE (g\n  MATCH (a IS node WHERE a.id = 1)-[IS link]->{1,}(b IS node)\n  COLUMNS (b.id AS b_id)\n)",
        "databaseType": "POSTGRES_19",
        "version": "1.3.0"
      },
      "result": "{\"error\":\"Query execution failed: ERROR: syntax error at or near \\\"}\\\"\\n  Position: 82\",\"sql\":\"SELECT b_id\\nFROM GRAPH_TABLE (g\\n  MATCH (a IS node WHERE a.id = 1)-[IS link]->{1,}(b IS node)\\n  COLUMNS (b.id AS b_id)\\n)\",\"computeSeconds\":0.09,\"warmCacheIterations\":0,\"feedbackHint\":\"If this result was unexpected, you can submit feedback using the submitFeedback tool with category 'bug'. Include the schema and query for reproduction.\",\"workerId\":\"832b2b68-49cf-4f23-bcfc-6e7702aece02\",\"correlationId\":\"bd2f90b0-d33e-4aad-8d72-8ed5c31f9a1d\",\"correlationIdInstruction\":\"Echo correlationId on subsequent ExoBench tool calls. Tracking only — it does NOT preserve DB state.\",\"version\":\"1.3.0\"}",
      "isError": true,
      "caption": "Unbounded SQL/PGQ quantifier {1,} on PostgreSQL 19beta1",
      "failingSqlCaption": "The unbounded quantifier -[IS link]->{1,} does not survive the parser. PostgreSQL 19beta1 answers 'syntax error at or near }' at position 82, so variable-depth SQL/PGQ traversal is not reachable in this beta by syntax alone."
    },
    "cliffBounded": {
      "type": "mcp-call",
      "name": "runSql",
      "input": {
        "schema": "CREATE TABLE node (id INT PRIMARY KEY);\nINSERT INTO node SELECT i FROM generate_series(1,15) i;\n\nCREATE TABLE edge (id SERIAL PRIMARY KEY, src INT REFERENCES node(id), dst INT REFERENCES node(id));\nINSERT INTO edge (src, dst)\nSELECT i, 2*i FROM generate_series(1,15) i WHERE 2*i<=15\nUNION ALL SELECT i, 2*i+1 FROM generate_series(1,15) i WHERE 2*i+1<=15;\n\nCREATE PROPERTY GRAPH g\n  VERTEX TABLES ( node KEY (id) LABEL node PROPERTIES (id) )\n  EDGE TABLES ( edge KEY (id) SOURCE KEY (src) REFERENCES node(id) DESTINATION KEY (dst) REFERENCES node(id) LABEL link );",
        "query": "SELECT b_id\nFROM GRAPH_TABLE (g\n  MATCH (a IS node WHERE a.id = 1)-[IS link]->{1,10}(b IS node)\n  COLUMNS (b.id AS b_id)\n)",
        "databaseType": "POSTGRES_19",
        "version": "1.3.0"
      },
      "result": "{\"error\":\"Query execution failed: ERROR: element pattern quantifier is not supported\",\"sql\":\"SELECT b_id\\nFROM GRAPH_TABLE (g\\n  MATCH (a IS node WHERE a.id = 1)-[IS link]->{1,10}(b IS node)\\n  COLUMNS (b.id AS b_id)\\n)\",\"computeSeconds\":0.066,\"warmCacheIterations\":0,\"feedbackHint\":\"If this result was unexpected, you can submit feedback using the submitFeedback tool with category 'bug'. Include the schema and query for reproduction.\",\"workerId\":\"e5951e0f-1114-4668-aab9-701798976212\",\"correlationId\":\"bd2f90b0-d33e-4aad-8d72-8ed5c31f9a1d\",\"correlationIdInstruction\":\"Echo correlationId on subsequent ExoBench tool calls. Tracking only — it does NOT preserve DB state.\",\"version\":\"1.3.0\"}",
      "isError": true,
      "caption": "Bounded SQL/PGQ quantifier {1,10} on PostgreSQL 19beta1",
      "failingSqlCaption": "Bounding the quantifier to {1,10} gets past the parser and fails one step later with 'element pattern quantifier is not supported'. PostgreSQL 19beta1 implements fixed-length GRAPH_TABLE patterns only, so a blast radius query still needs a recursive CTE."
    },
    "blastPlain": {
      "type": "mcp-call",
      "name": "benchmarkSql",
      "input": {
        "schemaTemplate": "CREATE TABLE nodes (id INT PRIMARY KEY);\nINSERT INTO nodes SELECT i FROM generate_series(1, ${NODES}) i;\nCREATE TABLE edges (id SERIAL PRIMARY KEY, src INT REFERENCES nodes(id), dst INT REFERENCES nodes(id));\nINSERT INTO edges (src, dst)\nSELECT i, 2*i FROM generate_series(1, ${NODES}) i WHERE 2*i <= ${NODES}\nUNION ALL\nSELECT i, 2*i+1 FROM generate_series(1, ${NODES}) i WHERE 2*i+1 <= ${NODES};\nCREATE INDEX ON edges(src);\nCREATE PROPERTY GRAPH g\n  VERTEX TABLES ( nodes KEY (id) LABEL node PROPERTIES (id) )\n  EDGE TABLES ( edges KEY (id) SOURCE KEY (src) REFERENCES nodes(id) DESTINATION KEY (dst) REFERENCES nodes(id) LABEL edge );\nVACUUM ANALYZE;",
        "query": "EXPLAIN ANALYZE\nWITH RECURSIVE reach AS (\n  SELECT dst AS b_id FROM edges WHERE src = 1\n  UNION\n  SELECT e.dst FROM edges e JOIN reach r ON e.src = r.b_id\n)\nSELECT count(*) FROM reach",
        "scalePoints": [
          {
            "NODES": 32767
          },
          {
            "NODES": 131071
          },
          {
            "NODES": 524287
          },
          {
            "NODES": 1048575
          }
        ],
        "databaseType": "POSTGRES_19",
        "warmCache": "true",
        "version": "1.3.0"
      },
      "result": "{\"timestamp\":\"2026-07-09T20:12:56.064602289Z\",\"databaseType\":\"postgres_19\",\"sql\":\"EXPLAIN ANALYZE\\nWITH RECURSIVE reach AS (\\n  SELECT dst AS b_id FROM edges WHERE src = 1\\n  UNION\\n  SELECT e.dst FROM edges e JOIN reach r ON e.src = r.b_id\\n)\\nSELECT count(*) FROM reach\",\"warmCacheIterations\":2,\"scalePointCount\":4,\"totalComputeSeconds\":25.247999999999998,\"results\":[{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"NODES\":32767},\"output\":\"QUERY PLAN\\nAggregate  (cost=2085.10..2085.11 rows=1 width=8) (actual time=58.902..58.904 rows=1.00 loops=1)\\n  Buffers: shared hit=98300\\n  CTE reach\\n    ->  Recursive Union  (cost=0.29..2076.05 rows=402 width=4) (actual time=0.011..52.107 rows=32766.00 loops=1)\\n          Storage: Memory  Maximum Storage: 1153kB\\n          Buffers: shared hit=98300\\n          ->  Index Scan using edges_src_idx on edges  (cost=0.29..11.31 rows=2 width=4) (actual time=0.010..0.012 rows=2.00 loops=1)\\n                Index Cond: (src = 1)\\n                Index Searches: 1\\n                Buffers: shared hit=4\\n          ->  Nested Loop  (cost=0.29..206.07 rows=40 width=4) (actual time=0.895..2.985 rows=2340.29 loops=14)\\n                Buffers: shared hit=98296\\n                ->  WorkTable Scan on reach r  (cost=0.00..0.40 rows=20 width=4) (actual time=0.000..0.079 rows=2340.43 loops=14)\\n                ->  Index Scan using edges_src_idx on edges e  (cost=0.29..10.26 rows=2 width=8) (actual time=0.001..0.001 rows=1.00 loops=32766)\\n                      Index Cond: (src = r.b_id)\\n                      Index Searches: 32766\\n                      Buffers: shared hit=98296\\n  ->  CTE Scan on reach  (cost=0.00..8.04 rows=402 width=0) (actual time=0.011..57.410 rows=32766.00 loops=1)\\n        Storage: Memory  Maximum Storage: 1280kB\\n        Buffers: shared hit=98300\\nPlanning Time: 0.174 ms\\nExecution Time: 59.147 ms\",\"runtimeDurationMs\":59,\"rowsAreCapped\":false,\"computeSeconds\":0.545},{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"NODES\":131071},\"output\":\"QUERY PLAN\\nAggregate  (cost=2291.88..2291.89 rows=1 width=8) (actual time=296.761..296.764 rows=1.00 loops=1)\\n  Buffers: shared hit=524283, temp written=223\\n  CTE reach\\n    ->  Recursive Union  (cost=0.42..2282.83 rows=402 width=4) (actual time=0.013..263.496 rows=131070.00 loops=1)\\n          Storage: Memory  Maximum Storage: 4404kB\\n          Buffers: shared hit=524283\\n          ->  Index Scan using edges_src_idx on edges  (cost=0.42..11.45 rows=2 width=4) (actual time=0.011..0.014 rows=2.00 loops=1)\\n                Index Cond: (src = 1)\\n                Index Searches: 1\\n                Buffers: shared hit=5\\n          ->  Nested Loop  (cost=0.42..226.74 rows=40 width=4) (actual time=5.057..13.536 rows=8191.75 loops=16)\\n                Buffers: shared hit=524278\\n                ->  WorkTable Scan on reach r  (cost=0.00..0.40 rows=20 width=4) (actual time=0.000..0.391 rows=8191.88 loops=16)\\n                ->  Index Scan using edges_src_idx on edges e  (cost=0.42..11.30 rows=2 width=8) (actual time=0.001..0.001 rows=1.00 loops=131070)\\n                      Index Cond: (src = r.b_id)\\n                      Index Searches: 131070\\n                      Buffers: shared hit=524278\\n  ->  CTE Scan on reach  (cost=0.00..8.04 rows=402 width=0) (actual time=0.013..290.714 rows=131070.00 loops=1)\\n        Storage: Disk  Maximum Storage: 4096kB\\n        Buffers: shared hit=524283, temp written=223\\nPlanning Time: 0.117 ms\\nExecution Time: 298.997 ms\",\"runtimeDurationMs\":299,\"rowsAreCapped\":false,\"computeSeconds\":2.133},{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"NODES\":524287},\"output\":\"QUERY PLAN\\nAggregate  (cost=2329.80..2329.81 rows=1 width=8) (actual time=1151.308..1151.310 rows=1.00 loops=1)\\n  Buffers: shared hit=2097147, temp read=672 written=1567\\n  CTE reach\\n    ->  Recursive Union  (cost=0.42..2321.21 rows=382 width=4) (actual time=0.013..1018.319 rows=524286.00 loops=1)\\n          Storage: Disk  Maximum Storage: 8192kB\\n          Buffers: shared hit=2097147, temp read=672 written=672\\n          ->  Index Scan using edges_src_idx on edges  (cost=0.42..11.49 rows=2 width=4) (actual time=0.011..0.014 rows=2.00 loops=1)\\n                Index Cond: (src = 1)\\n                Index Searches: 1\\n                Buffers: shared hit=5\\n          ->  Nested Loop  (cost=0.42..230.59 rows=38 width=4) (actual time=15.622..45.184 rows=29126.89 loops=18)\\n                Buffers: shared hit=2097142, temp read=672 written=2\\n                ->  WorkTable Scan on reach r  (cost=0.00..0.40 rows=20 width=4) (actual time=0.001..2.043 rows=29127.00 loops=18)\\n                      Buffers: temp read=672 written=2\\n                ->  Index Scan using edges_src_idx on edges e  (cost=0.42..11.49 rows=2 width=8) (actual time=0.001..0.001 rows=1.00 loops=524286)\\n                      Index Cond: (src = r.b_id)\\n                      Index Searches: 524286\\n                      Buffers: shared hit=2097142\\n  ->  CTE Scan on reach  (cost=0.00..7.64 rows=382 width=0) (actual time=0.013..1125.722 rows=524286.00 loops=1)\\n        Storage: Disk  Maximum Storage: 7160kB\\n        Buffers: shared hit=2097147, temp read=672 written=1567\\nPlanning Time: 0.122 ms\\nExecution Time: 1156.342 ms\",\"runtimeDurationMs\":1154,\"rowsAreCapped\":false,\"computeSeconds\":7.784},{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"NODES\":1048575},\"output\":\"QUERY PLAN\\nAggregate  (cost=2329.86..2329.87 rows=1 width=8) (actual time=2183.125..2183.126 rows=1.00 loops=1)\\n  Buffers: shared hit=4194299, temp read=1568 written=3359\\n  CTE reach\\n    ->  Recursive Union  (cost=0.42..2320.59 rows=412 width=4) (actual time=0.020..1944.067 rows=1048574.00 loops=1)\\n          Storage: Disk  Maximum Storage: 11264kB\\n          Buffers: shared hit=4194299, temp read=1568 written=1568\\n          ->  Index Scan using edges_src_idx on edges  (cost=0.42..11.48 rows=2 width=4) (actual time=0.017..0.020 rows=2.00 loops=1)\\n                Index Cond: (src = 1)\\n                Index Searches: 1\\n                Buffers: shared hit=5\\n          ->  Nested Loop  (cost=0.42..230.50 rows=41 width=4) (actual time=27.891..82.209 rows=55188.00 loops=19)\\n                Buffers: shared hit=4194294, temp read=1568 written=3\\n                ->  WorkTable Scan on reach r  (cost=0.00..0.40 rows=20 width=4) (actual time=0.002..3.934 rows=55188.11 loops=19)\\n                      Buffers: temp read=1568 written=3\\n                ->  Index Scan using edges_src_idx on edges e  (cost=0.42..11.48 rows=2 width=8) (actual time=0.001..0.001 rows=1.00 loops=1048574)\\n                      Index Cond: (src = r.b_id)\\n                      Index Searches: 1048574\\n                      Buffers: shared hit=4194294\\n  ->  CTE Scan on reach  (cost=0.00..8.24 rows=412 width=0) (actual time=0.020..2136.765 rows=1048574.00 loops=1)\\n        Storage: Disk  Maximum Storage: 14328kB\\n        Buffers: shared hit=4194299, temp read=1568 written=3359\\nPlanning Time: 0.184 ms\\nExecution Time: 2189.152 ms\",\"runtimeDurationMs\":2184,\"rowsAreCapped\":false,\"computeSeconds\":14.786}],\"workerId\":\"dc2b6af3-d2e1-4cfa-b333-0680538eaa97\",\"correlationId\":\"bd2f90b0-d33e-4aad-8d72-8ed5c31f9a1d\",\"correlationIdInstruction\":\"Echo correlationId on subsequent ExoBench tool calls. Tracking only \\u2014 it does NOT preserve DB state.\",\"version\":\"1.3.0\"}",
      "isError": false,
      "caption": "Blast radius as a plain recursive CTE",
      "planCaptions": {
        "32.8K": "Blast radius as a plain WITH RECURSIVE CTE over an edges table. Recursive Union driving Index Scan on edges_src_idx inside a Nested Loop, worktable Storage: Memory at 1,153kB, 98,300 shared buffer hits to reach 32,766 nodes. Execution Time 59.147 ms.",
        "131.1K": "At 131,070 reachable nodes the recursive worktable still fits in memory at 4,404kB but starts writing 223 temp blocks, 524,283 shared hits, Execution Time 298.997 ms. Roughly linear in the size of the reachable set.",
        "524.3K": "At 524,286 nodes the worktable switches to Storage: Disk at 8,192kB with temp read 672 and written 1,567 blocks, 2,097,147 shared hits, Execution Time 1156.342 ms. The spill is where the curve starts to bend.",
        "1M": "At 1,048,574 nodes the recursive CTE reads 4,194,299 shared buffers with its worktable on disk at 11,264kB, Execution Time 2189.152 ms. This is the floor every graph syntax in the post is measured against."
      }
    },
    "blastGraph": {
      "type": "mcp-call",
      "name": "benchmarkSql",
      "input": {
        "schemaTemplate": "CREATE TABLE nodes (id INT PRIMARY KEY);\nINSERT INTO nodes SELECT i FROM generate_series(1, ${NODES}) i;\nCREATE TABLE edges (id SERIAL PRIMARY KEY, src INT REFERENCES nodes(id), dst INT REFERENCES nodes(id));\nINSERT INTO edges (src, dst)\nSELECT i, 2*i FROM generate_series(1, ${NODES}) i WHERE 2*i <= ${NODES}\nUNION ALL\nSELECT i, 2*i+1 FROM generate_series(1, ${NODES}) i WHERE 2*i+1 <= ${NODES};\nCREATE INDEX ON edges(src);\nCREATE PROPERTY GRAPH g\n  VERTEX TABLES ( nodes KEY (id) LABEL node PROPERTIES (id) )\n  EDGE TABLES ( edges KEY (id) SOURCE KEY (src) REFERENCES nodes(id) DESTINATION KEY (dst) REFERENCES nodes(id) LABEL edge );\nVACUUM ANALYZE;",
        "query": "EXPLAIN ANALYZE\nWITH RECURSIVE reach AS (\n  SELECT b_id FROM GRAPH_TABLE (g\n    MATCH (a IS node WHERE a.id = 1)-[x IS edge]->(b IS node)\n    COLUMNS (b.id AS b_id))\n  UNION\n  SELECT e.b_id\n  FROM GRAPH_TABLE (g\n    MATCH (a IS node)-[x IS edge]->(b IS node)\n    COLUMNS (a.id AS a_id, b.id AS b_id)) e\n  JOIN reach r ON e.a_id = r.b_id\n)\nSELECT count(*) FROM reach",
        "scalePoints": [
          {
            "NODES": 32767
          },
          {
            "NODES": 131071
          },
          {
            "NODES": 524287
          },
          {
            "NODES": 1048575
          }
        ],
        "databaseType": "POSTGRES_19",
        "warmCache": "true",
        "version": "1.3.0"
      },
      "result": "{\"timestamp\":\"2026-07-09T20:11:58.425978901Z\",\"databaseType\":\"postgres_19\",\"sql\":\"EXPLAIN ANALYZE\\nWITH RECURSIVE reach AS (\\n  SELECT b_id FROM GRAPH_TABLE (g\\n    MATCH (a IS node WHERE a.id = 1)-[x IS edge]->(b IS node)\\n    COLUMNS (b.id AS b_id))\\n  UNION\\n  SELECT e.b_id\\n  FROM GRAPH_TABLE (g\\n    MATCH (a IS node)-[x IS edge]->(b IS node)\\n    COLUMNS (a.id AS a_id, b.id AS b_id)) e\\n  JOIN reach r ON e.a_id = r.b_id\\n)\\nSELECT count(*) FROM reach\",\"warmCacheIterations\":2,\"scalePointCount\":4,\"totalComputeSeconds\":44.355,\"results\":[{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"NODES\":32767},\"output\":\"QUERY PLAN\\nAggregate  (cost=993.95..993.96 rows=1 width=8) (actual time=151.027..151.030 rows=1.00 loops=1)\\n  Buffers: shared hit=229370\\n  CTE reach\\n    ->  Recursive Union  (cost=0.86..989.40 rows=202 width=4) (actual time=0.011..142.753 rows=32766.00 loops=1)\\n          Storage: Memory  Maximum Storage: 1153kB\\n          Buffers: shared hit=229370\\n          ->  Nested Loop  (cost=0.86..24.26 rows=2 width=4) (actual time=0.009..0.014 rows=2.00 loops=1)\\n                Buffers: shared hit=12\\n                ->  Nested Loop  (cost=0.57..15.65 rows=2 width=4) (actual time=0.007..0.010 rows=2.00 loops=1)\\n                      Buffers: shared hit=7\\n                      ->  Index Only Scan using nodes_pkey on nodes  (cost=0.29..4.30 rows=1 width=4) (actual time=0.004..0.004 rows=1.00 loops=1)\\n                            Index Cond: (id = 1)\\n                            Heap Fetches: 0\\n                            Index Searches: 1\\n                            Buffers: shared hit=3\\n                      ->  Index Scan using edges_src_idx on edges  (cost=0.29..11.33 rows=2 width=8) (actual time=0.002..0.004 rows=2.00 loops=1)\\n                            Index Cond: (src = 1)\\n                            Index Searches: 1\\n                            Buffers: shared hit=4\\n                ->  Index Only Scan using nodes_pkey on nodes nodes_1  (cost=0.29..4.30 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=2)\\n                      Index Cond: (id = edges.dst)\\n                      Heap Fetches: 0\\n                      Index Searches: 2\\n                      Buffers: shared hit=5\\n          ->  Nested Loop  (cost=0.86..96.31 rows=20 width=4) (actual time=2.239..9.289 rows=2340.29 loops=14)\\n                Buffers: shared hit=229358\\n                ->  Nested Loop  (cost=0.57..89.99 rows=20 width=4) (actual time=2.238..6.406 rows=2340.29 loops=14)\\n                      Buffers: shared hit=163829\\n                      ->  Nested Loop  (cost=0.29..82.50 rows=20 width=8) (actual time=0.002..2.923 rows=2340.43 loops=14)\\n                            Buffers: shared hit=65533\\n                            ->  WorkTable Scan on reach r  (cost=0.00..0.40 rows=20 width=4) (actual time=0.000..0.104 rows=2340.43 loops=14)\\n                            ->  Index Only Scan using nodes_pkey on nodes nodes_2  (cost=0.29..4.10 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=32766)\\n                                  Index Cond: (id = r.b_id)\\n                                  Heap Fetches: 0\\n                                  Index Searches: 32766\\n                                  Buffers: shared hit=65533\\n                      ->  Index Scan using edges_src_idx on edges edges_1  (cost=0.29..0.35 rows=2 width=8) (actual time=0.001..0.001 rows=1.00 loops=32766)\\n                            Index Cond: (src = nodes_2.id)\\n                            Index Searches: 32766\\n                            Buffers: shared hit=98296\\n                ->  Index Only Scan using nodes_pkey on nodes nodes_3  (cost=0.29..0.32 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=32764)\\n                      Index Cond: (id = edges_1.dst)\\n                      Heap Fetches: 0\\n                      Index Searches: 32764\\n                      Buffers: shared hit=65529\\n  ->  CTE Scan on reach  (cost=0.00..4.04 rows=202 width=0) (actual time=0.011..149.179 rows=32766.00 loops=1)\\n        Storage: Memory  Maximum Storage: 1280kB\\n        Buffers: shared hit=229370\\nPlanning:\\n  Buffers: shared hit=21\\nPlanning Time: 0.585 ms\\nExecution Time: 151.410 ms\",\"runtimeDurationMs\":153,\"rowsAreCapped\":false,\"computeSeconds\":0.902},{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"NODES\":131071},\"output\":\"QUERY PLAN\\nAggregate  (cost=1062.00..1062.01 rows=1 width=8) (actual time=606.481..606.483 rows=1.00 loops=1)\\n  Buffers: shared hit=1048569, temp written=223\\n  CTE reach\\n    ->  Recursive Union  (cost=1.00..1057.46 rows=202 width=4) (actual time=0.014..566.086 rows=131070.00 loops=1)\\n          Storage: Memory  Maximum Storage: 4404kB\\n          Buffers: shared hit=1048569\\n          ->  Nested Loop  (cost=1.00..24.41 rows=2 width=4) (actual time=0.012..0.016 rows=2.00 loops=1)\\n                Buffers: shared hit=13\\n                ->  Nested Loop  (cost=0.71..15.79 rows=2 width=4) (actual time=0.009..0.012 rows=2.00 loops=1)\\n                      Buffers: shared hit=8\\n                      ->  Index Only Scan using nodes_pkey on nodes  (cost=0.29..4.31 rows=1 width=4) (actual time=0.004..0.005 rows=1.00 loops=1)\\n                            Index Cond: (id = 1)\\n                            Heap Fetches: 0\\n                            Index Searches: 1\\n                            Buffers: shared hit=3\\n                      ->  Index Scan using edges_src_idx on edges  (cost=0.42..11.46 rows=2 width=8) (actual time=0.004..0.006 rows=2.00 loops=1)\\n                            Index Cond: (src = 1)\\n                            Index Searches: 1\\n                            Buffers: shared hit=5\\n                ->  Index Only Scan using nodes_pkey on nodes nodes_1  (cost=0.29..4.31 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=2)\\n                      Index Cond: (id = edges.dst)\\n                      Heap Fetches: 0\\n                      Index Searches: 2\\n                      Buffers: shared hit=5\\n          ->  Nested Loop  (cost=1.00..103.10 rows=20 width=4) (actual time=8.335..31.924 rows=8191.75 loops=16)\\n                Buffers: shared hit=1048556\\n                ->  Nested Loop  (cost=0.71..96.68 rows=20 width=4) (actual time=8.332..22.548 rows=8191.75 loops=16)\\n                      Buffers: shared hit=786419\\n                      ->  Nested Loop  (cost=0.29..86.60 rows=20 width=8) (actual time=0.002..9.827 rows=8191.88 loops=16)\\n                            Buffers: shared hit=262141\\n                            ->  WorkTable Scan on reach r  (cost=0.00..0.40 rows=20 width=4) (actual time=0.000..0.352 rows=8191.88 loops=16)\\n                            ->  Index Only Scan using nodes_pkey on nodes nodes_2  (cost=0.29..4.31 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=131070)\\n                                  Index Cond: (id = r.b_id)\\n                                  Heap Fetches: 0\\n                                  Index Searches: 131070\\n                                  Buffers: shared hit=262141\\n                      ->  Index Scan using edges_src_idx on edges edges_1  (cost=0.42..0.48 rows=2 width=8) (actual time=0.001..0.001 rows=1.00 loops=131070)\\n                            Index Cond: (src = nodes_2.id)\\n                            Index Searches: 131070\\n                            Buffers: shared hit=524278\\n                ->  Index Only Scan using nodes_pkey on nodes nodes_3  (cost=0.29..0.32 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=131068)\\n                      Index Cond: (id = edges_1.dst)\\n                      Heap Fetches: 0\\n                      Index Searches: 131068\\n                      Buffers: shared hit=262137\\n  ->  CTE Scan on reach  (cost=0.00..4.04 rows=202 width=0) (actual time=0.014..599.629 rows=131070.00 loops=1)\\n        Storage: Disk  Maximum Storage: 4096kB\\n        Buffers: shared hit=1048569, temp written=223\\nPlanning:\\n  Buffers: shared hit=23\\nPlanning Time: 0.563 ms\\nExecution Time: 608.223 ms\",\"runtimeDurationMs\":608,\"rowsAreCapped\":false,\"computeSeconds\":3.352},{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"NODES\":524287},\"output\":\"QUERY PLAN\\nAggregate  (cost=1115.38..1115.39 rows=1 width=8) (actual time=2914.743..2914.748 rows=1.00 loops=1)\\n  Buffers: shared hit=5242870, temp read=672 written=1567\\n  CTE reach\\n    ->  Recursive Union  (cost=1.27..1110.83 rows=202 width=4) (actual time=0.013..2759.491 rows=524286.00 loops=1)\\n          Storage: Disk  Maximum Storage: 8192kB\\n          Buffers: shared hit=5242870, temp read=672 written=672\\n          ->  Nested Loop  (cost=1.27..24.81 rows=2 width=4) (actual time=0.012..0.017 rows=2.00 loops=1)\\n                Buffers: shared hit=16\\n                ->  Nested Loop  (cost=0.84..15.93 rows=2 width=4) (actual time=0.009..0.012 rows=2.00 loops=1)\\n                      Buffers: shared hit=9\\n                      ->  Index Only Scan using nodes_pkey on nodes  (cost=0.42..4.44 rows=1 width=4) (actual time=0.005..0.005 rows=1.00 loops=1)\\n                            Index Cond: (id = 1)\\n                            Heap Fetches: 0\\n                            Index Searches: 1\\n                            Buffers: shared hit=4\\n                      ->  Index Scan using edges_src_idx on edges  (cost=0.42..11.47 rows=2 width=8) (actual time=0.003..0.004 rows=2.00 loops=1)\\n                            Index Cond: (src = 1)\\n                            Index Searches: 1\\n                            Buffers: shared hit=5\\n                ->  Index Only Scan using nodes_pkey on nodes nodes_1  (cost=0.42..4.44 rows=1 width=4) (actual time=0.001..0.002 rows=1.00 loops=2)\\n                      Index Cond: (id = edges.dst)\\n                      Heap Fetches: 0\\n                      Index Searches: 2\\n                      Buffers: shared hit=7\\n          ->  Nested Loop  (cost=1.27..108.40 rows=20 width=4) (actual time=39.031..138.418 rows=29126.89 loops=18)\\n                Buffers: shared hit=5242854, temp read=672 written=2\\n                ->  Nested Loop  (cost=0.84..99.38 rows=20 width=4) (actual time=39.030..97.049 rows=29126.89 loops=18)\\n                      Buffers: shared hit=3670001, temp read=672 written=2\\n                      ->  Nested Loop  (cost=0.42..89.20 rows=20 width=8) (actual time=0.005..46.125 rows=29127.00 loops=18)\\n                            Buffers: shared hit=1572859, temp read=672 written=2\\n                            ->  WorkTable Scan on reach r  (cost=0.00..0.40 rows=20 width=4) (actual time=0.002..2.634 rows=29127.00 loops=18)\\n                                  Buffers: temp read=672 written=2\\n                            ->  Index Only Scan using nodes_pkey on nodes nodes_2  (cost=0.42..4.44 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=524286)\\n                                  Index Cond: (id = r.b_id)\\n                                  Heap Fetches: 0\\n                                  Index Searches: 524286\\n                                  Buffers: shared hit=1572859\\n                      ->  Index Scan using edges_src_idx on edges edges_1  (cost=0.42..0.49 rows=2 width=8) (actual time=0.001..0.001 rows=1.00 loops=524286)\\n                            Index Cond: (src = nodes_2.id)\\n                            Index Searches: 524286\\n                            Buffers: shared hit=2097142\\n                ->  Index Only Scan using nodes_pkey on nodes nodes_3  (cost=0.42..0.45 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=524284)\\n                      Index Cond: (id = edges_1.dst)\\n                      Heap Fetches: 0\\n                      Index Searches: 524284\\n                      Buffers: shared hit=1572853\\n  ->  CTE Scan on reach  (cost=0.00..4.04 rows=202 width=0) (actual time=0.014..2883.685 rows=524286.00 loops=1)\\n        Storage: Disk  Maximum Storage: 7160kB\\n        Buffers: shared hit=5242870, temp read=672 written=1567\\nPlanning:\\n  Buffers: shared hit=28\\nPlanning Time: 0.480 ms\\nExecution Time: 2921.173 ms\",\"runtimeDurationMs\":2915,\"rowsAreCapped\":false,\"computeSeconds\":12.866},{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"NODES\":1048575},\"output\":\"QUERY PLAN\\nAggregate  (cost=1116.84..1116.85 rows=1 width=8) (actual time=5956.278..5956.281 rows=1.00 loops=1)\\n  Buffers: shared hit=10485750, temp read=1568 written=3359\\n  CTE reach\\n    ->  Recursive Union  (cost=1.27..1112.29 rows=202 width=4) (actual time=0.013..5648.270 rows=1048574.00 loops=1)\\n          Storage: Disk  Maximum Storage: 11264kB\\n          Buffers: shared hit=10485750, temp read=1568 written=1568\\n          ->  Nested Loop  (cost=1.27..24.78 rows=2 width=4) (actual time=0.011..0.016 rows=2.00 loops=1)\\n                Buffers: shared hit=16\\n                ->  Nested Loop  (cost=0.85..15.89 rows=2 width=4) (actual time=0.008..0.011 rows=2.00 loops=1)\\n                      Buffers: shared hit=9\\n                      ->  Index Only Scan using nodes_pkey on nodes  (cost=0.42..4.44 rows=1 width=4) (actual time=0.004..0.005 rows=1.00 loops=1)\\n                            Index Cond: (id = 1)\\n                            Heap Fetches: 0\\n                            Index Searches: 1\\n                            Buffers: shared hit=4\\n                      ->  Index Scan using edges_src_idx on edges  (cost=0.42..11.43 rows=2 width=8) (actual time=0.003..0.005 rows=2.00 loops=1)\\n                            Index Cond: (src = 1)\\n                            Index Searches: 1\\n                            Buffers: shared hit=5\\n                ->  Index Only Scan using nodes_pkey on nodes nodes_1  (cost=0.42..4.44 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=2)\\n                      Index Cond: (id = edges.dst)\\n                      Heap Fetches: 0\\n                      Index Searches: 2\\n                      Buffers: shared hit=7\\n          ->  Nested Loop  (cost=1.27..108.55 rows=20 width=4) (actual time=76.405..269.066 rows=55188.00 loops=19)\\n                Buffers: shared hit=10485734, temp read=1568 written=3\\n                ->  Nested Loop  (cost=0.85..99.48 rows=20 width=4) (actual time=76.404..188.922 rows=55188.00 loops=19)\\n                      Buffers: shared hit=7340017, temp read=1568 written=3\\n                      ->  Nested Loop  (cost=0.42..89.25 rows=20 width=8) (actual time=0.007..90.780 rows=55188.11 loops=19)\\n                            Buffers: shared hit=3145723, temp read=1568 written=3\\n                            ->  WorkTable Scan on reach r  (cost=0.00..0.40 rows=20 width=4) (actual time=0.003..5.706 rows=55188.11 loops=19)\\n                                  Buffers: temp read=1568 written=3\\n                            ->  Index Only Scan using nodes_pkey on nodes nodes_2  (cost=0.42..4.44 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=1048574)\\n                                  Index Cond: (id = r.b_id)\\n                                  Heap Fetches: 0\\n                                  Index Searches: 1048574\\n                                  Buffers: shared hit=3145723\\n                      ->  Index Scan using edges_src_idx on edges edges_1  (cost=0.42..0.49 rows=2 width=8) (actual time=0.001..0.001 rows=1.00 loops=1048574)\\n                            Index Cond: (src = nodes_2.id)\\n                            Index Searches: 1048574\\n                            Buffers: shared hit=4194294\\n                ->  Index Only Scan using nodes_pkey on nodes nodes_3  (cost=0.42..0.45 rows=1 width=4) (actual time=0.001..0.001 rows=1.00 loops=1048572)\\n                      Index Cond: (id = edges_1.dst)\\n                      Heap Fetches: 0\\n                      Index Searches: 1048572\\n                      Buffers: shared hit=3145717\\n  ->  CTE Scan on reach  (cost=0.00..4.04 rows=202 width=0) (actual time=0.014..5889.464 rows=1048574.00 loops=1)\\n        Storage: Disk  Maximum Storage: 14328kB\\n        Buffers: shared hit=10485750, temp read=1568 written=3359\\nPlanning:\\n  Buffers: shared hit=28\\nPlanning Time: 0.556 ms\\nExecution Time: 5964.955 ms\",\"runtimeDurationMs\":5950,\"rowsAreCapped\":false,\"computeSeconds\":27.235}],\"workerId\":\"cef05bc0-2f7a-4958-a163-74dbdd20e12d\",\"correlationId\":\"bd2f90b0-d33e-4aad-8d72-8ed5c31f9a1d\",\"correlationIdInstruction\":\"Echo correlationId on subsequent ExoBench tool calls. Tracking only \\u2014 it does NOT preserve DB state.\",\"version\":\"1.3.0\"}",
      "isError": false,
      "caption": "Blast radius as a recursive CTE over GRAPH_TABLE",
      "planCaptions": {
        "32.8K": "The same blast radius with each hop expressed as GRAPH_TABLE inside the recursive CTE. Every step re-resolves vertices through Index Only Scan on nodes_pkey, so buffer traffic rises from 98,300 to 229,370 shared hits for the identical 32,766 nodes. Execution Time 151.410 ms against 59.147 ms.",
        "131.1K": "At 131,070 nodes the GRAPH_TABLE recursion reads 1,048,569 shared buffers, twice the plain CTE, Execution Time 608.223 ms against 298.997 ms. The vertex-resolution join happens per hop, so the tax scales with the traversal.",
        "524.3K": "At 524,286 nodes, 5,242,870 shared hits and worktable Storage: Disk 8,192kB, Execution Time 2921.173 ms against 1156.342 ms for the plain recursive CTE. Same indexed edge traversal, 2.5x the time.",
        "1M": "At 1,048,574 nodes the GRAPH_TABLE recursion reaches 10,485,750 shared buffer hits and Execution Time 5964.955 ms, 2.7x the plain recursive CTE at 2189.152 ms. SQL/PGQ syntax on PostgreSQL 19beta1 buys vertex lookups, not a better algorithm."
      }
    },
    "blastAge": {
      "type": "mcp-call",
      "name": "benchmarkSql",
      "input": {
        "schemaTemplate": "CREATE EXTENSION IF NOT EXISTS age;\nSET search_path = ag_catalog, \"$user\", public;\nSELECT create_graph('bench_graph');\nSELECT create_vlabel('bench_graph','Account');\nSELECT create_elabel('bench_graph','SENT');\nINSERT INTO bench_graph.\"Account\" (id, properties)\nSELECT _graphid(_label_id('bench_graph','Account'), i), agtype_build_map('id', i)\nFROM generate_series(1, ${N}) i;\nINSERT INTO bench_graph.\"SENT\" (id, start_id, end_id, properties)\nSELECT _graphid(_label_id('bench_graph','SENT'), (row_number() over ())::bigint),\n       _graphid(_label_id('bench_graph','Account'), src),\n       _graphid(_label_id('bench_graph','Account'), dst),\n       agtype_build_map()\nFROM (\n  SELECT i AS src, 2*i AS dst FROM generate_series(1, ${N}) i WHERE 2*i <= ${N}\n  UNION ALL SELECT i, 2*i+1 FROM generate_series(1, ${N}) i WHERE 2*i+1 <= ${N}\n) e;\nCREATE INDEX ON bench_graph.\"Account\" USING gin (properties);\nVACUUM ANALYZE;",
        "query": "EXPLAIN ANALYZE\nSELECT * FROM cypher('bench_graph', $$\n  MATCH (a:Account {id: 1})-[:SENT*1..]->(b:Account)\n  RETURN b.id\n$$) AS (reachable agtype)",
        "scalePoints": [
          {
            "N": 32767
          },
          {
            "N": 131071
          },
          {
            "N": 524287
          },
          {
            "N": 1048575
          }
        ],
        "databaseType": "POSTGRES_19",
        "warmCache": "true",
        "version": "1.3.0"
      },
      "result": "{\"timestamp\":\"2026-07-09T20:43:09.903803814Z\",\"databaseType\":\"postgres_19\",\"sql\":\"EXPLAIN ANALYZE\\nSELECT * FROM cypher('bench_graph', $$\\n  MATCH (a:Account {id: 1})-[:SENT*1..]->(b:Account)\\n  RETURN b.id\\n$$) AS (reachable agtype)\",\"warmCacheIterations\":2,\"scalePointCount\":4,\"totalComputeSeconds\":44.931,\"results\":[{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"N\":32767},\"output\":\"QUERY PLAN\\nNested Loop  (cost=21.99..533.23 rows=1 width=32) (actual time=39.275..98.944 rows=32766.00 loops=1)\\n  Buffers: shared hit=90193, temp read=1160 written=1160\\n  ->  Nested Loop  (cost=21.71..531.46 rows=1 width=8) (actual time=39.251..46.894 rows=32766.00 loops=1)\\n        Buffers: shared hit=8, temp read=1160 written=1160\\n        ->  Bitmap Heap Scan on \\\"Account\\\" a  (cost=21.70..117.31 rows=33 width=8) (actual time=0.057..0.059 rows=1.00 loops=1)\\n              Recheck Cond: (properties @> '{\\\"id\\\": 1}'::agtype)\\n              Heap Blocks: exact=1\\n              Buffers: shared hit=8\\n              ->  Bitmap Index Scan on \\\"Account_properties_idx\\\"  (cost=0.00..21.69 rows=33 width=0) (actual time=0.045..0.046 rows=1.00 loops=1)\\n                    Index Cond: (properties @> '{\\\"id\\\": 1}'::agtype)\\n                    Index Searches: 1\\n                    Buffers: shared hit=7\\n        ->  Function Scan on age_vle _age_default_alias_0  (cost=0.01..12.51 rows=5 width=16) (actual time=39.192..45.144 rows=32766.00 loops=1)\\n              Filter: (a.id = start_id)\\n              Buffers: temp read=1160 written=1160\\n  ->  Index Scan using \\\"Account_pkey\\\" on \\\"Account\\\" b  (cost=0.29..1.77 rows=1 width=37) (actual time=0.001..0.001 rows=1.00 loops=32766)\\n        Index Cond: (id = _age_default_alias_0.end_id)\\n        Index Searches: 32766\\n        Buffers: shared hit=90185\\nPlanning:\\n  Buffers: shared hit=1\\nPlanning Time: 0.344 ms\\nExecution Time: 100.962 ms\",\"runtimeDurationMs\":102,\"rowsAreCapped\":false,\"computeSeconds\":0.894},{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"N\":131071},\"output\":\"QUERY PLAN\\nNested Loop  (cost=31.04..2051.64 rows=1 width=32) (actual time=159.361..411.143 rows=131070.00 loops=1)\\n  Buffers: shared hit=360940, temp read=5152 written=5152\\n  ->  Nested Loop  (cost=30.74..2047.13 rows=1 width=8) (actual time=159.333..192.150 rows=131070.00 loops=1)\\n        Buffers: shared hit=10, temp read=5152 written=5152\\n        ->  Bitmap Heap Scan on \\\"Account\\\" a  (cost=30.74..403.08 rows=131 width=8) (actual time=0.049..0.051 rows=1.00 loops=1)\\n              Recheck Cond: (properties @> '{\\\"id\\\": 1}'::agtype)\\n              Heap Blocks: exact=1\\n              Buffers: shared hit=10\\n              ->  Bitmap Index Scan on \\\"Account_properties_idx\\\"  (cost=0.00..30.71 rows=131 width=0) (actual time=0.040..0.040 rows=1.00 loops=1)\\n                    Index Cond: (properties @> '{\\\"id\\\": 1}'::agtype)\\n                    Index Searches: 1\\n                    Buffers: shared hit=9\\n        ->  Function Scan on age_vle _age_default_alias_0  (cost=0.01..12.51 rows=5 width=16) (actual time=159.282..184.875 rows=131070.00 loops=1)\\n              Filter: (a.id = start_id)\\n              Buffers: temp read=5152 written=5152\\n  ->  Index Scan using \\\"Account_pkey\\\" on \\\"Account\\\" b  (cost=0.29..4.50 rows=1 width=37) (actual time=0.001..0.001 rows=1.00 loops=131070)\\n        Index Cond: (id = _age_default_alias_0.end_id)\\n        Index Searches: 131070\\n        Buffers: shared hit=360930\\nPlanning:\\n  Buffers: shared hit=1\\nPlanning Time: 0.287 ms\\nExecution Time: 418.805 ms\",\"runtimeDurationMs\":419,\"rowsAreCapped\":false,\"computeSeconds\":3.406},{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"N\":524287},\"output\":\"QUERY PLAN\\nNested Loop  (cost=33.25..8102.79 rows=1 width=32) (actual time=757.714..1844.102 rows=524286.00 loops=1)\\n  Buffers: shared hit=1968209, temp read=22656 written=22656\\n  ->  Nested Loop  (cost=32.83..8095.79 rows=1 width=8) (actual time=757.684..885.929 rows=524286.00 loops=1)\\n        Buffers: shared hit=10, temp read=22656 written=22656\\n        ->  Bitmap Heap Scan on \\\"Account\\\" a  (cost=32.82..1519.58 rows=524 width=8) (actual time=0.047..0.048 rows=1.00 loops=1)\\n              Recheck Cond: (properties @> '{\\\"id\\\": 1}'::agtype)\\n              Heap Blocks: exact=1\\n              Buffers: shared hit=10\\n              ->  Bitmap Index Scan on \\\"Account_properties_idx\\\"  (cost=0.00..32.69 rows=524 width=0) (actual time=0.038..0.039 rows=1.00 loops=1)\\n                    Index Cond: (properties @> '{\\\"id\\\": 1}'::agtype)\\n                    Index Searches: 1\\n                    Buffers: shared hit=9\\n        ->  Function Scan on age_vle _age_default_alias_0  (cost=0.01..12.51 rows=5 width=16) (actual time=757.636..857.667 rows=524286.00 loops=1)\\n              Filter: (a.id = start_id)\\n              Buffers: temp read=22656 written=22656\\n  ->  Index Scan using \\\"Account_pkey\\\" on \\\"Account\\\" b  (cost=0.42..7.00 rows=1 width=37) (actual time=0.001..0.001 rows=1.00 loops=524286)\\n        Index Cond: (id = _age_default_alias_0.end_id)\\n        Index Searches: 524286\\n        Buffers: shared hit=1968199\\nPlanning:\\n  Buffers: shared hit=1\\nPlanning Time: 0.259 ms\\nExecution Time: 1873.619 ms\",\"runtimeDurationMs\":1869,\"rowsAreCapped\":false,\"computeSeconds\":13.031},{\"kind\":\"postgres_explain_analyze\",\"scalePoint\":{\"N\":1048575},\"output\":\"QUERY PLAN\\nNested Loop  (cost=44.52..16190.15 rows=1 width=32) (actual time=1868.967..4472.216 rows=1048574.00 loops=1)\\n  Buffers: shared hit=3936473, temp read=47360 written=47360\\n  ->  Nested Loop  (cost=44.09..16182.51 rows=1 width=8) (actual time=1868.938..2192.084 rows=1048574.00 loops=1)\\n        Buffers: shared hit=10, temp read=47360 written=47360\\n        ->  Bitmap Heap Scan on \\\"Account\\\" a  (cost=44.09..3017.56 rows=1049 width=8) (actual time=0.061..0.063 rows=1.00 loops=1)\\n              Recheck Cond: (properties @> '{\\\"id\\\": 1}'::agtype)\\n              Heap Blocks: exact=1\\n              Buffers: shared hit=10\\n              ->  Bitmap Index Scan on \\\"Account_properties_idx\\\"  (cost=0.00..43.82 rows=1049 width=0) (actual time=0.048..0.048 rows=1.00 loops=1)\\n                    Index Cond: (properties @> '{\\\"id\\\": 1}'::agtype)\\n                    Index Searches: 1\\n                    Buffers: shared hit=9\\n        ->  Function Scan on age_vle _age_default_alias_0  (cost=0.01..12.51 rows=5 width=16) (actual time=1868.876..2124.922 rows=1048574.00 loops=1)\\n              Filter: (a.id = start_id)\\n              Buffers: temp read=47360 written=47360\\n  ->  Index Scan using \\\"Account_pkey\\\" on \\\"Account\\\" b  (cost=0.42..7.63 rows=1 width=37) (actual time=0.001..0.001 rows=1.00 loops=1048574)\\n        Index Cond: (id = _age_default_alias_0.end_id)\\n        Index Searches: 1048574\\n        Buffers: shared hit=3936463\\nPlanning:\\n  Buffers: shared hit=1\\nPlanning Time: 0.237 ms\\nExecution Time: 4541.892 ms\",\"runtimeDurationMs\":4530,\"rowsAreCapped\":false,\"computeSeconds\":27.6}],\"workerId\":\"0bd0fbe4-410f-4827-b0ef-7cef6e22134c\",\"correlationId\":\"bd2f90b0-d33e-4aad-8d72-8ed5c31f9a1d\",\"correlationIdInstruction\":\"Echo correlationId on subsequent ExoBench tool calls. Tracking only \\u2014 it does NOT preserve DB state.\",\"version\":\"1.3.0\"}",
      "isError": false,
      "caption": "Blast radius as Apache AGE Cypher with a variable-length pattern",
      "planCaptions": {
        "32.8K": "Blast radius in Apache AGE Cypher, MATCH (a:Account {id: 1})-[:SENT*1..]->(b:Account). Bitmap Index Scan on Account_properties_idx resolves the agtype containment lookup, then Nested Loop walks the edges: 90,193 shared hits plus 1,160 temp blocks, Execution Time 100.962 ms for 32,766 nodes.",
        "131.1K": "At 131,070 nodes AGE reads 360,940 shared buffers and writes 5,152 temp blocks, Execution Time 418.805 ms. That sits between the plain recursive CTE at 298.997 ms and the GRAPH_TABLE recursion at 608.223 ms.",
        "524.3K": "At 524,286 nodes AGE writes 22,656 temp blocks against 1,968,209 shared hits, Execution Time 1873.619 ms. Cypher's variable-length pattern does run, which GRAPH_TABLE's quantifier cannot, and costs 1.6x the plain recursive CTE.",
        "1M": "At 1,048,574 nodes Apache AGE spills 47,360 temp blocks and finishes in 4541.892 ms against 2189.152 ms for the recursive CTE, a 2.1x tax for Cypher syntax over the same reachability. Bitmap Heap Scan plus Nested Loop throughout."
      }
    },
    "fixedChart": {
      "type": "chart",
      "chartType": "multi-line",
      "scales": [
        "602k",
        "1.2M",
        "2.4M"
      ],
      "xLabel": "Total graph size (rows)",
      "yLabel": "Execution time (ms)",
      "yUnit": "ms",
      "maxY": 2.5,
      "caption": "Fixed 4-hop typed chain, per-type schema, EXPLAIN ANALYZE execution time. The graph query and the hand-written join run the same because the graph query compiles to the join.",
      "summaryCaption": "The 4-hop chain stays flat under half a millisecond as the graph grows fourfold. SQL/PGQ and the join track each other because SQL/PGQ rewrites to the join.",
      "dataAsProse": "Per-type JOIN execution time: 0.25ms at 602k rows, 0.24ms at 1.2M, 0.26ms at 2.4M. Per-type SQL/PGQ: 0.48ms at 602k (first-scale planning jitter), 0.27ms at 1.2M, 0.29ms at 2.4M. Both flat and sub-millisecond; the graph query is the join.",
      "series": [
        {
          "label": "Hand-written JOIN",
          "data": [
            0.25,
            0.24,
            0.26
          ],
          "color": "#1D9E75",
          "pointStyle": "circle"
        },
        {
          "label": "SQL/PGQ GRAPH_TABLE",
          "data": [
            0.48,
            0.27,
            0.29
          ],
          "color": "#79BAFF",
          "style": "dashed",
          "pointStyle": "rectRot"
        }
      ]
    },
    "blastChart": {
      "type": "chart",
      "chartType": "multi-line",
      "scales": [
        "32,766",
        "131,070",
        "524,286",
        "1,048,574"
      ],
      "xLabel": "Blast radius (reachable nodes)",
      "yLabel": "Execution time (ms)",
      "yUnit": "ms",
      "maxY": 7000,
      "caption": "Variable-depth blast radius across the reachable set. The plain recursive CTE is the floor; every graph form pays a vertex-resolution tax on the same indexed traversal.",
      "summaryCaption": "The plain recursive CTE is fastest at every size. AGE's Cypher quantifier and the emulated GRAPH_TABLE recursion both land at roughly 1.5–2.5x the plain walk. None of them is index-free adjacency.",
      "dataAsProse": "Plain recursive CTE: 59ms at 32,766 reachable, 299ms at 131,070, 1,156ms at 524,286, 2,189ms at 1,048,574. Apache AGE variable-length edge (*1..): 101ms, 419ms, 1,874ms, 4,542ms. Emulated GRAPH_TABLE-in-recursion (pgq-form): 151ms, 608ms, 2,921ms, 5,965ms. The plain CTE is the floor; both graph forms sit above it at roughly 1.5–2.5x.",
      "series": [
        {
          "label": "Plain recursive CTE",
          "data": [
            59,
            299,
            1156,
            2189
          ],
          "color": "#1D9E75",
          "fill": true,
          "pointStyle": "circle"
        },
        {
          "label": "Apache AGE (*1..)",
          "data": [
            101,
            419,
            1874,
            4542
          ],
          "color": "#BA7517",
          "pointStyle": "triangle"
        },
        {
          "label": "GRAPH_TABLE in recursion",
          "data": [
            151,
            608,
            2921,
            5965
          ],
          "color": "#E24B4A",
          "style": "dashed",
          "pointStyle": "rectRot"
        }
      ]
    }
  }
}
