feat: add housekeeper task interceptor trigger and update docs

This commit is contained in:
Maksym Buz
2026-04-29 17:12:18 +00:00
parent a88cee43b6
commit 59b2780125
13 changed files with 158966 additions and 148395 deletions

View File

@@ -43,6 +43,8 @@ BEGIN
IF p_period = 'month' THEN
v_suffix := to_char(p_start_time, 'YYYYMM');
ELSIF p_period LIKE '%hour%' THEN
v_suffix := to_char(p_start_time, 'YYYYMMDDHH24');
ELSE
v_suffix := to_char(p_start_time, 'YYYYMMDD');
END IF;
@@ -50,10 +52,15 @@ BEGIN
v_partition_name := p_parent_table || '_p' || v_suffix;
IF NOT partitions.partition_exists(v_partition_name) THEN
EXECUTE format(
'CREATE TABLE %I.%I PARTITION OF %I.%I FOR VALUES FROM (%s) TO (%s)',
v_parent_schema, v_partition_name, v_parent_schema, p_parent_table, v_start_ts, v_end_ts
);
BEGIN
EXECUTE format(
'CREATE TABLE %I.%I PARTITION OF %I.%I FOR VALUES FROM (%s) TO (%s)',
v_parent_schema, v_partition_name, v_parent_schema, p_parent_table, v_start_ts, v_end_ts
);
EXCEPTION WHEN invalid_object_definition THEN
-- Ignore overlap errors (e.g., when transitioning from daily to hourly partitioning)
RAISE NOTICE 'Partition % overlaps with an existing partition. Skipping.', v_partition_name;
END;
END IF;
END;
$$;
@@ -91,26 +98,47 @@ BEGIN
BEGIN
IF length(v_suffix) = 6 THEN -- YYYYMM
v_partition_date := timezone('UTC', to_timestamp(v_suffix || '01', 'YYYYMMDD')::timestamp without time zone);
-- For monthly, we check if the END of the month is older than retention?
-- Or just strict retention.
-- To be safe, adding 1 month to check vs cutoff.
IF extract(epoch from (v_partition_date + '1 month'::interval)) < v_cutoff_ts THEN
ELSIF length(v_suffix) = 8 THEN -- YYYYMMDD
v_partition_date := timezone('UTC', to_timestamp(v_suffix, 'YYYYMMDD')::timestamp without time zone);
ELSIF length(v_suffix) = 10 THEN -- YYYYMMDDHH
v_partition_date := timezone('UTC', to_timestamp(v_suffix, 'YYYYMMDDHH24')::timestamp without time zone);
ELSE
CONTINUE; -- Ignore non-matching suffix lengths
END IF;
EXCEPTION WHEN OTHERS THEN
-- Safely ignore parsing errors for oddly named partitions
CONTINUE;
END;
-- Now check retention and execute DROP TABLE (so dropping errors are correctly raised!)
IF length(v_suffix) = 6 THEN -- YYYYMM
IF extract(epoch from (v_partition_date + '1 month'::interval)) < v_cutoff_ts THEN
RAISE NOTICE 'Dropping old partition %', v_partition.partition_name;
EXECUTE format('DROP TABLE %I.%I', v_partition.partition_schema, v_partition.partition_name);
COMMIT; -- Release lock immediately
END IF;
ELSIF length(v_suffix) = 8 THEN -- YYYYMMDD
-- If period is weekly, the partition spans an entire week. Otherwise, it spans one day.
IF p_period = 'week' THEN
IF extract(epoch from (v_partition_date + '1 week'::interval)) < v_cutoff_ts THEN
RAISE NOTICE 'Dropping old partition %', v_partition.partition_name;
EXECUTE format('DROP TABLE %I.%I', v_partition.partition_schema, v_partition.partition_name);
COMMIT; -- Release lock immediately
END IF;
ELSIF length(v_suffix) = 8 THEN -- YYYYMMDD
v_partition_date := timezone('UTC', to_timestamp(v_suffix, 'YYYYMMDD')::timestamp without time zone);
ELSE
IF extract(epoch from (v_partition_date + '1 day'::interval)) < v_cutoff_ts THEN
RAISE NOTICE 'Dropping old partition %', v_partition.partition_name;
EXECUTE format('DROP TABLE %I.%I', v_partition.partition_schema, v_partition.partition_name);
COMMIT; -- Release lock immediately
END IF;
END IF;
EXCEPTION WHEN OTHERS THEN
-- Ignore parsing errors for non-standard partitions
NULL;
END;
ELSIF length(v_suffix) = 10 THEN -- YYYYMMDDHH
IF extract(epoch from (v_partition_date + p_period::interval)) < v_cutoff_ts THEN
RAISE NOTICE 'Dropping old partition %', v_partition.partition_name;
EXECUTE format('DROP TABLE %I.%I', v_partition.partition_schema, v_partition.partition_name);
COMMIT; -- Release lock immediately
END IF;
END IF;
END LOOP;
END;
$$;
@@ -145,8 +173,14 @@ BEGIN
v_start_time := date_trunc('month', now(), 'UTC');
-- Approximate 30 days per month (2592000 seconds)
v_past_iterations := ceil(extract(epoch from p_keep_history) / 2592000)::integer;
ELSIF p_period LIKE '%hour%' THEN
v_period_interval := p_period::interval;
v_start_time := to_timestamp(floor(extract(epoch from now()) / extract(epoch from v_period_interval)) * extract(epoch from v_period_interval));
v_past_iterations := ceil(extract(epoch from p_keep_history) / extract(epoch from v_period_interval))::integer;
ELSE
RETURN;
RAISE EXCEPTION 'Unsupported partitioning period: %', p_period;
END IF;
-- 1. Create Future Partitions (Current + Buffer)
@@ -192,3 +226,14 @@ BEGIN
END LOOP;
END;
$$;
-- Trigger function to silently discard housekeeper tasks for partitioned tables
CREATE OR REPLACE FUNCTION partitions.housekeeper_insert_trigger()
RETURNS TRIGGER AS $$
BEGIN
IF EXISTS (SELECT 1 FROM partitions.config WHERE table_name = NEW.tablename) THEN
RETURN NULL;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;