Files
Oxicloud/tools/perf-audit/admin_user_order_index_pareto.sql
2026-07-22 02:06:29 +02:00

185 lines
8.2 KiB
PL/PgSQL

\set ON_ERROR_STOP on
\pset pager off
\pset format unaligned
\pset tuples_only on
-- SUPERSEDED EXPLORATORY FIXTURE: the tied-timestamp distribution is useful as
-- a stress shape, but PostgreSQL posting-list compression makes its index-size
-- result non-representative. Use admin_user_order_index_representative.sql for
-- decisions; this file is retained only as historical evidence.
-- Three-way Pareto gate for the stable admin pagination order:
-- A. no index;
-- B. created_at only (smaller btree + incremental sort inside ties);
-- C. created_at,id (fully ordered scan).
-- One hundred rows share each timestamp to reproduce CURRENT_TIMESTAMP ties.
BEGIN;
CREATE TEMP TABLE perf_users_base (
id uuid NOT NULL,
username text,
email text NOT NULL,
role_text text NOT NULL,
storage_quota_bytes bigint NOT NULL,
storage_used_bytes bigint NOT NULL,
created_at timestamptz NOT NULL,
last_login_at timestamptz,
active boolean NOT NULL,
oidc_provider text,
is_external boolean NOT NULL
);
INSERT INTO perf_users_base
SELECT
gen_random_uuid(),
'user-' || n,
'user-' || n || '@example.invalid',
CASE WHEN n % 20 = 0 THEN 'admin' ELSE 'user' END,
10737418240,
n::bigint * 1048576,
timestamptz '2026-01-01 00:00:00+00' + make_interval(secs => n / 100),
timestamptz '2026-01-01 00:00:00+00' + make_interval(secs => n / 2),
true,
CASE WHEN n % 3 = 0 THEN 'keycloak' END,
n % 7 = 0
FROM generate_series(1, 500000) AS n;
CREATE TEMP TABLE perf_users_timestamp (LIKE perf_users_base INCLUDING ALL);
CREATE TEMP TABLE perf_users_compound (LIKE perf_users_base INCLUDING ALL);
INSERT INTO perf_users_timestamp SELECT * FROM perf_users_base;
INSERT INTO perf_users_compound SELECT * FROM perf_users_base;
CREATE INDEX perf_users_timestamp_idx ON perf_users_timestamp (created_at DESC);
CREATE INDEX perf_users_compound_idx ON perf_users_compound (created_at DESC, id DESC);
ANALYZE perf_users_base;
ANALYZE perf_users_timestamp;
ANALYZE perf_users_compound;
SELECT 'timestamp_index_bytes|' || pg_relation_size('perf_users_timestamp_idx');
SELECT 'compound_index_bytes|' || pg_relation_size('perf_users_compound_idx');
SELECT 'tied_timestamp_groups|' || COUNT(*)
FROM (SELECT created_at FROM perf_users_base GROUP BY created_at HAVING COUNT(*) > 1) tied;
SELECT 'all_orders_match|' || (
ARRAY(SELECT id FROM perf_users_base ORDER BY created_at DESC, id DESC LIMIT 200 OFFSET 49900)
= ARRAY(SELECT id FROM perf_users_timestamp ORDER BY created_at DESC, id DESC LIMIT 200 OFFSET 49900)
AND ARRAY(SELECT id FROM perf_users_base ORDER BY created_at DESC, id DESC LIMIT 200 OFFSET 49900)
= ARRAY(SELECT id FROM perf_users_compound ORDER BY created_at DESC, id DESC LIMIT 200 OFFSET 49900)
);
\o /dev/null
\timing on
\echo warmup_no_index_first
SELECT * FROM perf_users_base ORDER BY created_at DESC, id DESC LIMIT 100;
\echo warmup_timestamp_first
SELECT * FROM perf_users_timestamp ORDER BY created_at DESC, id DESC LIMIT 100;
\echo warmup_compound_first
SELECT * FROM perf_users_compound ORDER BY created_at DESC, id DESC LIMIT 100;
\echo warmup_no_index_deep
SELECT * FROM perf_users_base ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
\echo warmup_timestamp_deep
SELECT * FROM perf_users_timestamp ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
\echo warmup_compound_deep
SELECT * FROM perf_users_compound ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
-- Rotate execution order between samples.
\echo no_index_first_1
SELECT * FROM perf_users_base ORDER BY created_at DESC, id DESC LIMIT 100;
\echo timestamp_first_1
SELECT * FROM perf_users_timestamp ORDER BY created_at DESC, id DESC LIMIT 100;
\echo compound_first_1
SELECT * FROM perf_users_compound ORDER BY created_at DESC, id DESC LIMIT 100;
\echo timestamp_deep_1
SELECT * FROM perf_users_timestamp ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
\echo compound_deep_1
SELECT * FROM perf_users_compound ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
\echo no_index_deep_1
SELECT * FROM perf_users_base ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
\echo compound_first_2
SELECT * FROM perf_users_compound ORDER BY created_at DESC, id DESC LIMIT 100;
\echo no_index_first_2
SELECT * FROM perf_users_base ORDER BY created_at DESC, id DESC LIMIT 100;
\echo timestamp_first_2
SELECT * FROM perf_users_timestamp ORDER BY created_at DESC, id DESC LIMIT 100;
\echo no_index_deep_2
SELECT * FROM perf_users_base ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
\echo timestamp_deep_2
SELECT * FROM perf_users_timestamp ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
\echo compound_deep_2
SELECT * FROM perf_users_compound ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
\echo timestamp_first_3
SELECT * FROM perf_users_timestamp ORDER BY created_at DESC, id DESC LIMIT 100;
\echo compound_first_3
SELECT * FROM perf_users_compound ORDER BY created_at DESC, id DESC LIMIT 100;
\echo no_index_first_3
SELECT * FROM perf_users_base ORDER BY created_at DESC, id DESC LIMIT 100;
\echo compound_deep_3
SELECT * FROM perf_users_compound ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
\echo no_index_deep_3
SELECT * FROM perf_users_base ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
\echo timestamp_deep_3
SELECT * FROM perf_users_timestamp ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
\echo no_index_first_4
SELECT * FROM perf_users_base ORDER BY created_at DESC, id DESC LIMIT 100;
\echo compound_first_4
SELECT * FROM perf_users_compound ORDER BY created_at DESC, id DESC LIMIT 100;
\echo timestamp_first_4
SELECT * FROM perf_users_timestamp ORDER BY created_at DESC, id DESC LIMIT 100;
\echo timestamp_deep_4
SELECT * FROM perf_users_timestamp ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
\echo no_index_deep_4
SELECT * FROM perf_users_base ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
\echo compound_deep_4
SELECT * FROM perf_users_compound ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
\echo compound_first_5
SELECT * FROM perf_users_compound ORDER BY created_at DESC, id DESC LIMIT 100;
\echo timestamp_first_5
SELECT * FROM perf_users_timestamp ORDER BY created_at DESC, id DESC LIMIT 100;
\echo no_index_first_5
SELECT * FROM perf_users_base ORDER BY created_at DESC, id DESC LIMIT 100;
\echo no_index_deep_5
SELECT * FROM perf_users_base ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
\echo compound_deep_5
SELECT * FROM perf_users_compound ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
\echo timestamp_deep_5
SELECT * FROM perf_users_timestamp ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 50000;
-- Quantify both index write taxes over identical 10k-row inserts.
\echo no_index_insert_1
INSERT INTO perf_users_base SELECT * FROM perf_users_base LIMIT 10000;
\echo timestamp_insert_1
INSERT INTO perf_users_timestamp SELECT * FROM perf_users_timestamp LIMIT 10000;
\echo compound_insert_1
INSERT INTO perf_users_compound SELECT * FROM perf_users_compound LIMIT 10000;
\echo compound_insert_2
INSERT INTO perf_users_compound SELECT * FROM perf_users_compound LIMIT 10000;
\echo no_index_insert_2
INSERT INTO perf_users_base SELECT * FROM perf_users_base LIMIT 10000;
\echo timestamp_insert_2
INSERT INTO perf_users_timestamp SELECT * FROM perf_users_timestamp LIMIT 10000;
\echo timestamp_insert_3
INSERT INTO perf_users_timestamp SELECT * FROM perf_users_timestamp LIMIT 10000;
\echo compound_insert_3
INSERT INTO perf_users_compound SELECT * FROM perf_users_compound LIMIT 10000;
\echo no_index_insert_3
INSERT INTO perf_users_base SELECT * FROM perf_users_base LIMIT 10000;
\echo no_index_insert_4
INSERT INTO perf_users_base SELECT * FROM perf_users_base LIMIT 10000;
\echo timestamp_insert_4
INSERT INTO perf_users_timestamp SELECT * FROM perf_users_timestamp LIMIT 10000;
\echo compound_insert_4
INSERT INTO perf_users_compound SELECT * FROM perf_users_compound LIMIT 10000;
\echo compound_insert_5
INSERT INTO perf_users_compound SELECT * FROM perf_users_compound LIMIT 10000;
\echo timestamp_insert_5
INSERT INTO perf_users_timestamp SELECT * FROM perf_users_timestamp LIMIT 10000;
\echo no_index_insert_5
INSERT INTO perf_users_base SELECT * FROM perf_users_base LIMIT 10000;
\timing off
\o
ROLLBACK;