Skip to content

Commit d619895

Browse files
committed
fix(tables): measure the unique-check scans by delta on a flushed GIN index
1 parent b21d12f commit d619895

1 file changed

Lines changed: 16 additions & 7 deletions

File tree

‎apps/sim/lib/table/rows/row-writes.integration.ts‎

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -742,26 +742,32 @@ describe('table row writes against real PostgreSQL', () => {
742742
)
743743

744744
/**
745-
* Index and sequential scans this transaction ran on `user_table_rows`, by relation, read from
746-
* the transaction's own statistics so concurrent suites cannot skew them.
745+
* Index and sequential scans this backend has counted on `user_table_rows`, by relation. The
746+
* counters are the backend's unflushed statistics, which can still hold scans from earlier
747+
* transactions on the same pooled connection, so callers diff two readings.
747748
*/
748-
async function rowScans(trx: DbTransaction): Promise<Record<string, number>> {
749+
async function rowScans(trx: DbTransaction): Promise<Map<string, number>> {
749750
const scans = await trx.execute<{ relname: string; scans: number }>(sql`
750751
SELECT c.relname, pg_stat_get_xact_numscans(c.oid)::int AS scans
751752
FROM pg_class c
752753
WHERE c.oid = 'user_table_rows'::regclass
753754
OR c.oid IN (SELECT indexrelid FROM pg_index WHERE indrelid = 'user_table_rows'::regclass)`)
754-
return Object.fromEntries(
755-
scans.filter((row) => row.scans > 0).map((row) => [row.relname, row.scans])
756-
)
755+
return new Map(scans.map((row) => [row.relname, row.scans]))
757756
}
758757

758+
/** Runs the check in a transaction and returns its result with the scans the check alone ran. */
759759
const checkWithScans = (table: TableDefinition, rows: RowData[]) =>
760760
db.transaction(async (trx) => {
761761
// A parallel worker's scans are not counted in the leader's transaction statistics.
762762
await trx.execute(sql`SET LOCAL max_parallel_workers_per_gather = 0`)
763+
const before = await rowScans(trx)
763764
const result = await checkBatchUniqueConstraintsDb(table.id, rows, table.schema, trx)
764-
return { result, scans: await rowScans(trx) }
765+
const scans: Record<string, number> = {}
766+
for (const [relname, count] of await rowScans(trx)) {
767+
const ran = count - (before.get(relname) ?? 0)
768+
if (ran > 0) scans[relname] = ran
769+
}
770+
return { result, scans }
765771
})
766772

767773
it('reports stored and in-batch conflicts across every probe group', async () => {
@@ -862,6 +868,9 @@ describe('table row writes against real PostgreSQL', () => {
862868
SELECT ${table.id}::text || '-' || i, ${table.id}, ${workspaceId},
863869
jsonb_build_object('email', 'stored-' || i || '@example.test'), i, 'a' || i
864870
FROM generate_series(0, 39999) AS i`
871+
// A bulk insert leaves its entries in the GIN pending list, which a manual ANALYZE does not
872+
// merge and which the planner charges to every probe, so flush it for a deterministic plan.
873+
await control`SELECT gin_clean_pending_list('user_table_rows_tenant_data_gin_idx'::regclass)`
865874
await control`ANALYZE user_table_rows`
866875
const rows: RowData[] = Array.from({ length: 260 }, (_, i) => ({
867876
email: i % 10 === 0 ? `stored-${i * 100}@example.test` : `new-${i}@example.test`,

0 commit comments

Comments
 (0)