Merge "Add support for PostgreSQL 13 and above."

This commit is contained in:
Zuul 2025-03-27 14:38:04 +00:00 committed by Gerrit Code Review
commit e074043b0f
6 changed files with 166 additions and 17 deletions

View File

@ -0,0 +1,11 @@
---
fixes:
- |
Fixes support for PostgreSQL v13 and above.
Sets the `wal_keep_size` or `wal_keep_segments` in the instance
configuration file depending on the version in the data store version name.
The version number is parsed from the Datastore Version name and is exposed
to the database instance configuration templates to allow the the use of
conditional based on the version.
`Story 2008285 <https://storyboard.openstack.org/#!/story/2008285>`__

View File

@ -17,6 +17,8 @@
from oslo_config import cfg as oslo_config
from oslo_log import log as logging
from semantic_version import Version
from trove.common import cfg
from trove.common import configurations
from trove.common import exception
@ -69,15 +71,20 @@ class SingleInstanceConfigTemplate(object):
'name': self.datastore_version.datastore_name,
'manager': self.datastore_version.manager,
'version': self.datastore_version.name,
'semantic_version': self._parse_datastore_version(),
}
self.instance_id = instance_id
def get_template(self):
patterns = ['{name}/{version}/{template_name}',
'{name}/{major}.{minor}/{template_name}',
'{name}/{major}/{template_name}',
'{name}/{template_name}',
'{manager}/{template_name}']
context = self.datastore_dict.copy()
context['template_name'] = self.template_name
context['major'] = str(context['semantic_version'].major)
context['minor'] = str(context['semantic_version'].minor)
names = [name.format(**context) for name in patterns]
return ENV.select_template(names)
@ -115,6 +122,24 @@ class SingleInstanceConfigTemplate(object):
"""
return abs(hash(self.instance_id) % (2 ** 31))
def _parse_datastore_version(self):
"""
Attempt to parse a version from the DatastoreVersion name.
Returns version 0.0.0 if unable to parse.
:return: A Version instance.
"""
try:
return Version.coerce(self.datastore_version.version or
self.datastore_version.name)
except ValueError:
LOG.warning('Unable to parse a version number from datastore '
'version name "%s" or "%s"',
self.datastore_version.name,
self.datastore_version.version)
# TODO(adrianjarvis) define default version for each datastore
return Version(major=0, minor=0, patch=0)
def _validate_datastore(datastore_manager):
try:

View File

@ -297,7 +297,11 @@ restore_command = 'cp /var/lib/postgresql/data/wal_archive/%f "%p"' # command t
#max_wal_senders = 10 # max number of walsender processes
# (change requires restart)
{% if datastore.semantic_version.major >= 13 %}
wal_keep_size = 80 # in MB = wal_keep_segments x wal_keep_size
{% else %}
wal_keep_segments = 5 # in logfile segments; 0 disables
{% endif %}
# (Trove default)
#wal_sender_timeout = 60s # in milliseconds; 0 disables

View File

@ -906,6 +906,12 @@
"min": 0,
"type": "integer"
},
{
"name": "wal_keep_size",
"restart_required": true,
"min": 0,
"type": "integer"
},
{
"name": "wal_log_hints",
"restart_required": true,

View File

@ -12,12 +12,26 @@
import re
from unittest.mock import Mock
from unittest.mock import patch
from semantic_version import Version
from trove.common import template
from trove.common.utils import ENV
from trove.datastore.models import DatastoreVersion
from trove.tests.unittests import trove_testtools
def mock_datastore_version(datastore_name='MySQL', name='mysql-5.7',
manager='mysql', version=''):
datastore = Mock(spec=DatastoreVersion)
datastore.datastore_name = datastore_name
datastore.name = name
datastore.manager = manager
datastore.version = version
return datastore
class TemplateTest(trove_testtools.TestCase):
def setUp(self):
super(TemplateTest, self).setUp()
@ -58,10 +72,7 @@ class TemplateTest(trove_testtools.TestCase):
self.server_id)
def test_single_instance_config_rendering(self):
datastore = Mock(spec=DatastoreVersion)
datastore.datastore_name = 'MySql'
datastore.name = 'mysql-5.7'
datastore.manager = 'mysql'
datastore = mock_datastore_version()
config = template.SingleInstanceConfigTemplate(datastore,
self.flavor_dict,
self.server_id)
@ -70,10 +81,9 @@ class TemplateTest(trove_testtools.TestCase):
def test_renderer_discovers_special_config(self):
"""Finds our special config file for the version 'mysql-test'."""
datastore = Mock(spec=DatastoreVersion)
datastore.datastore_name = 'mysql'
datastore.name = 'mysql-test'
datastore.manager = 'mysql'
datastore = mock_datastore_version(name='mysql-test',
manager='mysql',
datastore_name='mysql')
config = template.SingleInstanceConfigTemplate(datastore,
self.flavor_dict,
self.server_id)
@ -81,21 +91,106 @@ class TemplateTest(trove_testtools.TestCase):
{'ram': 0}, self.server_id)
def test_replica_source_config_rendering(self):
datastore = Mock(spec=DatastoreVersion)
datastore.datastore_name = 'MySql'
datastore.name = 'mysql-5.7'
datastore.manager = 'mysql'
datastore = mock_datastore_version()
config = template.ReplicaSourceConfigTemplate(datastore,
self.flavor_dict,
self.server_id)
self.assertTrue(self._find_in_template(config.render(), "log_bin"))
def test_replica_config_rendering(self):
datastore = Mock(spec=DatastoreVersion)
datastore.datastore_name = 'MySql'
datastore.name = 'mysql-5.7'
datastore.manager = 'mysql'
datastore = mock_datastore_version()
config = template.ReplicaConfigTemplate(datastore,
self.flavor_dict,
self.server_id)
self.assertTrue(self._find_in_template(config.render(), "relay_log"))
def test_replica_config_rendering_mysql_v8(self):
datastore = mock_datastore_version(name='8.0')
config = template.ReplicaConfigTemplate(datastore,
self.flavor_dict,
self.server_id)
self.assertTrue(self._find_in_template(config.render(),
"binlog_format"))
def test_config_postgresql_pre_v13_sets_wal_keep_segments(self):
datastore = mock_datastore_version(datastore_name='PostgreSQL',
name='12.17',
manager='postgresql')
config = template.SingleInstanceConfigTemplate(datastore,
self.flavor_dict,
self.server_id)
self.assertTrue(
self._find_in_template(config.render(), "wal_keep_segments"))
self.assertIsNone(
self._find_in_template(config.render(), "wal_keep_size"))
def test_config_postgresql_post_v12_sets_wal_keep_size(self):
datastore = mock_datastore_version(datastore_name='PostgreSQL',
name='13.2',
manager='postgresql')
config = template.SingleInstanceConfigTemplate(datastore,
self.flavor_dict,
self.server_id)
self.assertTrue(
self._find_in_template(config.render(), "wal_keep_size"))
self.assertIsNone(
self._find_in_template(config.render(), "wal_keep_segments"))
def test_parse_version_name_missing_release_number(self):
datastore = mock_datastore_version(datastore_name='PostgreSQL',
name='test',
manager='postgresql')
config = template.SingleInstanceConfigTemplate(datastore,
self.flavor_dict,
self.server_id)
self.assertEqual(Version(major=0, minor=0, patch=0),
config._parse_datastore_version())
def test_parse_version_release_part_of_name(self):
datastore = mock_datastore_version(datastore_name='PostgreSQL',
name='test-12.17',
manager='postgresql')
config = template.SingleInstanceConfigTemplate(datastore,
self.flavor_dict,
self.server_id)
self.assertEqual(Version(major=0, minor=0, patch=0),
config._parse_datastore_version())
def test_parse_version_name_with_prefix(self):
datastore = mock_datastore_version(datastore_name='PostgreSQL',
name='16.1-test',
manager='postgresql')
config = template.SingleInstanceConfigTemplate(datastore,
self.flavor_dict,
self.server_id)
self.assertEqual(Version(major=16, minor=1, patch=0,
prerelease=('test',)),
config._parse_datastore_version())
def test_parse_version_prefer_version_if_set(self):
datastore = mock_datastore_version(datastore_name='PostgreSQL',
name='test',
manager='postgresql',
version='16.1')
config = template.SingleInstanceConfigTemplate(datastore,
self.flavor_dict,
self.server_id)
self.assertEqual(Version(major=16, minor=1, patch=0,
),
config._parse_datastore_version())
def test_template_paths(self):
datastore = mock_datastore_version(name='5.7.9')
config = template.SingleInstanceConfigTemplate(datastore,
self.flavor_dict,
self.server_id)
with patch.object(ENV, 'select_template') as select_template_mock:
config.get_template()
select_template_mock.assert_called_with(
['MySQL/5.7.9/config.template',
'MySQL/5.7/config.template',
'MySQL/5/config.template',
'MySQL/config.template',
'mysql/config.template'
]
)

View File

@ -817,9 +817,17 @@ class BuiltInstanceTasksTest(trove_testtools.TestCase):
# this is used during the final check of whether the resize successful
db_instance.server_status = 'HEALTHY'
self.db_instance = db_instance
datastore_version = MagicMock(
spec=datastore_models.DatastoreVersion)
datastore_version.id = '1'
datastore_version.name = '5.7'
datastore_version.version = '5.7'
datastore_version.datastore_id = 'id-1'
datastore_version.manager = 'mysql'
datastore_version.datastore_name = 'mysql'
self.dm_dv_load_by_uuid_patch = patch.object(
datastore_models.DatastoreVersion, 'load_by_uuid', MagicMock(
return_value=datastore_models.DatastoreVersion(db_instance)))
return_value=datastore_version))
self.dm_dv_load_by_uuid_mock = self.dm_dv_load_by_uuid_patch.start()
self.addCleanup(self.dm_dv_load_by_uuid_patch.stop)
self.dm_ds_load_patch = patch.object(