diff --git a/postgresql/tests/docker/init_scripts/01_10_schema_create.sql b/postgresql/tests/docker/init_scripts/01_10_schema_create.sql index b194b90..46761b1 100644 --- a/postgresql/tests/docker/init_scripts/01_10_schema_create.sql +++ b/postgresql/tests/docker/init_scripts/01_10_schema_create.sql @@ -22,7 +22,7 @@ CREATE TABLE IF NOT EXISTS partitions.version ( description text ); -INSERT INTO partitions.version (version, description) VALUES ('7-1', 'Zabbix 7.4 and 7.0 compatible version') +INSERT INTO partitions.version (version, description) VALUES ('7-2', 'Added housekeeper task interceptor trigger to drop tasks for partitioned tables') ON CONFLICT (version) DO NOTHING; -- Default configuration for Zabbix tables (adjust as needed) @@ -32,9 +32,16 @@ INSERT INTO partitions.config (table_name, period, keep_history) VALUES ('history_uint', 'day', '30 days'), ('history_str', 'day', '30 days'), ('history_log', 'day', '30 days'), -('history_text', 'day', '30 days') +('history_text', 'day', '30 days'), +('history_bin', 'day', '30 days') ON CONFLICT (table_name) DO NOTHING; +-- Zabbix 8.0+ only: Uncomment the following lines if running Zabbix 8.0 or later +-- INSERT INTO partitions.config (table_name, period, keep_history) VALUES +-- ('history_json', 'day', '30 days') +-- ON CONFLICT (table_name) DO NOTHING; + + -- Trends tables: Monthly partitions, keep 12 months INSERT INTO partitions.config (table_name, period, keep_history) VALUES ('trends', 'month', '12 months'), diff --git a/postgresql/tests/docker/init_scripts/01_30_maintenance.sql b/postgresql/tests/docker/init_scripts/01_30_maintenance.sql index 56b4a17..af60ce3 100644 --- a/postgresql/tests/docker/init_scripts/01_30_maintenance.sql +++ b/postgresql/tests/docker/init_scripts/01_30_maintenance.sql @@ -2,14 +2,15 @@ -- Core functions for Zabbix partitioning (Create, Drop, Maintain). -- ============================================================================ --- Function to check if a partition exists -CREATE OR REPLACE FUNCTION partitions.partition_exists(p_partition_name text) +-- Function to check if a partition exists in a specific schema +CREATE OR REPLACE FUNCTION partitions.partition_exists(p_partition_name text, p_schema text) RETURNS boolean AS $$ BEGIN RETURN EXISTS ( SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relname = p_partition_name + AND n.nspname = p_schema ); END; $$ LANGUAGE plpgsql; @@ -32,7 +33,7 @@ BEGIN SELECT n.nspname INTO v_parent_schema FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace - WHERE c.relname = p_parent_table; + WHERE c.relname = p_parent_table AND pg_table_is_visible(c.oid); IF NOT FOUND THEN RAISE EXCEPTION 'Parent table % not found', p_parent_table; @@ -51,15 +52,19 @@ BEGIN v_partition_name := p_parent_table || '_p' || v_suffix; - IF NOT partitions.partition_exists(v_partition_name) THEN + IF NOT partitions.partition_exists(v_partition_name, v_parent_schema) THEN 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; + 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; + WHEN duplicate_table THEN + -- Ignore race condition: another process created the partition concurrently + RAISE NOTICE 'Partition % already exists (concurrent creation). Skipping.', v_partition_name; END; END IF; END; @@ -89,7 +94,7 @@ BEGIN JOIN pg_class parent ON pg_inherits.inhparent = parent.oid JOIN pg_class child ON pg_inherits.inhrelid = child.oid JOIN pg_namespace n ON child.relnamespace = n.oid - WHERE parent.relname = p_parent_table + WHERE parent.relname = p_parent_table AND pg_table_is_visible(parent.oid) LOOP -- Parse partition suffix to determine age -- Format: parent_pYYYYMM or parent_pYYYYMMDD diff --git a/postgresql/tests/docker/init_scripts/01_40_enable.sql b/postgresql/tests/docker/init_scripts/01_40_enable.sql index d6cb961..b256c1d 100644 --- a/postgresql/tests/docker/init_scripts/01_40_enable.sql +++ b/postgresql/tests/docker/init_scripts/01_40_enable.sql @@ -19,10 +19,10 @@ BEGIN SELECT n.nspname INTO v_schema FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace - WHERE c.relname = v_table; + WHERE c.relname = v_table AND pg_table_is_visible(c.oid); - IF EXISTS (SELECT 1 FROM pg_class WHERE relname = v_table AND relkind = 'r') THEN + IF EXISTS (SELECT 1 FROM pg_class WHERE relname = v_table AND relkind = 'r' AND pg_table_is_visible(oid)) THEN RAISE NOTICE 'Converting table % to partitioned table...', v_table; -- 1. Rename existing table @@ -48,19 +48,37 @@ BEGIN -- Optional: Migrate existing data -- EXECUTE format('INSERT INTO %I.%I SELECT * FROM %I.%I', v_schema, v_table, v_schema, v_old_table); - ELSIF EXISTS (SELECT 1 FROM pg_class WHERE relname = v_table AND relkind = 'p') THEN + ELSIF EXISTS (SELECT 1 FROM pg_class WHERE relname = v_table AND relkind = 'p' AND pg_table_is_visible(oid)) THEN RAISE NOTICE 'Table % is already partitioned. Skipping conversion.', v_table; - -- Just run maintenance to ensure partitions exist - CALL partitions.run_maintenance(); + -- Just run maintenance for this specific table to ensure partitions exist + CALL partitions.maintain_table(v_table, v_row.period, v_row.keep_history, v_row.future_partitions); ELSE RAISE WARNING 'Table % not found!', v_table; END IF; END LOOP; + + -- Attach trigger to housekeeper table to silently discard tasks for partitioned tables. + -- Dynamically determine the schema of the housekeeper table to support custom schemas. + SELECT n.nspname INTO v_schema + FROM pg_class c + JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE c.relname = 'housekeeper' AND pg_table_is_visible(c.oid); + + IF v_schema IS NOT NULL THEN + EXECUTE format('DROP TRIGGER IF EXISTS housekeeper_filter ON %I.housekeeper', v_schema); + EXECUTE format('CREATE TRIGGER housekeeper_filter BEFORE INSERT ON %I.housekeeper FOR EACH ROW EXECUTE FUNCTION partitions.housekeeper_insert_trigger()', v_schema); + RAISE NOTICE 'Housekeeper intercept trigger installed on %.housekeeper', v_schema; + ELSE + RAISE WARNING 'housekeeper table not found — trigger NOT installed!'; + END IF; END $$; --- Attach trigger to housekeeper table to silently discard tasks for partitioned tables -DROP TRIGGER IF EXISTS housekeeper_filter ON housekeeper; -CREATE TRIGGER housekeeper_filter -BEFORE INSERT ON housekeeper -FOR EACH ROW -EXECUTE FUNCTION partitions.housekeeper_insert_trigger(); +-- ========================================================================== +-- IMPORTANT: If the Zabbix Server connects with a non-superuser (e.g., 'zabbix'), +-- that user MUST have access to the partitions schema for the housekeeper trigger +-- to work. Without these GRANTs, every INSERT into housekeeper will FAIL. +-- Uncomment and adjust the username below: +-- ========================================================================== +-- GRANT USAGE ON SCHEMA partitions TO zabbix; +-- GRANT SELECT ON partitions.config TO zabbix; + diff --git a/postgresql/tests/docker/init_scripts/01_50_monitoring.sql b/postgresql/tests/docker/init_scripts/01_50_monitoring.sql index eb77ade..705bd95 100644 --- a/postgresql/tests/docker/init_scripts/01_50_monitoring.sql +++ b/postgresql/tests/docker/init_scripts/01_50_monitoring.sql @@ -2,8 +2,7 @@ -- Creates a view to monitor partition status and sizes. -- ============================================================================ -DROP VIEW IF EXISTS partitions.monitoring; -CREATE VIEW partitions.monitoring AS +CREATE OR REPLACE VIEW partitions.monitoring AS SELECT parent.relname AS parent_table, c.table_name, @@ -27,7 +26,7 @@ SELECT max(child.relname) AS newest_partition, c.last_updated FROM partitions.config c -JOIN pg_class parent ON parent.relname = c.table_name +JOIN pg_class parent ON parent.relname = c.table_name AND pg_table_is_visible(parent.oid) LEFT JOIN pg_inherits ON pg_inherits.inhparent = parent.oid LEFT JOIN pg_class child ON pg_inherits.inhrelid = child.oid WHERE parent.relkind = 'p' -- Only partitioned tables