Skip to content

fix: return an error for INSERT .. SELECT into sharded tables when router can't find shards of rows - #1987

Merged
jkaczman merged 1 commit into
mainfrom
jk-insert-select-shard-error
Nov 9, 2026
Merged

jkaczman merged 1 commit into
mainfrom
jk-insert-select-shard-error

Conversation

@jkaczman

@jkaczman jkaczman commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Previously, when the subquery rewrite was off, queries matching INSERT INTO <sharded table> .. SELECT .. were routed incorrectly if the sharding key wasn't explicitly specified (e.g, from a generate_series or unnest function), as the router can't find the sharding keys for the rows belonging to the SELECT.

E.g., if we have a statement such as

INSERT INTO sharded (id, value) SELECT g, 'a' FROM generate_series(1, 10) g

the router, right now, has no idea what generate_series(1, 10) would return, thus, currently uses round-robin to route that query, meaning that, in a case where we have two shards, all the 10 rows from that generate_series function are routed to a single shard (e.g., Shard A), even though some of those ids belong on Shard B.

Now, the router returns an error for these kind of INSERTs in this situation.

A lot of this is irrelevant after subqueries are enabled by default, but until then, this prevents incorrect functionality stemming from this kind of query.

@github-actions

github-actions Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Metal bench (arm64) passed. All 475 measures within bounds.

suite throughput cpu/txn user cycles/txn user instr/txn kernel cycles/txn kernel instr/txn ctx/txn
pooler 18.56 (-0.2% ± 0.5%) 17,719 (-0.5% ± 0.1%) 31,989 (+0.0% ± 0.04%) 29,570 (-0.1% ± 1.4%) 51,203 (-0.1% ± 0.5%)
lb 19.22 (-0.1% ± 1.0%) 18,310 (-0.2% ± 0.1%) 32,884 (+0.0% ± 0.07%) 30,571 (+0.1% ± 1.7%) 52,378 (+0.3% ± 0.4%)
sharding-w2 33.69 (+1.3% ± 1.7%) 45,517 (+0.6% ± 1.5%) 97,319 (+0.0% ± 0.02%) 42,212 (+1.4% ± 2.3%) 53,618 (+0.1% ± 0.3%) 1.543 (+0.3% ± 0.5%)
sharding-w8 241,661 (+0.6% ± 3.1%) 29.77 (-0.8% ± 2.8%) 45,664 (+0.4% ± 1.2%) 52,881 (+0.0% ± 0.3%) 36,614 (-2.3% ± 5.0%) 42,343 (-0.0% ± 0.2%) 0.164 (+1.3% ± 2.3%)
Counters per txn
suite frontend stall l1i refill backend stall
pooler 7,025 (-0.3% ± 0.3%) 2,556 (-0.0% ± 0.1%)
lb 7,359 (+0.7% ± 0.6%) 2,608 (+0.1% ± 0.2%)
sharding-w2 19,370 (+0.9% ± 3.3%) 4,186 (-0.4% ± 2.0%)
sharding-w8 21,656 (+2.1% ± 2.2%) 4,254 (+1.3% ± 2.3%)

Microbench: all 462 measures within bounds.

Hot code per txn
path base head change
pooler 110.1 KB 109.4 KB -0.7 KB
sharding-w2 167.1 KB 166.2 KB -0.9 KB

pooler:

crate base head change
pgdog::frontend::client::query_engine 19.8 KB 20.1 KB +0.2 KB
tokio 18.1 KB 18.1 KB +0.0 KB
pgdog::backend::pool 17.2 KB 17.2 KB +0.1 KB
aws-lc (TLS crypto) 9.9 KB 10.2 KB +0.2 KB
rustls (TLS) 8.4 KB 8.4 KB +0.0 KB
pgdog::frontend (other) 7.6 KB 7.6 KB -0.1 KB
pgdog::backend::server 7.2 KB 6.9 KB -0.3 KB
other 6.1 KB 5.7 KB -0.4 KB
everything else 15.8 KB 15.2 KB -0.5 KB
function head change
pgdog::frontend::client::query_engine::query::<impl pgdog::frontend::client::query_engine: 5.5 KB +0.1 KB
pgdog::frontend::client::Client::run::{{closure}} 5.2 KB +0.0 KB
pgdog::frontend::client::query_engine::QueryEngine::handle::{{closure}} 4.2 KB +0.0 KB
tokio::runtime::task::raw::poll 3.1 KB +0.0 KB
pgdog::frontend::client::query_engine::query::<impl pgdog::frontend::client::query_engine: 3.0 KB +0.1 KB

