Steer /agg off the sort-based GroupAggregate plan
pf_logid arrives pre-sorted off its index, so the planner picks an Incremental Sort GroupAggregate on the strength of that "free" sort — but comparing 30+ grouping columns per row makes it 5-7x slower in practice than a HashAggregate. Confirmed via EXPLAIN ANALYZE on fc_osm_skinny_34: 34-50s sorted vs ~7-10s hashed, independent of work_mem or stats (ANALYZE didn't change the plan choice). Scoped to this transaction only, since disabling incremental sort globally could hurt some other query's plan. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
8bc7d65fce
commit
748e301464
@ -517,6 +517,8 @@ module.exports = function(pool) {
|
|||||||
// result loads as one small native Perspective table indexed on pf_gkey and
|
// result loads as one small native Perspective table indexed on pf_gkey and
|
||||||
// the WASM view still does all rollup/expand/collapse locally.
|
// the WASM view still does all rollup/expand/collapse locally.
|
||||||
router.get('/versions/:id/agg', async (req, res) => {
|
router.get('/versions/:id/agg', async (req, res) => {
|
||||||
|
let client;
|
||||||
|
let committed = false;
|
||||||
try {
|
try {
|
||||||
const ctx = await getContext(parseInt(req.params.id), 'get_agg');
|
const ctx = await getContext(parseInt(req.params.id), 'get_agg');
|
||||||
// Before the GROUP BY, not after: the territory column need not be
|
// Before the GROUP BY, not after: the territory column need not be
|
||||||
@ -526,7 +528,23 @@ module.exports = function(pool) {
|
|||||||
territory_clause:
|
territory_clause:
|
||||||
buildTerritoryClause(sessionTerritory(req), ctx.territoryCol, 't') || 'TRUE',
|
buildTerritoryClause(sessionTerritory(req), ctx.territoryCol, 't') || 'TRUE',
|
||||||
});
|
});
|
||||||
const { rows } = await runSQL(sql);
|
|
||||||
|
// The GROUP BY spans every in_grain column plus pf_logid and the
|
||||||
|
// pf.log/pf.version columns joined in for segment/bucket — 30+ keys
|
||||||
|
// on a wide source. The planner sees pf_logid arrive pre-sorted off
|
||||||
|
// its index and picks a sort-based GroupAggregate on the strength of
|
||||||
|
// that "free" sort, but comparing that many columns per row makes it
|
||||||
|
// 5-7x slower in practice than a HashAggregate — confirmed via
|
||||||
|
// EXPLAIN ANALYZE on fc_osm_skinny_34 (34-50s sorted vs ~7s hashed,
|
||||||
|
// independent of work_mem or stats). Scoped to this transaction only:
|
||||||
|
// disabling incremental sort globally could hurt some other query's plan.
|
||||||
|
client = await pool.connect();
|
||||||
|
await client.query('BEGIN');
|
||||||
|
await client.query('SET LOCAL enable_sort = off');
|
||||||
|
await client.query('SET LOCAL enable_incremental_sort = off');
|
||||||
|
const { rows } = await runSQL(sql, client);
|
||||||
|
await client.query('COMMIT');
|
||||||
|
committed = true;
|
||||||
|
|
||||||
res.setHeader('Content-Type', 'application/vnd.apache.arrow.stream');
|
res.setHeader('Content-Type', 'application/vnd.apache.arrow.stream');
|
||||||
res.setHeader('X-Row-Count', String(rows.length));
|
res.setHeader('X-Row-Count', String(rows.length));
|
||||||
@ -545,6 +563,11 @@ module.exports = function(pool) {
|
|||||||
console.error(err);
|
console.error(err);
|
||||||
if (!res.headersSent) res.status(err.status || 500).json({ error: err.message });
|
if (!res.headersSent) res.status(err.status || 500).json({ error: err.message });
|
||||||
else res.destroy();
|
else res.destroy();
|
||||||
|
} finally {
|
||||||
|
if (client) {
|
||||||
|
if (!committed) try { await client.query('ROLLBACK'); } catch {}
|
||||||
|
client.release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user