diff --git a/diagrams/erdiagram_targetdb_latest.pdf b/diagrams/erdiagram_targetdb_latest.pdf index db3c921..b3a2300 100644 Binary files a/diagrams/erdiagram_targetdb_latest.pdf and b/diagrams/erdiagram_targetdb_latest.pdf differ diff --git a/docs/examples/queries.md b/docs/examples/queries.md index 2bacad5..2de81fb 100644 --- a/docs/examples/queries.md +++ b/docs/examples/queries.md @@ -101,3 +101,233 @@ AND EXISTS ( AND t.proposal_id = 'S25A-039' ); ``` + +--- + +## PostgreSQL Troubleshooting + +### Running Processes + +#### Show all active processes + +```sql +/* Show all currently connected processes */ +SELECT + pid, + usename, + application_name, + client_addr, + state, + wait_event_type, + wait_event, + query_start, + now() - query_start AS duration, + query +FROM pg_stat_activity +WHERE pid <> pg_backend_pid() +ORDER BY query_start; +``` + +#### Show processes for a specific database + +```sql +/* Show processes for a specific database */ +SELECT + pid, + usename, + state, + wait_event_type, + wait_event, + now() - query_start AS duration, + query +FROM pg_stat_activity +WHERE datname = 'your_database_name' + AND pid <> pg_backend_pid() +ORDER BY query_start; +``` + +#### Show only active queries + +```sql +/* Show only currently running queries */ +SELECT pid, usename, now() - query_start AS duration, query +FROM pg_stat_activity +WHERE state = 'active' + AND pid <> pg_backend_pid() +ORDER BY duration DESC; +``` + +#### Show long-running queries + +```sql +/* Show queries that have been running for more than 5 minutes */ +SELECT pid, usename, state, now() - query_start AS duration, query +FROM pg_stat_activity +WHERE now() - query_start > interval '5 minutes' + AND state <> 'idle' + AND pid <> pg_backend_pid() +ORDER BY duration DESC; +``` + +#### Show idle-in-transaction processes + +```sql +/* Show processes in 'idle in transaction' state (common cause of locks) */ +SELECT pid, usename, state, now() - query_start AS duration, query +FROM pg_stat_activity +WHERE state IN ('idle in transaction', 'idle in transaction (aborted)') +ORDER BY duration DESC; +``` + +#### Count connections by user and state + +```sql +/* Count connections grouped by user and state */ +SELECT count(*) AS cnt, usename, state +FROM pg_stat_activity +GROUP BY usename, state +ORDER BY cnt DESC; +``` + +--- + +### Locks + +#### Show lock-waiting processes + +```sql +/* Show processes waiting on locks and the processes blocking them */ +SELECT + blocking.pid AS blocking_pid, + blocking.state AS blocking_state, + left(blocking.query, 80) AS blocking_query, + blocked.pid AS blocked_pid, + blocked.state AS blocked_state, + blocked.wait_event, + left(blocked.query, 80) AS blocked_query +FROM pg_stat_activity blocked +JOIN pg_stat_activity blocking + ON blocking.pid = ANY(pg_blocking_pids(blocked.pid)) +ORDER BY blocking.pid; +``` + +#### Show blockers for a specific query + +```sql +/* Show processes blocking a specific query (e.g., ALTER TABLE) */ +SELECT + blocking.pid AS blocking_pid, + blocking.state AS blocking_state, + left(blocking.query, 80) AS blocking_query, + blocked.pid AS blocked_pid, + blocked.state AS blocked_state, + blocked.wait_event +FROM pg_stat_activity blocked +JOIN pg_stat_activity blocking + ON blocking.pid = ANY(pg_blocking_pids(blocked.pid)) +WHERE blocked.query LIKE 'ALTER TABLE%'; +``` + +--- + +### Terminating Processes + +#### Cancel a query (keep connection) + +```sql +/* Cancel the query only; the connection is preserved */ +SELECT pg_cancel_backend(); +``` + +#### Force-terminate a process + +```sql +/* Terminate the connection entirely; use when pg_cancel_backend has no effect */ +SELECT pg_terminate_backend(); +``` + +#### Bulk-terminate idle-in-transaction processes + +```sql +/* Terminate all idle-in-transaction processes older than 1 hour */ +SELECT pg_terminate_backend(pid) +FROM pg_stat_activity +WHERE state IN ('idle in transaction', 'idle in transaction (aborted)') + AND now() - query_start > interval '1 hour' + AND pid <> pg_backend_pid(); +``` + +#### Bulk-terminate stale idle connections + +```sql +/* Terminate all idle connections older than 1 day */ +SELECT pg_terminate_backend(pid) +FROM pg_stat_activity +WHERE state = 'idle' + AND now() - state_change > interval '1 day' + AND pid <> pg_backend_pid(); +``` + +--- + +### Table and Index Information + +#### Check table sizes + +```sql +/* Check the size of specific tables (including indexes) */ +SELECT + pg_size_pretty(pg_total_relation_size('target')) AS target_size, + pg_size_pretty(pg_total_relation_size('fluxstd')) AS fluxstd_size; +``` + +#### List largest tables in the database + +```sql +/* List tables ordered by total size */ +SELECT + relname AS table_name, + pg_size_pretty(pg_total_relation_size(relid)) AS total_size +FROM pg_catalog.pg_statio_user_tables +ORDER BY pg_total_relation_size(relid) DESC +LIMIT 20; +``` + +--- + +### DDL Operations on Large Tables + +#### Safely add a FOREIGN KEY constraint + +```sql +/* Split into NOT VALID + VALIDATE to minimise lock duration */ + +-- Step 1: Add the constraint without validation (short lock) +ALTER TABLE target + ADD CONSTRAINT target_filter_u_fkey + FOREIGN KEY (filter_u) REFERENCES filter_name(filter_name) + NOT VALID; + +-- Step 2: Validate the constraint (uses ShareUpdateExclusiveLock; does not block reads/writes) +ALTER TABLE target VALIDATE CONSTRAINT target_filter_u_fkey; +``` + +#### Safely create or drop an index + +```sql +/* Use CONCURRENTLY to avoid blocking other queries */ + +-- Create +CREATE INDEX CONCURRENTLY idx_target_ra ON target(ra); + +-- Drop +DROP INDEX CONCURRENTLY idx_target_ra; +``` + +#### Set timeouts during migrations + +```sql +/* Prevent migrations from hanging indefinitely on lock acquisition */ +SET lock_timeout = '5s'; -- Fail immediately if the lock cannot be acquired within 5 s +SET statement_timeout = '0'; -- No time limit on the statement itself +``` diff --git a/docs/tbls/public.fluxstd.md b/docs/tbls/public.fluxstd.md index bbfcb9e..7e61766 100644 --- a/docs/tbls/public.fluxstd.md +++ b/docs/tbls/public.fluxstd.md @@ -101,10 +101,10 @@ | ---- | ---------- | | fluxstd_pkey | CREATE UNIQUE INDEX fluxstd_pkey ON public.fluxstd USING btree (fluxstd_id) | | uq_obj_id_input_catalog_id_version | CREATE UNIQUE INDEX uq_obj_id_input_catalog_id_version ON public.fluxstd USING btree (obj_id, input_catalog_id, version) | +| fluxstd_q3c_ang2ipix_idx | CREATE INDEX fluxstd_q3c_ang2ipix_idx ON public.fluxstd USING btree (q3c_ang2ipix(ra, "dec")) | +| ix_fluxstd_input_catalog_fluxstdid | CREATE INDEX ix_fluxstd_input_catalog_fluxstdid ON public.fluxstd USING btree (input_catalog_id, fluxstd_id) | | ix_fluxstd_version_fluxstdid | CREATE INDEX ix_fluxstd_version_fluxstdid ON public.fluxstd USING btree (version, fluxstd_id) | | ix_fluxstd_version | CREATE INDEX ix_fluxstd_version ON public.fluxstd USING btree (version) | -| ix_fluxstd_input_catalog_fluxstdid | CREATE INDEX ix_fluxstd_input_catalog_fluxstdid ON public.fluxstd USING btree (input_catalog_id, fluxstd_id) | -| fluxstd_q3c_ang2ipix_idx | CREATE INDEX fluxstd_q3c_ang2ipix_idx ON public.fluxstd USING btree (q3c_ang2ipix(ra, "dec")) | ## Relations diff --git a/docs/tbls/public.sky.md b/docs/tbls/public.sky.md index 3931078..6b4f98d 100644 --- a/docs/tbls/public.sky.md +++ b/docs/tbls/public.sky.md @@ -34,8 +34,8 @@ | ---- | ---------- | | sky_pkey | CREATE UNIQUE INDEX sky_pkey ON public.sky USING btree (sky_id) | | sky_obj_id_input_catalog_id_version_key | CREATE UNIQUE INDEX sky_obj_id_input_catalog_id_version_key ON public.sky USING btree (obj_id, input_catalog_id, version) | -| sky_q3c_ang2ipix_idx | CREATE INDEX sky_q3c_ang2ipix_idx ON public.sky USING btree (q3c_ang2ipix(ra, "dec")) | | ix_sky_input_catalog_id | CREATE INDEX ix_sky_input_catalog_id ON public.sky USING btree (input_catalog_id) | +| sky_q3c_ang2ipix_idx | CREATE INDEX sky_q3c_ang2ipix_idx ON public.sky USING btree (q3c_ang2ipix(ra, "dec")) | | ix_sky_version | CREATE INDEX ix_sky_version ON public.sky USING btree (version) | ## Relations