sharding-w2:

crate base head change
pgdog::frontend::router 26.8 KB 27.0 KB +0.2 KB
pgdog::frontend::client::query_engine 23.0 KB 22.8 KB -0.2 KB
tokio 19.4 KB 19.2 KB -0.2 KB
pgdog::backend::pool 18.0 KB 18.0 KB +0.0 KB
aws-lc (TLS crypto) 12.8 KB 12.8 KB -0.1 KB
other 10.0 KB 10.0 KB +0.0 KB
pgdog::frontend (other) 8.5 KB 8.8 KB +0.2 KB
rustls (TLS) 8.2 KB 8.2 KB -0.1 KB
everything else 40.3 KB 39.4 KB -0.9 KB
function head change
pgdog::frontend::client::Client::run::{{closure}} 6.1 KB +0.1 KB
pgdog::frontend::client::query_engine::query::<impl pgdog::frontend::client::query_engine: 5.2 KB -0.1 KB
pgdog::frontend::client::query_engine::QueryEngine::handle::{{closure}} 4.2 KB -0.1 KB
pgdog::frontend::router::parser::query::select::<impl pgdog::frontend::router::parser::que 3.4 KB +0.0 KB
tokio::runtime::task::raw::poll 3.3 KB -0.1 KB
lb flamegraph vs ba27030

lb

Open interactive for hover, zoom and search.

lb removed

Open interactive.

microbench flamegraph vs ba27030

microbench

Open interactive for hover, zoom and search.

microbench removed

Open interactive.

pooler flamegraph vs ba27030

pooler

Open interactive for hover, zoom and search.

pooler removed

Open interactive.

sharding-w2 flamegraph vs ba27030

sharding-w2

Open interactive for hover, zoom and search.

sharding-w2 removed

Open interactive.

sharding-w8 flamegraph vs ba27030

sharding-w8

Open interactive for hover, zoom and search.

sharding-w8 removed

Open interactive.

Logs

lb, microbench, pooler, sharding-w2, sharding-w8, verdict.json

Compared 0b7e835 with ba27030 on c8g.metal-24xl (on-demand).

@github-actions

github-actions Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Metal bench (x86) passed. All 475 measures within bounds.

suite throughput cpu/txn user cycles/txn user instr/txn kernel cycles/txn kernel instr/txn ctx/txn
pooler 34.86 (+0.3% ± 0.2%) 23,150 (+1.7% ± 0.8%) 33,858 (-0.0% ± 0.02%) 58,033 (-0.3% ± 0.3%) 38,562 (+0.1% ± 0.2%)
lb 35.89 (+0.4% ± 0.3%) 24,286 (+1.5% ± 0.6%) 34,803 (-0.0% ± 0.03%) 59,073 (+0.0% ± 0.5%) 39,216 (-0.1% ± 0.2%)
sharding-w2 56.94 (+0.3% ± 1.1%) 53,819 (+0.8% ± 1.4%) 101,187 (+0.0% ± 0.01%) 73,372 (+0.0% ± 1.1%) 43,023 (-0.0% ± 0.2%) 1.391 (-0.1% ± 0.5%)
sharding-w8 147,644 (-0.6% ± 0.6%) 48.38 (-0.0% ± 0.05%) 57,848 (-0.2% ± 0.4%) 56,666 (+0.0% ± 0.03%) 65,003 (+0.1% ± 0.5%) 34,218 (+0.0% ± 0.03%) 0.1661 (-1.2% ± 2.7%)
Counters per txn
suite frontend stall l1i refill backend stall
pooler 75,053 (+3.6% ± 1.0%) 3,629 (+0.9% ± 0.3%)
lb 78,139 (+3.4% ± 0.9%) 3,763 (+1.1% ± 0.1%)
sharding-w2 161,340 (+2.2% ± 2.9%) 6,154 (+2.2% ± 0.6%)
sharding-w8 124,381 (+0.1% ± 2.5%) 4,910 (+2.2% ± 0.2%)

Microbench: all 462 measures within bounds.

