Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions scripts/ops/reclaim_supabase_swap.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
-- Capgo-EU Phase A reclaim (run manually in a maintenance window).
Comment thread
riderx marked this conversation as resolved.
Comment thread
riderx marked this conversation as resolved.
Comment thread
riderx marked this conversation as resolved.
Comment thread
riderx marked this conversation as resolved.
Comment thread
riderx marked this conversation as resolved.
Comment thread
riderx marked this conversation as resolved.
Comment thread
riderx marked this conversation as resolved.
-- Safe order: truncate empty bloat -> batched archive deletes -> null dual manifests -> trim audit.
-- Do NOT wrap the whole file in one transaction. VACUUM cannot run inside a transaction block.
-- Prefer Supabase SQL editor / psql as postgres. Re-run sections until counts hit zero.

\timing on
Comment thread
riderx marked this conversation as resolved.
Outdated
\set ON_ERROR_STOP on

-- ---------------------------------------------------------------------------
-- 0) Baseline sizes
-- ---------------------------------------------------------------------------
SELECT pg_size_pretty(pg_database_size(current_database())::bigint) AS db_size;

SELECT
relname,
n_live_tup,
pg_size_pretty(pg_total_relation_size(format('%I.%I', schemaname, relname)::regclass)::bigint) AS total
FROM pg_stat_user_tables
WHERE (schemaname, relname) IN (
('net', '_http_response'),
('public', 'audit_logs'),
('public', 'app_versions'),
('public', 'manifest'),
('pgmq', 'a_on_version_update'),
('pgmq', 'a_on_manifest_create'),
('pgmq', 'a_webhook_dispatcher'),
('pgmq', 'a_on_channel_update')
)
ORDER BY pg_total_relation_size(format('%I.%I', schemaname, relname)::regclass) DESC;

-- ---------------------------------------------------------------------------
-- 1) Truncate pg_net response bloat (~5GB empty table in prod)
-- ---------------------------------------------------------------------------
TRUNCATE TABLE net._http_response;
Comment thread
riderx marked this conversation as resolved.

-- ---------------------------------------------------------------------------
-- 2) Purge pgmq archives older than 2 days (batched). Repeat until deleted=0.
-- ---------------------------------------------------------------------------
DO $$
DECLARE
queue_name text;
cutoff timestamptz := now() - interval '2 days';
batch_size integer := 10000;
deleted_batch integer;
deleted_total bigint;
BEGIN
FOREACH queue_name IN ARRAY ARRAY[
'on_version_update',
'on_manifest_create',
'webhook_dispatcher',
'on_channel_update'
]
LOOP
deleted_total := 0;
LOOP
EXECUTE format(
'WITH doomed AS (
SELECT ctid FROM pgmq.a_%I WHERE archived_at < $1 LIMIT $2
)
DELETE FROM pgmq.a_%I AS archive
USING doomed
WHERE archive.ctid = doomed.ctid',
queue_name, queue_name
) USING cutoff, batch_size;
GET DIAGNOSTICS deleted_batch = ROW_COUNT;
deleted_total := deleted_total + deleted_batch;
EXIT WHEN deleted_batch = 0;
END LOOP;
RAISE NOTICE 'purged % rows from pgmq.a_%', deleted_total, queue_name;
END LOOP;
END $$;

VACUUM (VERBOSE) pgmq.a_on_version_update;
Comment thread
riderx marked this conversation as resolved.
VACUUM (VERBOSE) pgmq.a_on_manifest_create;
VACUUM (VERBOSE) pgmq.a_webhook_dispatcher;
VACUUM (VERBOSE) pgmq.a_on_channel_update;

-- Optional hard reclaim if VACUUM leaves a lot of empty pages (takes stronger locks):
-- VACUUM (FULL, VERBOSE) pgmq.a_on_version_update;
-- VACUUM (FULL, VERBOSE) pgmq.a_on_manifest_create;
-- VACUUM (FULL, VERBOSE) pgmq.a_webhook_dispatcher;
-- VACUUM (FULL, VERBOSE) pgmq.a_on_channel_update;

-- ---------------------------------------------------------------------------
-- 3) Null migrated app_versions.manifest arrays (dual storage leftovers)
-- ---------------------------------------------------------------------------
DO $$
DECLARE
batch_size integer := 200;
updated_batch integer;
updated_total bigint := 0;
BEGIN
LOOP
WITH doomed AS (
SELECT av.id
FROM public.app_versions AS av
WHERE av.manifest IS NOT NULL
AND cardinality(av.manifest) > 0
AND EXISTS (
Comment thread
riderx marked this conversation as resolved.
Outdated
SELECT 1 FROM public.manifest AS m WHERE m.app_version_id = av.id
)
ORDER BY av.id
LIMIT batch_size
)
UPDATE public.app_versions AS av
SET manifest = NULL
FROM doomed
WHERE av.id = doomed.id;
GET DIAGNOSTICS updated_batch = ROW_COUNT;
updated_total := updated_total + updated_batch;
EXIT WHEN updated_batch = 0;
END LOOP;
RAISE NOTICE 'nulled manifest arrays on % versions', updated_total;
END $$;

