test: add tackle query acceptance gate
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
\set ON_ERROR_STOP on
|
||||
\timing off
|
||||
|
||||
-- Session-local fixture for the G06/G07 public tackle paths.
|
||||
-- No production table or row is modified.
|
||||
SET search_path = pg_temp, public;
|
||||
SET jit = off;
|
||||
SET work_mem = '16MB';
|
||||
|
||||
CREATE TEMP TABLE tackle_item (
|
||||
id uuid PRIMARY KEY, name varchar(200) NOT NULL, category varchar(20) NOT NULL,
|
||||
brand varchar(100), family varchar(100), unlock_level integer
|
||||
);
|
||||
CREATE TEMP TABLE catch_report (
|
||||
id uuid PRIMARY KEY, moderation_status moderationstatus NOT NULL,
|
||||
deleted_at timestamptz, reported_at timestamptz NOT NULL
|
||||
);
|
||||
CREATE TEMP TABLE catch_tackle_component (
|
||||
id uuid PRIMARY KEY, catch_report_id uuid NOT NULL, role varchar(50) NOT NULL,
|
||||
raw_value varchar(200) NOT NULL, position integer NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO tackle_item (id, name, category, brand, family, unlock_level)
|
||||
SELECT md5('tackle-' || g)::uuid, 'Предмет ' || g,
|
||||
CASE WHEN g % 4 = 0 THEN 'bait' ELSE 'lure' END,
|
||||
'Brand-' || (g % 25), 'Family-' || (g % 100), g % 30
|
||||
FROM generate_series(1, 5000) AS g;
|
||||
|
||||
INSERT INTO catch_report (id, moderation_status, deleted_at, reported_at)
|
||||
SELECT md5('tackle-report-' || g)::uuid,
|
||||
CASE WHEN g % 10 = 0 THEN 'pending'::moderationstatus ELSE 'approved'::moderationstatus END,
|
||||
CASE WHEN g % 50 = 0 THEN clock_timestamp() ELSE NULL END,
|
||||
clock_timestamp() - (((g * 97) % 7776000) * interval '1 second')
|
||||
FROM generate_series(1, 100000) AS g;
|
||||
|
||||
INSERT INTO catch_tackle_component (id, catch_report_id, role, raw_value, position)
|
||||
SELECT md5('component-' || g)::uuid,
|
||||
md5('tackle-report-' || g)::uuid,
|
||||
CASE WHEN g % 3 = 0 THEN 'rig' ELSE 'lure' END,
|
||||
'Предмет ' || ((g - 1) % 5000 + 1), 0
|
||||
FROM generate_series(1, 100000) AS g;
|
||||
|
||||
CREATE INDEX tackle_item_catalog_filter_idx
|
||||
ON tackle_item (category, brand, family, unlock_level, name, id);
|
||||
CREATE INDEX catch_report_tackle_feed_idx
|
||||
ON catch_report (moderation_status, deleted_at, reported_at, id);
|
||||
CREATE INDEX catch_tackle_component_report_idx
|
||||
ON catch_tackle_component (catch_report_id, role, raw_value);
|
||||
|
||||
ANALYZE tackle_item;
|
||||
ANALYZE catch_report;
|
||||
ANALYZE catch_tackle_component;
|
||||
|
||||
\echo 'DATASET'
|
||||
SELECT count(*) AS tackle_items, (SELECT count(*) FROM catch_report) AS reports,
|
||||
(SELECT count(*) FROM catch_tackle_component) AS components
|
||||
FROM tackle_item;
|
||||
|
||||
\echo 'PLAN tackle_catalog_filter'
|
||||
EXPLAIN (ANALYZE, BUFFERS, SETTINGS)
|
||||
SELECT id, name, category, brand, family, unlock_level
|
||||
FROM tackle_item
|
||||
WHERE category = 'lure' AND brand = 'Brand-7' AND family = 'Family-7'
|
||||
ORDER BY name, id
|
||||
LIMIT 50;
|
||||
|
||||
\echo 'PLAN tackle_combinations'
|
||||
EXPLAIN (ANALYZE, BUFFERS, SETTINGS)
|
||||
SELECT c.role, c.raw_value, count(DISTINCT r.id)
|
||||
FROM catch_report AS r
|
||||
JOIN catch_tackle_component AS c ON c.catch_report_id = r.id
|
||||
WHERE r.moderation_status = 'approved'
|
||||
AND r.deleted_at IS NULL
|
||||
AND r.reported_at >= now() - interval '72 hours'
|
||||
GROUP BY c.role, c.raw_value
|
||||
ORDER BY count(DISTINCT r.id) DESC, c.role, c.raw_value
|
||||
LIMIT 50;
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
repo=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
||||
output=$(mktemp)
|
||||
cleanup() { rm -f "$output"; }
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
docker compose exec -T db psql -X -U rf4 -d rf4_spotter \
|
||||
< "$repo/deploy/tackle-query-plan-gate.sql" > "$output"
|
||||
|
||||
for plan in tackle_catalog_filter tackle_combinations; do
|
||||
grep -F "PLAN $plan" "$output" >/dev/null
|
||||
done
|
||||
|
||||
awk '
|
||||
/Execution Time:/ { count += 1; if (($3 + 0) > 250) { print "Tackle query plan exceeded 250 ms: " $0 > "/dev/stderr"; failed = 1 } }
|
||||
END { if (count != 2 || failed) exit 1 }
|
||||
' "$output"
|
||||
|
||||
grep -A40 'PLAN tackle_catalog_filter' "$output" | grep -Eq 'Index Scan|Index Only Scan|Bitmap Index Scan'
|
||||
grep -A60 'PLAN tackle_combinations' "$output" | grep -Eq 'Index Scan|Bitmap Index Scan'
|
||||
grep 'Execution Time:' "$output"
|
||||
echo "Tackle query-plan gate passed: indexed catalog and combination paths under 250 ms"
|
||||
Reference in New Issue
Block a user