Microbench latency: 53 benches changed more than 3%
benchmark latency baseline change instructions change
simple_cold::set 2,648 ns 2,446 ns +8.26% ± 1.61% +0.00% ± 0.00%
simple_cold::delete_all_shards 2,969 ns 2,773 ns +7.07% ± 0.97% +0.01% ± 0.00%
extended_evicting::delete_all_shards 3,283 ns 3,067 ns +7.03% ± 1.04% +0.01% ± 0.00%
extended_evicting::set 2,928 ns 2,740 ns +6.88% ± 2.27% +0.00% ± 0.00%
simple_cold::begin 2,082 ns 1,952 ns +6.66% ± 0.80% +0.00% ± 0.00%
extended_evicting::select_omni 4,065 ns 3,813 ns +6.61% ± 0.84% +0.00% ± 0.00%
extended_cold::set 2,770 ns 2,602 ns +6.46% ± 1.68% +0.00% ± 0.00%
extended_cold::delete_all_shards 3,113 ns 2,925 ns +6.43% ± 0.79% +0.01% ± 0.00%
extended_evicting::select_all_shards 4,455 ns 4,199 ns +6.09% ± 1.80% +0.00% ± 0.00%
extended_evicting::begin 2,348 ns 2,214 ns +6.05% ± 1.44% +0.00% ± 0.00%
extended_evicting::delete_by_key 4,240 ns 4,009 ns +5.77% ± 1.57% +0.00% ± 0.00%
simple_cold::select_omni 3,717 ns 3,517 ns +5.69% ± 1.47% +0.00% ± 0.00%
extended_cold::begin 2,197 ns 2,081 ns +5.58% ± 0.84% +0.00% ± 0.00%
simple_cold::update_all_shards 3,651 ns 3,459 ns +5.56% ± 1.84% +0.00% ± 0.00%
extended_cold::select_omni 3,845 ns 3,643 ns +5.54% ± 0.85% +0.00% ± 0.00%
extended_evicting::update_all_shards 3,936 ns 3,731 ns +5.50% ± 1.73% +0.00% ± 0.00%
simple_cold::select_all_shards 4,111 ns 3,897 ns +5.50% ± 1.82% +0.00% ± 0.00%
extended_evicting::distinct 4,727 ns 4,497 ns +5.12% ± 1.51% +0.00% ± 0.00%
extended_cold::delete_by_key 4,024 ns 3,830 ns +5.07% ± 0.95% +0.00% ± 0.00%
simple_cold::delete_by_key 3,900 ns 3,716 ns +4.96% ± 1.82% +0.00% ± 0.00%
extended_cold::select_all_shards 4,244 ns 4,046 ns +4.89% ± 1.12% +0.00% ± 0.00%
extended_cached::group_by 2,990 ns 2,852 ns +4.84% ± 1.51% +0.01% ± 0.00%
extended_evicting::select_by_key 5,302 ns 5,064 ns +4.70% ± 1.02% +0.00% ± 0.00%
extended_cold::update_all_shards 3,767 ns 3,601 ns +4.61% ± 1.31% +0.00% ± 0.00%
extended_evicting::order_limit_offset 8,628 ns 8,250 ns +4.58% ± 1.03% -0.02% ± 0.00%
extended_cached::order_limit_offset 2,858 ns 2,738 ns +4.39% ± 1.10% +0.01% ± 0.00%
extended_cached::set 1,533 ns 1,471 ns +4.22% ± 1.42% +0.00% ± 0.00%
extended_cached::select_all_shards 2,500 ns 2,400 ns +4.17% ± 1.38% +0.01% ± 0.00%
simple_cold::select_by_key 4,976 ns 4,778 ns +4.15% ± 2.66% +0.00% ± 0.00%
simple_cold::distinct 4,375 ns 4,204 ns +4.07% ± 1.67% +0.00% ± 0.00%
extended_evicting::lookup 5,904 ns 5,674 ns +4.06% ± 1.09% +0.00% ± 0.00%
extended_cached::aggregate 2,995 ns 2,881 ns +3.96% ± 0.67% +0.01% ± 0.00%
extended_cold::update_by_key 4,618 ns 4,446 ns +3.87% ± 0.88% +0.00% ± 0.00%
extended_evicting::update_by_key 4,782 ns 4,606 ns +3.82% ± 1.08% +0.00% ± 0.00%
simple_cold::cte 7,183 ns 6,927 ns +3.70% ± 1.46% +0.00% ± 0.00%
extended_evicting::cte 7,529 ns 7,263 ns +3.66% ± 1.18% +0.00% ± 0.00%
extended_cached::min_max_sum 3,367 ns 3,249 ns +3.63% ± 0.85% +0.00% ± 0.00%
extended_evicting::subquery 6,344 ns 6,124 ns +3.59% ± 0.78% +0.00% ± 0.00%
simple_cold::order_limit_offset 8,279 ns 7,995 ns +3.55% ± 0.37% -0.02% ± 0.00%
extended_cold::order_limit_offset 8,336 ns 8,051 ns +3.54% ± 0.74% -0.02% ± 0.00%
Hot code per txn
path base head change
pooler 100.6 KB 102.0 KB +1.4 KB
sharding-w2 158.2 KB 156.9 KB -1.3 KB