VACUUM (VERBOSE) public.app_versions;

-- ---------------------------------------------------------------------------
-- 4) Trim audit_logs older than 30 days (batched)
-- ---------------------------------------------------------------------------
DO $$
DECLARE
cutoff timestamptz := now() - interval '30 days';
batch_size integer := 5000;
deleted_batch integer;
deleted_total bigint := 0;
BEGIN
LOOP
WITH doomed AS (
SELECT ctid
FROM public.audit_logs
WHERE created_at < cutoff
LIMIT batch_size
)
DELETE FROM public.audit_logs AS audit_logs
USING doomed
WHERE audit_logs.ctid = doomed.ctid;
GET DIAGNOSTICS deleted_batch = ROW_COUNT;
deleted_total := deleted_total + deleted_batch;
EXIT WHEN deleted_batch = 0;
END LOOP;
RAISE NOTICE 'deleted % audit_logs rows older than 30 days', deleted_total;
END $$;

VACUUM (VERBOSE) public.audit_logs;

-- ---------------------------------------------------------------------------
-- 5) Final sizes
-- ---------------------------------------------------------------------------
SELECT pg_size_pretty(pg_database_size(current_database())::bigint) AS db_size_after;

SELECT
relname,
n_live_tup,
pg_size_pretty(pg_total_relation_size(format('%I.%I', schemaname, relname)::regclass)::bigint) AS total
FROM pg_stat_user_tables
WHERE (schemaname, relname) IN (
('net', '_http_response'),
('public', 'audit_logs'),
('public', 'app_versions'),
('public', 'manifest'),
('pgmq', 'a_on_version_update'),
('pgmq', 'a_on_manifest_create'),
('pgmq', 'a_webhook_dispatcher'),
('pgmq', 'a_on_channel_update')
)
ORDER BY pg_total_relation_size(format('%I.%I', schemaname, relname)::regclass) DESC;
63 changes: 63 additions & 0 deletions scripts/ops/verify_supabase_swap.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
-- Post-deploy / post-reclaim verification for Capgo-EU swap pressure.
Comment thread
riderx marked this conversation as resolved.

SELECT pg_size_pretty(pg_database_size(current_database())::bigint) AS db_size;

SELECT
name,
setting
FROM pg_settings
WHERE name IN ('shared_buffers', 'work_mem', 'max_connections');

SELECT
relname,
n_live_tup,
pg_size_pretty(pg_total_relation_size(format('%I.%I', schemaname, relname)::regclass)::bigint) AS total
FROM pg_stat_user_tables
WHERE (schemaname, relname) IN (
('net', '_http_response'),
('public', 'audit_logs'),
('public', 'app_versions'),
('public', 'manifest'),
('pgmq', 'a_on_version_update'),
('pgmq', 'a_on_manifest_create'),
('pgmq', 'a_webhook_dispatcher'),
('pgmq', 'a_on_channel_update')
)
ORDER BY pg_total_relation_size(format('%I.%I', schemaname, relname)::regclass) DESC;

SELECT count(*) AS versions_with_array_manifest
FROM public.app_versions
WHERE manifest IS NOT NULL AND cardinality(manifest) > 0;
Comment thread
riderx marked this conversation as resolved.
Outdated

SELECT name, enabled, hour_interval, run_at_hour, run_at_minute, target
FROM public.cron_tasks
WHERE name IN (
'cleanup_queue_messages',
'cleanup_net_http_response',
'cleanup_old_audit_logs',
'null_migrated_app_version_manifests'
)
ORDER BY name;

SELECT status, return_message, count(*) AS n
Comment thread
riderx marked this conversation as resolved.
Outdated
FROM cron.job_run_details
WHERE start_time > now() - interval '24 hours'
OR (start_time IS NULL AND status IN ('failed', 'connecting'))
GROUP BY status, return_message
ORDER BY n DESC
LIMIT 20;

SELECT
count(*) FILTER (WHERE archived_at < now() - interval '2 days') AS archives_older_than_2d,
count(*) AS archives_total
FROM pgmq.a_on_manifest_create;
Comment thread
riderx marked this conversation as resolved.
Outdated

SELECT
'index hit rate' AS name,
ROUND((sum(idx_blks_hit) / nullif(sum(idx_blks_hit + idx_blks_read), 0) * 100)::numeric, 2) AS ratio
Comment thread
riderx marked this conversation as resolved.
Outdated
FROM pg_statio_user_indexes
UNION ALL
SELECT
'table hit rate',
ROUND((sum(heap_blks_hit) / nullif(sum(heap_blks_hit) + sum(heap_blks_read), 0) * 100)::numeric, 2)
FROM pg_statio_user_tables;
1 change: 1 addition & 0 deletions src/services/versions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export function createBuiltinChannelVersion(channel: {
cli_version: null,
comment: null,
created_at: channel.created_at,
created_by_apikey_rbac_id: null,
deleted: false,
deleted_at: null,
external_url: null,
Expand Down
Loading
Loading