mirror of
https://github.com/OCA/server-backend.git
synced 2025-02-18 09:52:42 +02:00
[ADD] pglogical
This commit is contained in:
97
pglogical/README.rst
Normal file
97
pglogical/README.rst
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
=========
|
||||||
|
pglogical
|
||||||
|
=========
|
||||||
|
|
||||||
|
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
!! This file is generated by oca-gen-addon-readme !!
|
||||||
|
!! changes will be overwritten. !!
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
|
||||||
|
.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
|
||||||
|
:target: https://odoo-community.org/page/development-status
|
||||||
|
:alt: Alpha
|
||||||
|
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
|
||||||
|
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||||
|
:alt: License: AGPL-3
|
||||||
|
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--backend-lightgray.png?logo=github
|
||||||
|
:target: https://github.com/OCA/server-backend/tree/12.0/pglogical
|
||||||
|
:alt: OCA/server-backend
|
||||||
|
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
||||||
|
:target: https://translation.odoo-community.org/projects/server-backend-12-0/server-backend-12-0-pglogical
|
||||||
|
:alt: Translate me on Weblate
|
||||||
|
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
|
||||||
|
:target: https://runbot.odoo-community.org/runbot/253/12.0
|
||||||
|
:alt: Try me on Runbot
|
||||||
|
|
||||||
|
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||||
|
|
||||||
|
This module supports replication of an Odoo database with `pglogical <https://github.com/2ndQuadrant/pglogical>`__ by also passing DDL to the replicas. You can configure which replica sets to pass DDL to.
|
||||||
|
|
||||||
|
.. IMPORTANT::
|
||||||
|
This is an alpha version, the data model and design can change at any time without warning.
|
||||||
|
Only for development or testing purpose, do not use in production.
|
||||||
|
`More details on development status <https://odoo-community.org/page/development-status>`_
|
||||||
|
|
||||||
|
**Table of contents**
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
:local:
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
|
To configure this module, you need to:
|
||||||
|
|
||||||
|
#. add section ``[pglogical]`` to your odoo configuration file
|
||||||
|
#. add value ``replication_sets = set1,set2,set3,etc`` in this section
|
||||||
|
#. add ``pglogical`` to your list of server wide modules
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
[pglogical]
|
||||||
|
replication_sets = ddl_sql
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
If configured correctly, this module is completely transparent for users.
|
||||||
|
|
||||||
|
Bug Tracker
|
||||||
|
===========
|
||||||
|
|
||||||
|
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-backend/issues>`_.
|
||||||
|
In case of trouble, please check there if your issue has already been reported.
|
||||||
|
If you spotted it first, help us smashing it by providing a detailed and welcomed
|
||||||
|
`feedback <https://github.com/OCA/server-backend/issues/new?body=module:%20pglogical%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||||
|
|
||||||
|
Do not contact contributors directly about support or help with technical issues.
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Authors
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
* Hunki Enterprises BV
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Holger Brunn <mail@hunki-enterprises.com> (https://hunki-enterprises.com)
|
||||||
|
|
||||||
|
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-backend <https://github.com/OCA/server-backend/tree/12.0/pglogical>`_ project on GitHub.
|
||||||
|
|
||||||
|
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||||
3
pglogical/__init__.py
Normal file
3
pglogical/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from .hooks import post_load
|
||||||
13
pglogical/__manifest__.py
Normal file
13
pglogical/__manifest__.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Copyright 2022 Hunki Enterprises BV
|
||||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||||
|
{
|
||||||
|
"name": "pglogical",
|
||||||
|
"summary": "Support for replicating Odoo's database",
|
||||||
|
"version": "12.0.1.0.0",
|
||||||
|
"development_status": "Alpha",
|
||||||
|
"category": "Tools",
|
||||||
|
"website": "https://github.com/OCA/server-backend",
|
||||||
|
"author": "Hunki Enterprises BV, Odoo Community Association (OCA)",
|
||||||
|
"license": "AGPL-3",
|
||||||
|
"post_load": "post_load",
|
||||||
|
}
|
||||||
50
pglogical/hooks.py
Normal file
50
pglogical/hooks.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# Copyright 2022 Hunki Enterprises BV
|
||||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||||
|
import logging
|
||||||
|
from odoo.sql_db import Cursor
|
||||||
|
from odoo.tools import config
|
||||||
|
|
||||||
|
SECTION_NAME = "pglogical"
|
||||||
|
|
||||||
|
|
||||||
|
def post_load():
|
||||||
|
"""
|
||||||
|
Patch cursor to funnel DDL through pglogical.replicate_ddl_command if configured to
|
||||||
|
do so
|
||||||
|
"""
|
||||||
|
|
||||||
|
_logger = logging.getLogger("odoo.addons.pglogical")
|
||||||
|
|
||||||
|
if SECTION_NAME not in config.misc:
|
||||||
|
_logger.info("%s section missing in config, not doing anything", SECTION_NAME)
|
||||||
|
return
|
||||||
|
replication_sets = list(
|
||||||
|
filter(None, config.misc[SECTION_NAME].get("replication_sets", "").split(","))
|
||||||
|
)
|
||||||
|
if not replication_sets:
|
||||||
|
_logger.error("no replication sets defined, not doing anything")
|
||||||
|
return
|
||||||
|
if config["test_enable"]:
|
||||||
|
_logger.info("test mode enabled, not doing anything")
|
||||||
|
return
|
||||||
|
|
||||||
|
_logger.info("patching cursor to intercept ddl")
|
||||||
|
execute_org = Cursor.execute
|
||||||
|
|
||||||
|
def execute(self, query, params=None, log_exceptions=None):
|
||||||
|
"""Wrap DDL in pglogical.replicate_ddl_command"""
|
||||||
|
if (query[:6] == "CREATE" or query[:5] == "ALTER" or query[:4] == "DROP") and (
|
||||||
|
# don't replicate constraints, triggers, indexes
|
||||||
|
"CONSTRAINT" not in query
|
||||||
|
and "TRIGGER" not in query
|
||||||
|
and "INDEX" not in query
|
||||||
|
):
|
||||||
|
mogrified = self.mogrify(query, params).decode("utf8")
|
||||||
|
query = "SELECT pglogical.replicate_ddl_command(%s, %s)"
|
||||||
|
params = (mogrified, execute.replication_sets)
|
||||||
|
return execute.origin(self, query, params=params, log_exceptions=log_exceptions)
|
||||||
|
|
||||||
|
execute.origin = execute_org
|
||||||
|
execute.replication_sets = replication_sets
|
||||||
|
|
||||||
|
Cursor.execute = execute
|
||||||
10
pglogical/readme/CONFIGURE.rst
Normal file
10
pglogical/readme/CONFIGURE.rst
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
To configure this module, you need to:
|
||||||
|
|
||||||
|
#. add section ``[pglogical]`` to your odoo configuration file
|
||||||
|
#. add value ``replication_sets = set1,set2,set3,etc`` in this section
|
||||||
|
#. add ``pglogical`` to your list of server wide modules
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
[pglogical]
|
||||||
|
replication_sets = ddl_sql
|
||||||
1
pglogical/readme/CONTRIBUTORS.rst
Normal file
1
pglogical/readme/CONTRIBUTORS.rst
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* Holger Brunn <mail@hunki-enterprises.com> (https://hunki-enterprises.com)
|
||||||
1
pglogical/readme/DESCRIPTION.rst
Normal file
1
pglogical/readme/DESCRIPTION.rst
Normal file
@@ -0,0 +1 @@
|
|||||||
|
This module supports replication of an Odoo database with `pglogical <https://github.com/2ndQuadrant/pglogical>`__ by also passing DDL to the replicas. You can configure which replica sets to pass DDL to.
|
||||||
1
pglogical/readme/USAGE.rst
Normal file
1
pglogical/readme/USAGE.rst
Normal file
@@ -0,0 +1 @@
|
|||||||
|
If configured correctly, this module is completely transparent for users.
|
||||||
BIN
pglogical/static/description/icon.png
Normal file
BIN
pglogical/static/description/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.2 KiB |
124
pglogical/static/description/index.html
Normal file
124
pglogical/static/description/index.html
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
<section class="oe_container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<div class="oe_span12">
|
||||||
|
<h2 class="oe_slogan">Module name</h2>
|
||||||
|
<p>This module was written to extend the functionality of ... to support ... and allow you to ...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container oe_dark">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<div class="oe_span12">
|
||||||
|
<h2 class="oe_slogan">Installation</h2>
|
||||||
|
</div>
|
||||||
|
<div class="oe_span6">
|
||||||
|
<p class="oe_mt32">To install this module, you need to:
|
||||||
|
<ul>
|
||||||
|
<li>...</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="oe_span6">
|
||||||
|
<div class="oe_demo oe_picture oe_screenshot">
|
||||||
|
<a href="https://www.odoo.com/saas_master/demo?lang=en_US&module=crm">
|
||||||
|
<img src="crm_sc_01.png">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<div class="oe_span12">
|
||||||
|
<h2 class="oe_slogan">Configuration</h2>
|
||||||
|
</div>
|
||||||
|
<div class="oe_span6">
|
||||||
|
<p class="oe_mt32">To configure this module, you need to:
|
||||||
|
<ul>
|
||||||
|
<li>...</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="oe_span6">
|
||||||
|
<div class="oe_demo oe_picture oe_screenshot">
|
||||||
|
<a href="https://www.odoo.com/saas_master/demo?lang=en_US&module=crm">
|
||||||
|
<img src="crm_sc_01.png">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container oe_dark">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<div class="oe_span12">
|
||||||
|
<h2 class="oe_slogan">Usage</h2>
|
||||||
|
</div>
|
||||||
|
<div class="oe_span6">
|
||||||
|
<p class="oe_mt32">To use this module, you need to:
|
||||||
|
<ul>
|
||||||
|
<li>...</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
<p class="oe_mt32">For further information, please visit:
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://www.odoo.com/forum/help-1">https://www.odoo.com/forum/help-1</a></li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="oe_span6">
|
||||||
|
<div class="oe_demo oe_picture oe_screenshot">
|
||||||
|
<a href="https://www.odoo.com/saas_master/demo?lang=en_US&module=crm">
|
||||||
|
<img src="crm_sc_01.png">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<div class="oe_span12">
|
||||||
|
<h2 class="oe_slogan">Known issues / Roadmap</h2>
|
||||||
|
</div>
|
||||||
|
<div class="oe_span6">
|
||||||
|
<p class="oe_mt32">
|
||||||
|
<ul>
|
||||||
|
<li>...</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="oe_span6">
|
||||||
|
<div class="oe_demo oe_picture oe_screenshot">
|
||||||
|
<a href="https://www.odoo.com/saas_master/demo?lang=en_US&module=crm">
|
||||||
|
<img src="crm_sc_01.png">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container oe_dark">
|
||||||
|
<div class="oe_row">
|
||||||
|
<div class="oe_span12">
|
||||||
|
<h2 class="oe_slogan">Credits</h2>
|
||||||
|
</div>
|
||||||
|
<div class="oe_span12">
|
||||||
|
<h3>Contributors</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Firstname Lastname <<a href="mailto:email.address@example.com">email.address@example.com</a>></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="oe_span12">
|
||||||
|
<h3>Maintainer</h3>
|
||||||
|
<p>
|
||||||
|
This module is maintained by the OCA.<br/>
|
||||||
|
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.<br/>
|
||||||
|
To contribute to this module, please visit <a href="http://odoo-community.org">http://odoo-community.org</a>.<br/>
|
||||||
|
<a href="http://odoo-community.org"><img class="oe_picture oe_centered" src="http://odoo-community.org/logo.png"></a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
3
pglogical/tests/__init__.py
Normal file
3
pglogical/tests/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from . import test_pglogical
|
||||||
92
pglogical/tests/test_pglogical.py
Normal file
92
pglogical/tests/test_pglogical.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
# Copyright 2022 Hunki Enterprises BV
|
||||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
import mock
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from odoo.sql_db import Cursor
|
||||||
|
from odoo.tests.common import TransactionCase
|
||||||
|
from odoo.tools.config import config
|
||||||
|
from ..hooks import post_load
|
||||||
|
|
||||||
|
|
||||||
|
class TestPglogical(TransactionCase):
|
||||||
|
@contextmanager
|
||||||
|
def _config(self, misc, test_enable=False):
|
||||||
|
"""
|
||||||
|
Temporarily change the config to test_enable=False and impose a custom misc
|
||||||
|
section
|
||||||
|
"""
|
||||||
|
original_misc = config.misc
|
||||||
|
config.misc = misc
|
||||||
|
config["test_enable"] = test_enable
|
||||||
|
yield
|
||||||
|
config["test_enable"] = True
|
||||||
|
config.misc = original_misc
|
||||||
|
|
||||||
|
def test_configuration(self):
|
||||||
|
"""Test we react correctly to misconfigurations"""
|
||||||
|
with self._config(
|
||||||
|
dict(pglogical=dict(replication_sets="nothing")), True
|
||||||
|
), self.assertLogs("odoo.addons.pglogical") as log:
|
||||||
|
post_load()
|
||||||
|
self.assertEqual(
|
||||||
|
log.output,
|
||||||
|
["INFO:odoo.addons.pglogical:test mode enabled, not doing anything"],
|
||||||
|
)
|
||||||
|
|
||||||
|
with self._config({}), self.assertLogs("odoo.addons.pglogical") as log:
|
||||||
|
post_load()
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
log.output,
|
||||||
|
[
|
||||||
|
"INFO:odoo.addons.pglogical:pglogical section missing in config, "
|
||||||
|
"not doing anything"
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
with self._config(dict(pglogical={"hello": "world"})), self.assertLogs(
|
||||||
|
"odoo.addons.pglogical"
|
||||||
|
) as log:
|
||||||
|
post_load()
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
log.output,
|
||||||
|
[
|
||||||
|
"ERROR:odoo.addons.pglogical:no replication sets defined, "
|
||||||
|
"not doing anything"
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_patching(self):
|
||||||
|
"""Test patching the cursor succeeds"""
|
||||||
|
with self._config(dict(pglogical=dict(replication_sets="set1,set2"))):
|
||||||
|
try:
|
||||||
|
post_load()
|
||||||
|
self.assertTrue(getattr(Cursor.execute, "origin", False))
|
||||||
|
with mock.patch.object(self.env.cr, "_obj") as mock_cursor:
|
||||||
|
self.env.cr.execute("ALTER TABLE test ADD COLUMN test varchar")
|
||||||
|
self.assertIn(
|
||||||
|
"pglogical.replicate_ddl_command",
|
||||||
|
mock_cursor.execute.call_args[0][0],
|
||||||
|
)
|
||||||
|
|
||||||
|
with mock.patch.object(self.env.cr, "_obj") as mock_cursor:
|
||||||
|
self.env.cr.execute(
|
||||||
|
"ALTER TABLE test ADD CONSTRAINT test unique(id)"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertNotIn(
|
||||||
|
"pglogical.replicate_ddl_command",
|
||||||
|
mock_cursor.execute.call_args[0][0],
|
||||||
|
)
|
||||||
|
|
||||||
|
with mock.patch.object(self.env.cr, "_obj") as mock_cursor:
|
||||||
|
self.env.cr.execute("SELECT * from test")
|
||||||
|
|
||||||
|
self.assertNotIn(
|
||||||
|
"pglogical.replicate_ddl_command",
|
||||||
|
mock_cursor.execute.call_args[0][0],
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
Cursor.execute = getattr(Cursor.execute, "origin", Cursor.execute)
|
||||||
Reference in New Issue
Block a user