Shuffle/supabase/perf/catalog_search_perf_load.sql
cschlaefke 81be119f4e test(catalog): Latenzmessung der Katalogsuche (Projekt perf, vorlaeufig) und Doku
Playwright-Projekt perf (nur mit PERF_WEB_URL, nur lesend, nie in der CI):
CDP-Drosselung 100 ms / 10 Mbit/s, mind. 100 Suchen, Messung im Browser vom
letzten Tastendruck bis zur ersten Kachel. Ladeskript fuer synthetisches
Volumen unter supabase/perf/. Messung und Aussagegrenzen in der Umsetzungsdoku.

Refs #3

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 19:50:03 +01:00

116 lines
5.5 KiB
PL/PgSQL

-- Synthetisches Katalogvolumen fuer die Latenzmessung KAT-01 Kriterium 7 (Playwright-Projekt `perf`,
-- Spec #3 Abschnitt 7.3). Anders als catalog_search_perf.sql (Messung in der Datenbank, Rollback) wird hier
-- COMMITTET: Die App im Browser muss die Daten ueber Kong lesen koennen. Deshalb nur lokal, nur kurz, und immer
-- mit catalog_search_perf_unload.sql wieder entfernen.
--
-- Was es tut: legt 10 000 sichtbare Katalogspiele (Standard), je zwei alternative Titel, 2 000 Verlage
-- (12 900 Zuordnungen) und je Spiel zwei Genres und drei Mechaniken aus dem VORHANDENEN Vokabular an. Alle Spiele
-- und Verlage tragen IDs mit dem Praefix 9e4f0000- , das Entfernen loescht darueber (Zuordnungen und alternative
-- Titel haengen per on delete cascade an den Spielen). Danach vacuum (analyze), damit der Planer die echten
-- Groessen kennt.
--
-- Schutz wie in catalog_search_perf.sql: -v confirm_local=yes, nur per Unix-Socket (docker exec ... psql), Abbruch
-- bei mehr als 20 privaten Spielen. (Konten werden bewusst NICHT gezaehlt: die E2E-Tests legen laufend welche an.)
-- Aufruf:
-- docker exec -i supabase_db_shuffle psql -U postgres -d postgres -X -v confirm_local=yes -v n_games=10000 \
-- < supabase/perf/catalog_search_perf_load.sql
\set ON_ERROR_STOP on
\if :{?confirm_local}
\else
\set confirm_local no
\endif
select :'confirm_local' = 'yes' as confirm_ok, coalesce(:'HOST', '') like '/%' as socket_ok \gset
\if :confirm_ok
\else
\echo 'Abbruch: mit -v confirm_local=yes aufrufen (nur lokal, siehe Kopfkommentar).'
\quit
\endif
\if :socket_ok
\else
\echo 'Abbruch: nur per Unix-Socket im lokalen Container (docker exec ... psql), nie ueber TCP gegen einen Server.'
\quit
\endif
\if :{?n_games}
\else
\set n_games 10000
\endif
do $guard$
begin
if (select count(*) from public.games where visibility = 'private') > 20 then
raise exception 'Abbruch: Die Datenbank sieht nach echten Daten aus (mehr als 20 private Spiele). Nur lokal.';
end if;
if exists (select 1 from public.games where id::text like '9e4f0000-%') then
raise exception 'Abbruch: synthetische Daten sind schon geladen (erst catalog_search_perf_unload.sql ausfuehren).';
end if;
if (select count(*) from public.genres) < 3 or (select count(*) from public.mechanics) < 3 then
raise exception 'Abbruch: Genre-/Mechanik-Vokabular fehlt (supabase db reset einspielen).';
end if;
end
$guard$;
begin;
select setseed(0.42);
create temp table perf_words on commit drop as
select i, translate(substr(md5('w' || i), 1, 4 + (i % 6)), '0123456789', 'ghijklmnop') as w
from generate_series(1, 3000) i;
alter table perf_words add primary key (i);
create function pg_temp.pick() returns text language sql volatile as
$$ select w from perf_words where i = (select 1 + floor(power(random(), 2.2) * 3000)::int) $$;
insert into public.publishers (id, name)
select ('9e4f0000-0000-4000-9000-' || lpad(to_hex(i), 12, '0'))::uuid, initcap(pg_temp.pick()) || ' Spiele ' || i
from generate_series(1, 2000) i;
insert into public.games (id, title, visibility, year, min_players, max_players, min_minutes, max_minutes, min_age, description, description_locale)
select ('9e4f0000-0000-4000-8000-' || lpad(to_hex(g), 12, '0'))::uuid,
initcap(concat_ws(' ', pg_temp.pick(), case when k >= 2 then pg_temp.pick() end, case when k >= 3 then pg_temp.pick() end,
case when k >= 4 then pg_temp.pick() end)),
'catalog', 1990 + (g % 35), mn, mn + (g % 5), 10 + (g % 12) * 10, 30 + (g % 12) * 10, 6 + (g % 4) * 3,
'Synthetischer Kurztext ' || g, 'de'
from (select g, 1 + floor(random() * 3.99)::int as k, 1 + (g % 4) as mn from generate_series(1, :n_games) g) s;
insert into public.game_alt_titles (game_id, locale, title)
select g.id, l.locale, initcap(pg_temp.pick() || ' ' || pg_temp.pick())
from public.games g, (values ('de'), ('en')) as l(locale)
where g.id::text like '9e4f0000-%'
on conflict do nothing;
insert into public.game_publishers (game_id, publisher_id)
select g.id, p.id
from public.games g
cross join lateral (select id from public.publishers where id::text like '9e4f0000-%'
order by md5(g.id::text || id::text) limit 1) p
where g.id::text like '9e4f0000-%'
on conflict do nothing;
insert into public.game_publishers (game_id, publisher_id)
select g.id, p.id
from public.games g
cross join lateral (select id from public.publishers where id::text like '9e4f0000-%'
order by md5(g.id::text || id::text || 'b') limit 1) p
where g.id::text like '9e4f0000-%' and random() < 0.3
on conflict do nothing;
insert into public.game_genres (game_id, genre_id)
select g.id, t.id from public.games g cross join lateral
(select x.id from public.genres x order by md5(g.id::text || x.id::text) limit 2) t
where g.id::text like '9e4f0000-%' on conflict do nothing;
insert into public.game_mechanics (game_id, mechanic_id)
select g.id, t.id from public.games g cross join lateral
(select x.id from public.mechanics x order by md5(g.id::text || x.id::text) limit 3) t
where g.id::text like '9e4f0000-%' on conflict do nothing;
commit;
vacuum (analyze) public.games;
vacuum (analyze) public.game_alt_titles;
vacuum (analyze) public.publishers;
vacuum (analyze) public.game_publishers;
vacuum (analyze) public.game_genres;
vacuum (analyze) public.game_mechanics;
\echo === geladen ===
select (select count(*) from public.catalog_games) as katalogspiele,
(select count(*) from public.game_alt_titles) as alt_titel,
(select count(*) from public.publishers) as verlage,
(select count(*) from public.game_publishers) as verlag_zuordnungen;