pooler:

crate base head change
pgdog::frontend::client::query_engine 19.8 KB 19.6 KB -0.2 KB
tokio 16.9 KB 17.4 KB +0.5 KB
pgdog::backend::pool 16.1 KB 16.4 KB +0.2 KB
pgdog::frontend (other) 9.1 KB 9.1 KB +0.1 KB
rustls (TLS) 7.0 KB 7.1 KB +0.1 KB
aws-lc (TLS crypto) 6.6 KB 6.7 KB +0.1 KB
pgdog::backend::server 6.3 KB 6.2 KB -0.1 KB
other 5.5 KB 5.5 KB +0.0 KB
everything else 13.3 KB 14.0 KB +0.7 KB
function head change
pgdog::frontend::client::Client::run::{{closure}} 6.9 KB +0.1 KB
pgdog::frontend::client::query_engine::query::<impl pgdog::frontend::client::query_engine: 5.7 KB +0.1 KB
pgdog::frontend::client::query_engine::QueryEngine::handle::{{closure}} 4.9 KB +0.2 KB
tokio::runtime::task::raw::poll 3.6 KB +0.0 KB
tokio_rustls::common::Stream<IO,C>::read_io 2.8 KB +0.1 KB

sharding-w2:

crate base head change
pgdog::frontend::router 25.6 KB 24.3 KB -1.3 KB
pgdog::frontend::client::query_engine 23.7 KB 23.2 KB -0.5 KB
tokio 18.0 KB 17.9 KB -0.1 KB
pgdog::backend::pool 18.1 KB 17.2 KB -0.9 KB
aws-lc (TLS crypto) 10.1 KB 10.3 KB +0.2 KB
other 10.2 KB 10.2 KB -0.1 KB
pgdog::frontend (other) 10.1 KB 10.1 KB +0.1 KB
pgdog::backend::server 7.4 KB 7.3 KB -0.1 KB
everything else 35.1 KB 36.4 KB +1.3 KB
function head change
pgdog::frontend::client::Client::run::{{closure}} 7.6 KB +0.1 KB
pgdog::frontend::client::query_engine::query::<impl pgdog::frontend::client::query_engine: 5.6 KB -0.2 KB
pgdog::frontend::client::query_engine::QueryEngine::handle::{{closure}} 4.9 KB +0.2 KB
pgdog::frontend::router::parser::query::select::<impl pgdog::frontend::router::parser::que 4.2 KB +0.1 KB
tokio::runtime::task::raw::poll 3.7 KB -0.2 KB
lb flamegraph vs ba27030

lb

Open interactive for hover, zoom and search.

lb removed

Open interactive.

microbench flamegraph vs ba27030

microbench

Open interactive for hover, zoom and search.

microbench removed

Open interactive.

pooler flamegraph vs ba27030

pooler

Open interactive for hover, zoom and search.

pooler removed

Open interactive.

sharding-w2 flamegraph vs ba27030

sharding-w2

Open interactive for hover, zoom and search.

sharding-w2 removed

Open interactive.

sharding-w8 flamegraph vs ba27030

sharding-w8

Open interactive for hover, zoom and search.

sharding-w8 removed

Open interactive.

Logs

lb, microbench, pooler, sharding-w2, sharding-w8, verdict.json

Compared 0b7e835 with ba27030 on m7a.metal-48xl (spot).

@jkaczman
jkaczman force-pushed the jk-insert-select-shard-error branch 3 times, most recently from 7d572f1 to 0b7e835 Compare November 9, 2026 21:55
@codecov

codecov Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.39130% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...og/src/frontend/client/query_engine/route_query.rs 0.00% 7 Missing ⚠️

📢 Thoughts on this report? Let us know!

Base automatically changed from jk-subqueries-plain-ctes to main November 9, 2026 23:50
@jkaczman
jkaczman force-pushed the jk-insert-select-shard-error branch from 0b7e835 to 6df7724 Compare November 9, 2026 23:50
@jkaczman
jkaczman marked this pull request as ready for review November 9, 2026 23:50
@jkaczman
jkaczman merged commit 87965fb into main Nov 9, 2026
41 checks passed
@jkaczman
jkaczman deleted the jk-insert-select-shard-error branch November 9, 2026 23:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant