diff --git a/database_autovacuum_tuning/README.rst b/database_autovacuum_tuning/README.rst new file mode 100644 index 00000000000..5dcbc419bbf --- /dev/null +++ b/database_autovacuum_tuning/README.rst @@ -0,0 +1,118 @@ +========================== +Database Autovacuum Tuning +========================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:162a7723c383e0b6239b46e50d6884767430de1a22f1c42a6c90d80e25b1b4fd + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github + :target: https://github.com/OCA/server-tools/tree/18.0/database_autovacuum_tuning + :alt: OCA/server-tools +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-tools-18-0/server-tools-18-0-database_autovacuum_tuning + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/server-tools&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Database Autovacuum Tuning helps administrators keep PostgreSQL healthy +by exposing recommended autovacuum settings in Odoo. It provides +guidance and documentation for sizing thresholds and scale factors so +large, busy databases avoid table bloat and excessive vacuum lag. Use it +to standardize autovacuum configuration across environments and speed up +maintenance operations without manual tuning. + +This module is mostly useful for PostgreSQL <= 17. PostgreSQL 18.0 +introduces the ``autovacuum_vacuum_max_threshold`` parameter, which +already provides the capability this module targets. + +The ``pgstattuple`` extension must be installed on the database. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +1. Install the module on the database you want to tune. + +2. Go to Settings > Technical > Database Structure > Database Autovacuum + Tuning and review the recommended thresholds and scale factors. + +3. If needed, override the defaults using the following system + parameters: + + - ``database_autovacuum_tuning.autovacuum_vacuum_max_threshold`` + - ``database_autovacuum_tuning.autovacuum_vacuum_analyze_max_threshold`` + +4. The configuration parameters are applied to tables by the daily cron + job. When the number of dead tuples in a table exceeds the vacuum + threshold, it applies the following configuration: + + .. code:: sql + + ALTER TABLE {schemaname}.{tablename} SET ( + autovacuum_vacuum_scale_factor = 0, + autovacuum_vacuum_threshold = %s, + autovacuum_analyze_scale_factor = 0, + autovacuum_analyze_threshold = %s + ) + +5. Monitor vacuum activity and table bloat, then adjust the settings if + your workload changes. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Camptocamp + +Contributors +------------ + +- Telmo Santos +- Alexandre Fayolle + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/server-tools `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/database_autovacuum_tuning/__init__.py b/database_autovacuum_tuning/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/database_autovacuum_tuning/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/database_autovacuum_tuning/__manifest__.py b/database_autovacuum_tuning/__manifest__.py new file mode 100644 index 00000000000..7b1508ca6d4 --- /dev/null +++ b/database_autovacuum_tuning/__manifest__.py @@ -0,0 +1,24 @@ +# Copyright 2026 Camptocamp (https://www.camptocamp.com). +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + +{ + "name": "Database Autovacuum Tuning", + "summary": "Scheduled checks for Odoo autovacuum thresholds and scale factors", + "version": "18.0.1.0.1", + "author": "Camptocamp, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/server-tools", + "category": "Tools", + "depends": [ + "base_setup", + ], + "data": [ + "data/config_parameter.xml", + "data/ir_cron.xml", + "security/ir.model.access.csv", + "views/res_config_settings_views.xml", + "views/database_autovacuum_tuning_views.xml", + ], + "development_status": "Alpha", + "license": "LGPL-3", + "installable": True, +} diff --git a/database_autovacuum_tuning/data/config_parameter.xml b/database_autovacuum_tuning/data/config_parameter.xml new file mode 100644 index 00000000000..057ab3e8bb6 --- /dev/null +++ b/database_autovacuum_tuning/data/config_parameter.xml @@ -0,0 +1,18 @@ + + + + database_autovacuum_tuning.autovacuum_vacuum_max_threshold + 100000 + + + database_autovacuum_tuning.autovacuum_vacuum_analyze_max_threshold + 50000 + + diff --git a/database_autovacuum_tuning/data/ir_cron.xml b/database_autovacuum_tuning/data/ir_cron.xml new file mode 100644 index 00000000000..7929b42f093 --- /dev/null +++ b/database_autovacuum_tuning/data/ir_cron.xml @@ -0,0 +1,13 @@ + + + + Database Autovacuum Tuning + + code + model._db_autovacuum_tune() + 1 + days + True + + + diff --git a/database_autovacuum_tuning/models/__init__.py b/database_autovacuum_tuning/models/__init__.py new file mode 100644 index 00000000000..c3700214bb1 --- /dev/null +++ b/database_autovacuum_tuning/models/__init__.py @@ -0,0 +1,2 @@ +from . import database_autovacuum_tuning +from . import res_config_settings diff --git a/database_autovacuum_tuning/models/database_autovacuum_tuning.py b/database_autovacuum_tuning/models/database_autovacuum_tuning.py new file mode 100644 index 00000000000..fc0cf017a0e --- /dev/null +++ b/database_autovacuum_tuning/models/database_autovacuum_tuning.py @@ -0,0 +1,85 @@ +# Copyright 2026 Camptocamp (https://www.camptocamp.com). +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + + +from odoo import api, fields, models + + +class DatabaseAutovacuumTuning(models.Model): + _name = "database.autovacuum.tuning" + _description = "Database Autovacuum Tuning" + + name = fields.Char(required=True, help="Table name") + vacuum_threshold = fields.Integer() + analyze_threshold = fields.Integer() + + @api.model + def _db_autovacuum_tune(self): + vacuum_threshold, analyze_threshold = self._get_thresholds() + if vacuum_threshold <= 0: + return + results = self._get_tables_exceeding_dead_tuples(vacuum_threshold) + for schemaname, tablename, _ in results: + self.env.cr.execute( + f""" + ALTER TABLE {schemaname}.{tablename} SET ( + autovacuum_vacuum_scale_factor = 0, + autovacuum_vacuum_threshold = %s, + autovacuum_analyze_scale_factor = 0, + autovacuum_analyze_threshold = %s + ) + """, + (vacuum_threshold, analyze_threshold), + ) + self.sudo().create( + { + "name": f"{schemaname}.{tablename}", + "vacuum_threshold": vacuum_threshold, + "analyze_threshold": analyze_threshold, + } + ) + + def _get_tables_exceeding_dead_tuples(self, vacuum_threshold): + query = """ + SELECT + t.schemaname, + t.tablename, + st.n_dead_tup + FROM pg_tables AS t + JOIN pg_stat_all_tables AS st + ON st.schemaname = t.schemaname + AND st.relname = t.tablename + WHERE t.tableowner = current_user + AND t.schemaname = 'public' + AND st.n_dead_tup > %s + ORDER BY t.schemaname, t.tablename + """ + self.env.cr.execute(query, (vacuum_threshold,)) + return self.env.cr.fetchall() + + def _get_thresholds(self): + try: + vacuum_threshold = int( + self.env["ir.config_parameter"] + .sudo() + .get_param( + "database_autovacuum_tuning.autovacuum_vacuum_max_threshold", + default="0", + ) + ) + except ValueError: + vacuum_threshold = 0 + + try: + analyze_threshold = int( + self.env["ir.config_parameter"] + .sudo() + .get_param( + "database_autovacuum_tuning.autovacuum_vacuum_analyze_max_threshold", + default="0", + ) + ) + except ValueError: + analyze_threshold = 0 + + return vacuum_threshold, analyze_threshold diff --git a/database_autovacuum_tuning/models/res_config_settings.py b/database_autovacuum_tuning/models/res_config_settings.py new file mode 100644 index 00000000000..f864d9daaab --- /dev/null +++ b/database_autovacuum_tuning/models/res_config_settings.py @@ -0,0 +1,14 @@ +# Copyright 2026 Camptocamp (https://www.camptocamp.com). +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + autovacuum_vacuum_max_threshold = fields.Integer( + config_parameter="database_autovacuum_tuning.autovacuum_vacuum_max_threshold", + ) + autovacuum_vacuum_analyze_max_threshold = fields.Integer( + config_parameter="database_autovacuum_tuning.autovacuum_vacuum_analyze_max_threshold", + ) diff --git a/database_autovacuum_tuning/pyproject.toml b/database_autovacuum_tuning/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/database_autovacuum_tuning/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/database_autovacuum_tuning/readme/CONTRIBUTORS.md b/database_autovacuum_tuning/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..62e88c57a16 --- /dev/null +++ b/database_autovacuum_tuning/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- Telmo Santos \<\> +- Alexandre Fayolle \<\> diff --git a/database_autovacuum_tuning/readme/DESCRIPTION.md b/database_autovacuum_tuning/readme/DESCRIPTION.md new file mode 100644 index 00000000000..9193c261573 --- /dev/null +++ b/database_autovacuum_tuning/readme/DESCRIPTION.md @@ -0,0 +1,12 @@ +Database Autovacuum Tuning helps administrators keep PostgreSQL healthy by +exposing recommended autovacuum settings in Odoo. It provides guidance and +documentation for sizing thresholds and scale factors so large, busy databases +avoid table bloat and excessive vacuum lag. Use it to standardize autovacuum +configuration across environments and speed up maintenance operations without +manual tuning. + +This module is mostly useful for PostgreSQL <= 17. PostgreSQL 18.0 introduces +the `autovacuum_vacuum_max_threshold` parameter, which already provides the +capability this module targets. + +The `pgstattuple` extension must be installed on the database. \ No newline at end of file diff --git a/database_autovacuum_tuning/readme/USAGE.md b/database_autovacuum_tuning/readme/USAGE.md new file mode 100644 index 00000000000..c82e039acc4 --- /dev/null +++ b/database_autovacuum_tuning/readme/USAGE.md @@ -0,0 +1,20 @@ +1. Install the module on the database you want to tune. +2. Go to Settings > Technical > Database Structure > Database Autovacuum + Tuning and review the recommended thresholds and scale factors. +3. If needed, override the defaults using the following system parameters: + - `database_autovacuum_tuning.autovacuum_vacuum_max_threshold` + - `database_autovacuum_tuning.autovacuum_vacuum_analyze_max_threshold` +4. The configuration parameters are applied to tables by the daily cron job. + When the number of dead tuples in a table exceeds the vacuum threshold, it + applies the following configuration: + + ```sql + ALTER TABLE {schemaname}.{tablename} SET ( + autovacuum_vacuum_scale_factor = 0, + autovacuum_vacuum_threshold = %s, + autovacuum_analyze_scale_factor = 0, + autovacuum_analyze_threshold = %s + ) + ``` +5. Monitor vacuum activity and table bloat, then adjust the settings if your + workload changes. diff --git a/database_autovacuum_tuning/security/ir.model.access.csv b/database_autovacuum_tuning/security/ir.model.access.csv new file mode 100644 index 00000000000..432a55c91b6 --- /dev/null +++ b/database_autovacuum_tuning/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_database_autovacuum_tuning_system,access_database_autovacuum_tuning_system,model_database_autovacuum_tuning,base.group_system,1,0,1,1 diff --git a/database_autovacuum_tuning/static/description/index.html b/database_autovacuum_tuning/static/description/index.html new file mode 100644 index 00000000000..e909053750c --- /dev/null +++ b/database_autovacuum_tuning/static/description/index.html @@ -0,0 +1,466 @@ + + + + + +Database Autovacuum Tuning + + + +
+

Database Autovacuum Tuning

+ + +

Beta License: LGPL-3 OCA/server-tools Translate me on Weblate Try me on Runboat

+

Database Autovacuum Tuning helps administrators keep PostgreSQL healthy +by exposing recommended autovacuum settings in Odoo. It provides +guidance and documentation for sizing thresholds and scale factors so +large, busy databases avoid table bloat and excessive vacuum lag. Use it +to standardize autovacuum configuration across environments and speed up +maintenance operations without manual tuning.

+

This module is mostly useful for PostgreSQL <= 17. PostgreSQL 18.0 +introduces the autovacuum_vacuum_max_threshold parameter, which +already provides the capability this module targets.

+

The pgstattuple extension must be installed on the database.

+

Table of contents

+ +
+

Usage

+
    +
  1. Install the module on the database you want to tune.

    +
  2. +
  3. Go to Settings > Technical > Database Structure > Database Autovacuum +Tuning and review the recommended thresholds and scale factors.

    +
  4. +
  5. If needed, override the defaults using the following system +parameters:

    +
      +
    • database_autovacuum_tuning.autovacuum_vacuum_max_threshold
    • +
    • database_autovacuum_tuning.autovacuum_vacuum_analyze_max_threshold
    • +
    +
  6. +
  7. The configuration parameters are applied to tables by the daily cron +job. When the number of dead tuples in a table exceeds the vacuum +threshold, it applies the following configuration:

    +
    +ALTER TABLE {schemaname}.{tablename} SET (
    +     autovacuum_vacuum_scale_factor = 0,
    +     autovacuum_vacuum_threshold = %s,
    +     autovacuum_analyze_scale_factor = 0,
    +     autovacuum_analyze_threshold = %s
    +)
    +
    +
  8. +
  9. Monitor vacuum activity and table bloat, then adjust the settings if +your workload changes.

    +
  10. +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Camptocamp
  • +
