Skip to content

Commit 03e6dd5

Browse files
fix(seed): run workload queries as standalone statements, not inside DO block
pg_stat_statements does NOT track individual queries inside PL/pgSQL DO blocks - only the outer DO block is tracked. This is why zero slow queries were appearing. Changed workload to run each pattern as a standalone SELECT statement with generate_series() + LATERAL to execute multiple iterations. Each pattern now records as a single query with high mean_exec_time (total / calls). Co-authored-by: Venkat SF <venkatesh.sakamuri@stayflexi.com>
1 parent 5ea091a commit 03e6dd5

1 file changed

Lines changed: 62 additions & 95 deletions

File tree

‎scripts/self-host/seed-demo-data.sh‎

Lines changed: 62 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -358,104 +358,71 @@ compose exec -T postgres psql -U postgres -d demo_shop -c "SELECT pg_stat_statem
358358
# These patterns are intentionally suboptimal to trigger index recommendations
359359
# NO pg_sleep - all slowness comes from real inefficient query patterns
360360
echo " Starting workload (this takes about 30-60 seconds)..."
361-
compose exec -T postgres psql -U postgres -d demo_shop -v ON_ERROR_STOP=1 <<'EOWORK'
362-
-- Workload simulation for pg_stat_statements
363-
-- With 300K+ audit_log and 100K+ order_items, these patterns will genuinely exceed 100ms
364-
-- All slowness is REAL from inefficient queries (no pg_sleep)
361+
# Generate SQL file with individual statements.
362+
# Queries inside PL/pgSQL DO blocks are NOT tracked separately by pg_stat_statements.
363+
# Each SELECT must be a standalone statement to be tracked individually.
364+
# Using generate_series() to repeat each pattern multiple times within a single query,
365+
# which pg_stat_statements will track and report the cumulative execution time.
365366

366-
DO $$
367-
DECLARE
368-
i int;
369-
result_count bigint;
370-
dummy_row record;
371-
BEGIN
372-
RAISE NOTICE 'Starting workload simulation with realistic slow patterns...';
373-
374-
-- Pattern 1: LOWER() on orders.status column prevents index use
375-
-- This scans all 5000 orders and applies LOWER() to each row
376-
-- Run 50 times to accumulate enough calls for the advisor
377-
RAISE NOTICE 'Running LOWER(status) pattern...';
378-
FOR i IN 1..50 LOOP
379-
SELECT COUNT(*) INTO result_count FROM orders
380-
WHERE LOWER(status) = 'delivered' AND total_amount > 100;
381-
END LOOP;
382-
383-
-- Pattern 2: Full table scan on audit_log (300K+ rows) without index
384-
-- This genuinely takes time due to the large table size
385-
RAISE NOTICE 'Running audit_log full scan pattern...';
386-
FOR i IN 1..20 LOOP
387-
SELECT COUNT(*) INTO result_count FROM audit_log
388-
WHERE table_name = 'orders'
389-
AND changed_at > NOW() - INTERVAL '90 days';
390-
END LOOP;
391-
392-
-- Pattern 3: Sort on audit_log without index support (300K rows)
393-
-- Sorting 300K rows without index is genuinely slow
394-
RAISE NOTICE 'Running audit_log sort pattern...';
395-
FOR i IN 1..15 LOOP
396-
SELECT * INTO dummy_row FROM audit_log
397-
WHERE table_name IN ('orders', 'customers', 'products')
398-
ORDER BY changed_at DESC
399-
LIMIT 1000;
400-
END LOOP;
401-
402-
-- Pattern 4: ILIKE with leading wildcard on products (forces seq scan)
403-
RAISE NOTICE 'Running ILIKE pattern...';
404-
FOR i IN 1..30 LOOP
405-
SELECT COUNT(*) INTO result_count FROM products
406-
WHERE name ILIKE '%widget%' OR description ILIKE '%premium%';
407-
END LOOP;
408-
409-
-- Pattern 5: Large join between order_items (100K) and orders (5K)
410-
-- Missing index on the join column makes this slow
411-
RAISE NOTICE 'Running large join pattern...';
412-
FOR i IN 1..15 LOOP
413-
SELECT COUNT(*), SUM(oi.subtotal) INTO result_count, result_count FROM order_items oi
414-
JOIN orders o ON oi.order_id = o.id
415-
WHERE o.status = 'delivered';
416-
END LOOP;
417-
418-
-- Pattern 6: Expensive aggregation across 100K+ order_items
419-
RAISE NOTICE 'Running expensive aggregation pattern...';
420-
FOR i IN 1..10 LOOP
421-
SELECT COUNT(*) INTO result_count FROM (
422-
SELECT DATE_TRUNC('month', o.created_at) as month,
423-
p.name as product_name,
424-
COUNT(*) as order_count,
425-
SUM(oi.subtotal) as revenue
426-
FROM orders o
427-
JOIN order_items oi ON o.id = oi.order_id
428-
JOIN products p ON oi.product_id = p.id
429-
WHERE o.status NOT IN ('cancelled', 'refunded')
430-
GROUP BY DATE_TRUNC('month', o.created_at), p.id, p.name
431-
ORDER BY revenue DESC
432-
) sub;
433-
END LOOP;
434-
435-
-- Pattern 7: Missing composite index - status + payment_status filter
436-
RAISE NOTICE 'Running missing composite index pattern...';
437-
FOR i IN 1..40 LOOP
438-
SELECT COUNT(*) INTO result_count FROM orders
439-
WHERE status = 'pending' AND payment_status = 'paid'
440-
AND created_at > NOW() - INTERVAL '30 days';
441-
END LOOP;
442-
443-
-- Pattern 8: Correlated subquery (inefficient N+1 style)
444-
RAISE NOTICE 'Running correlated subquery pattern...';
445-
FOR i IN 1..20 LOOP
446-
SELECT COUNT(*) INTO result_count FROM orders o
447-
WHERE EXISTS (
448-
SELECT 1 FROM audit_log a
449-
WHERE a.record_id = o.id
450-
AND a.table_name = 'orders'
451-
AND a.action = 'UPDATE'
452-
);
453-
END LOOP;
454-
455-
RAISE NOTICE 'Workload simulation completed.';
456-
END $$;
367+
compose exec -T postgres psql -U postgres -d demo_shop -v ON_ERROR_STOP=1 <<'EOWORK'
368+
-- Pattern 1: LOWER() on status column defeats index (50 calls)
369+
-- Each row of generate_series triggers a separate evaluation via LATERAL join
370+
SELECT 'LOWER(status) pattern' as pattern, COUNT(*)
371+
FROM generate_series(1, 50) g,
372+
LATERAL (SELECT COUNT(*) FROM orders WHERE LOWER(status) = 'delivered' AND total_amount > 100) sub;
373+
374+
-- Pattern 2: Full table scan on audit_log 300K rows (20 calls)
375+
SELECT 'audit_log scan pattern' as pattern, COUNT(*)
376+
FROM generate_series(1, 20) g,
377+
LATERAL (SELECT COUNT(*) FROM audit_log WHERE table_name = 'orders' AND changed_at > NOW() - INTERVAL '90 days') sub;
378+
379+
-- Pattern 3: Sort on audit_log without index (15 calls)
380+
SELECT 'audit_log sort pattern' as pattern, COUNT(*)
381+
FROM generate_series(1, 15) g,
382+
LATERAL (SELECT id FROM audit_log WHERE table_name IN ('orders', 'customers', 'products') ORDER BY changed_at DESC LIMIT 1000) sub;
383+
384+
-- Pattern 4: ILIKE with leading wildcard (30 calls)
385+
SELECT 'ILIKE pattern' as pattern, COUNT(*)
386+
FROM generate_series(1, 30) g,
387+
LATERAL (SELECT COUNT(*) FROM products WHERE name ILIKE '%widget%' OR description ILIKE '%premium%') sub;
388+
389+
-- Pattern 5: Large join order_items (100K) to orders (5K) (15 calls)
390+
SELECT 'large join pattern' as pattern, COUNT(*), SUM(total)
391+
FROM generate_series(1, 15) g,
392+
LATERAL (SELECT COUNT(*) as cnt, SUM(oi.subtotal) as total FROM order_items oi JOIN orders o ON oi.order_id = o.id WHERE o.status = 'delivered') sub;
393+
394+
-- Pattern 6: Expensive aggregation with GROUP BY (10 calls)
395+
SELECT 'expensive aggregation pattern' as pattern, COUNT(*)
396+
FROM generate_series(1, 10) g,
397+
LATERAL (
398+
SELECT DATE_TRUNC('month', o.created_at), p.name, COUNT(*), SUM(oi.subtotal)
399+
FROM orders o
400+
JOIN order_items oi ON o.id = oi.order_id
401+
JOIN products p ON oi.product_id = p.id
402+
WHERE o.status NOT IN ('cancelled', 'refunded')
403+
GROUP BY 1, p.id, p.name
404+
ORDER BY 4 DESC
405+
LIMIT 100
406+
) sub;
407+
408+
-- Pattern 7: Missing composite index on (status, payment_status) (40 calls)
409+
SELECT 'composite index pattern' as pattern, COUNT(*)
410+
FROM generate_series(1, 40) g,
411+
LATERAL (SELECT COUNT(*) FROM orders WHERE status = 'pending' AND payment_status = 'paid' AND created_at > NOW() - INTERVAL '30 days') sub;
412+
413+
-- Pattern 8: Correlated subquery - inefficient EXISTS (20 calls)
414+
SELECT 'correlated subquery pattern' as pattern, COUNT(*)
415+
FROM generate_series(1, 20) g,
416+
LATERAL (
417+
SELECT COUNT(*) FROM orders o
418+
WHERE EXISTS (SELECT 1 FROM audit_log a WHERE a.record_id = o.id AND a.table_name = 'orders' AND a.action = 'UPDATE')
419+
) sub;
420+
421+
SELECT 'Workload patterns completed' as status;
457422
EOWORK
458423

424+
echo " Workload patterns completed."
425+
459426
echo " Workload simulation completed."
460427
echo " Verifying pg_stat_statements data..."
461428
slow_count="$(compose exec -T postgres psql -U postgres -d demo_shop -At -c \

0 commit comments

Comments
 (0)