From b695cc8442cc59eb33d216b2b67688d0011e0d90 Mon Sep 17 00:00:00 2001 From: Maksym Buz Date: Mon, 11 May 2026 13:13:30 +0000 Subject: [PATCH] docs: restructure documentation and add invalid transaction termination exception --- postgresql/procedures/MANUAL.md | 12 ++++++++++-- postgresql/procedures/README.md | 22 ++++++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/postgresql/procedures/MANUAL.md b/postgresql/procedures/MANUAL.md index f6a7960..db977e5 100644 --- a/postgresql/procedures/MANUAL.md +++ b/postgresql/procedures/MANUAL.md @@ -87,6 +87,9 @@ If you use a visual database manager, simply open each file in your query editor Partitioning requires a daily job to create new partitions for tomorrow and drop old partitions from last month. +> [!WARNING] +> **CRITICAL EXECUTION RULE**: You MUST execute the maintenance procedure as a standalone command exactly as shown below. **DO NOT** wrap it in a transaction block (`BEGIN; ... COMMIT;`) or batch it with other SQL commands in a single string, or it will crash with an `invalid transaction termination` error. + If you are using **AWS RDS** or a managed database with `pg_cron` enabled, run this inside `psql`: ```sql @@ -94,7 +97,12 @@ CREATE EXTENSION IF NOT EXISTS pg_cron; SELECT cron.schedule('zabbix_partition_maintenance', '30 5,23 * * *', 'CALL partitions.run_maintenance();'); ``` -*(If you are self-hosting and don't have `pg_cron`, please refer to the `README.md` for instructions on setting up standard OS `cron` or systemd timers.)* +If you are **self-hosting** and prefer standard system `cron`, simply add this to your `crontab -e`: + +```bash +# Run Zabbix partition maintenance twice daily (5:30 AM and 11:30 PM) +30 5,23 * * * psql -U postgres -d zabbix -c "CALL partitions.run_maintenance();" >> /var/log/zabbix_maintenance.log 2>&1 +``` --- @@ -106,7 +114,7 @@ Now that the database is fully partitioned, you can safely start Zabbix Server a sudo systemctl start zabbix-server ``` -*(Note: Your old history data remains in tables like `history_old`. It is no longer visible in the UI. If you need it, you must manually insert it into the new tables. See `README.md` for more details.)* +*(Note: Your old history data remains safely preserved in tables like `history_old`. It is no longer visible in the UI, but it is available in the database if you ever need to manually migrate it.)* --- diff --git a/postgresql/procedures/README.md b/postgresql/procedures/README.md index c9a7462..78753be 100644 --- a/postgresql/procedures/README.md +++ b/postgresql/procedures/README.md @@ -39,8 +39,8 @@ All procedures, information, statistics and configuration are stored in the `par ## Installation > [!IMPORTANT] -> **Please refer to the [MANUAL.md](MANUAL.md) for the complete, step-by-step, foolproof installation instructions.** -> The manual contains critical safety procedures, backup warnings, and copy-pasteable commands for a safe deployment. +> **For deployment instructions, please refer to [MANUAL.md](MANUAL.md).** +> `MANUAL.md` contains the step-by-step, foolproof installation guide intended for customer sharing. This `README.md` serves as an internal knowledge base for maintaining, troubleshooting, and understanding the architecture. ## Configuration @@ -246,6 +246,9 @@ The enablement script guarantees practically zero downtime by automatically rena * New data flows into the new partitioned tables immediately. * Old data remains accessible in `table_name_old` for manual lookup or migration if required. +> [!WARNING] +> **Non-Atomic Enablement**: The `02_enable_partitioning.sql` script is executed within a `DO $$` block. However, because it calls the maintenance procedure (which issues `COMMIT` statements to manage locks), the enablement process is **not atomic**. If the script crashes halfway through (e.g., due to a missing table), it will not roll back the previously converted tables. The database will be in a partially partitioned state. Fortunately, the script is fully restartable. Simply fix the underlying issue and run the script again; it will skip already partitioned tables and resume where it left off. + ### Housekeeper Interceptor Even when Zabbix Housekeeping is disabled in the UI for History and Trends, the Zabbix Server daemon may still generate and insert tasks into the `housekeeper` table (e.g., when an item or trigger is deleted, it schedules the deletion of its historical data). Without intervention, this results in the `housekeeper` table bloating massively over time, leading to slow sequential scans and `autovacuum` overhead. @@ -254,6 +257,21 @@ To prevent this, this extension installs a `BEFORE INSERT` trigger on the `house * If the table is partitioned (like `history`), the trigger **silently discards the insert** (`RETURNS NULL`), preventing disk I/O and table bloat entirely. * If the table is not partitioned (like `events` or `sessions`), the task is allowed to be recorded and is cleaned up naturally by Zabbix. +## Troubleshooting & Known Exceptions + +### `invalid transaction termination` +When manually executing or scheduling the maintenance procedure (`CALL partitions.run_maintenance();`), you or the customer might encounter the following fatal error: +`ERROR: invalid transaction termination` + +**Why this happens:** +Inside the `partitions` PL/pgSQL procedures, we execute `COMMIT;` statements within loops to eagerly release locks and prevent transaction bloat when dropping dozens of partitions. PostgreSQL fundamentally prohibits executing `COMMIT` from within a procedure if that procedure was called from inside an explicit transaction block or a batched command string. + +**How to fix/avoid it:** +Ensure the procedure is ALWAYS executed as a top-level, standalone command. +* **Bad**: `BEGIN; CALL partitions.run_maintenance(); COMMIT;` +* **Bad**: `psql -c "UPDATE partitions.config SET keep_history = '1 day'; CALL partitions.run_maintenance();"` (psql batches these into one transaction) +* **Good**: `psql -c "CALL partitions.run_maintenance();"` + ## PostgreSQL Tuning Before or immediately after enabling partitioning, you should tune your `postgresql.conf`. The standard configuration is not optimized for partitioned tables and might cause performance degradation or out-of-memory errors.