+
+ +
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/server-tools project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/database_autovacuum_tuning/tests/__init__.py b/database_autovacuum_tuning/tests/__init__.py new file mode 100644 index 00000000000..a17e75ae912 --- /dev/null +++ b/database_autovacuum_tuning/tests/__init__.py @@ -0,0 +1 @@ +from . import test_database_autovacuum_tunning diff --git a/database_autovacuum_tuning/tests/test_database_autovacuum_tunning.py b/database_autovacuum_tuning/tests/test_database_autovacuum_tunning.py new file mode 100644 index 00000000000..e285197e9e1 --- /dev/null +++ b/database_autovacuum_tuning/tests/test_database_autovacuum_tunning.py @@ -0,0 +1,36 @@ +# Copyright 2026 Camptocamp +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). + +from unittest.mock import patch + +from odoo.tests import TransactionCase + + +class TestAutovacuumTunning(TransactionCase): + def _set_thresholds(self, vacuum_threshold=1, analyze_threshold=1): + params = self.env["ir.config_parameter"].sudo() + params.set_param( + "database_autovacuum_tuning.autovacuum_vacuum_max_threshold", + str(vacuum_threshold), + ) + params.set_param( + "database_autovacuum_tuning.autovacuum_vacuum_analyze_max_threshold", + str(analyze_threshold), + ) + + def test_tune_creates_record_for_res_partner(self): + # Set low thresholds to ensure res_partner exceeds them + self._set_thresholds(vacuum_threshold=9, analyze_threshold=5) + # Mock the method to return res_partner as exceeding the thresholds + with patch.object( + self.env.registry["database.autovacuum.tuning"], + "_get_tables_exceeding_dead_tuples", + return_value=[("public", "res_partner", 10)], + ): + self.env["database.autovacuum.tuning"]._db_autovacuum_tune() + record = self.env["database.autovacuum.tuning"].search( + [("name", "=", "public.res_partner")], + limit=1, + ) + + self.assertTrue(record) diff --git a/database_autovacuum_tuning/views/database_autovacuum_tuning_views.xml b/database_autovacuum_tuning/views/database_autovacuum_tuning_views.xml new file mode 100644 index 00000000000..20c6e461166 --- /dev/null +++ b/database_autovacuum_tuning/views/database_autovacuum_tuning_views.xml @@ -0,0 +1,49 @@ + + + + database.autovacuum.tuning + + + + + + + + + + + + database.autovacuum.tuning + + + + + + + + + + + + + Database Vacuum Tuning + database.autovacuum.tuning + list + + + + + Database Autovacuum Tuning + + + + + diff --git a/database_autovacuum_tuning/views/res_config_settings_views.xml b/database_autovacuum_tuning/views/res_config_settings_views.xml new file mode 100644 index 00000000000..b6204ee3ab4 --- /dev/null +++ b/database_autovacuum_tuning/views/res_config_settings_views.xml @@ -0,0 +1,24 @@ + + + + Database Autovacuum Tuning Settings + res.config.settings + + + + + + + + + + + + +