185 lines
8.2 KiB
PL/PgSQL
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;
|