From 17e523b175d0bb515c0202d52ab11a04faf12d92 Mon Sep 17 00:00:00 2001 From: Alexander Kukushkin Date: Fri, 19 Nov 2021 14:33:24 +0100 Subject: [PATCH] Optimize checkpoint after promote (#2114) 1. Avoid doing CHECKPOINT if `pg_control` is already updated. 2. Explicitly call ensure_checkpoint_after_promote() right after the bootstrap finished successfully. --- patroni/ha.py | 1 + patroni/postgresql/__init__.py | 1 + patroni/postgresql/rewind.py | 9 +++------ tests/test_rewind.py | 20 +++++++++++--------- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/patroni/ha.py b/patroni/ha.py index 664349a5..d4b91f56 100644 --- a/patroni/ha.py +++ b/patroni/ha.py @@ -1283,6 +1283,7 @@ class Ha(object): if not self.watchdog.activate(): logger.error('Cancelling bootstrap because watchdog activation failed') self.cancel_initialization() + self._rewind.ensure_checkpoint_after_promote(self.wakeup) self.dcs.initialize(create_new=(self.cluster.initialize is None), sysid=self.state_handler.sysid) self.dcs.set_config_value(json.dumps(self.patroni.config.dynamic_configuration, separators=(',', ':'))) self.dcs.take_leader() diff --git a/patroni/postgresql/__init__.py b/patroni/postgresql/__init__.py index 57d41823..0061f52f 100644 --- a/patroni/postgresql/__init__.py +++ b/patroni/postgresql/__init__.py @@ -388,6 +388,7 @@ class Postgresql(object): def pg_control_timeline(self): try: + return int(self.controldata().get("Latest checkpoint's TimeLineID")) except (TypeError, ValueError): logger.exception('Failed to parse timeline from pg_controldata output') diff --git a/patroni/postgresql/rewind.py b/patroni/postgresql/rewind.py index fadc41a7..9fd7cc3b 100644 --- a/patroni/postgresql/rewind.py +++ b/patroni/postgresql/rewind.py @@ -232,14 +232,11 @@ class Rewind(object): with self._checkpoint_task: if self._checkpoint_task.result: self._state = REWIND_STATUS.CHECKPOINT - if self._checkpoint_task.result is not False: - return + elif self._postgresql.get_master_timeline() == self._postgresql.pg_control_timeline(): + self._state = REWIND_STATUS.CHECKPOINT else: self._checkpoint_task = CriticalTask() - return Thread(target=self.__checkpoint, args=(self._checkpoint_task, wakeup)).start() - - if self._postgresql.get_master_timeline() == self._postgresql.pg_control_timeline(): - self._state = REWIND_STATUS.CHECKPOINT + Thread(target=self.__checkpoint, args=(self._checkpoint_task, wakeup)).start() def checkpoint_after_promote(self): return self._state == REWIND_STATUS.CHECKPOINT diff --git a/tests/test_rewind.py b/tests/test_rewind.py index ba890b1b..b303d5eb 100644 --- a/tests/test_rewind.py +++ b/tests/test_rewind.py @@ -219,18 +219,20 @@ class TestRewind(BaseTestPostgresql): @patch('patroni.postgresql.rewind.Thread', MockThread) @patch.object(Postgresql, 'controldata') @patch.object(Postgresql, 'checkpoint') - def test_ensure_checkpoint_after_promote(self, mock_checkpoint, mock_controldata): - mock_checkpoint.return_value = None + @patch.object(Postgresql, 'get_master_timeline') + def test_ensure_checkpoint_after_promote(self, mock_get_master_timeline, mock_checkpoint, mock_controldata): + mock_controldata.return_value = {"Latest checkpoint's TimeLineID": 1} + mock_get_master_timeline.return_value = 1 + self.r.ensure_checkpoint_after_promote(Mock()) + + self.r.reset_state() + mock_get_master_timeline.return_value = 2 + mock_checkpoint.return_value = 0 self.r.ensure_checkpoint_after_promote(Mock()) self.r.ensure_checkpoint_after_promote(Mock()) self.r.reset_state() - mock_controldata.return_value = {"Latest checkpoint's TimeLineID": 1} + + mock_controldata.side_effect = TypeError mock_checkpoint.side_effect = Exception self.r.ensure_checkpoint_after_promote(Mock()) - self.r.ensure_checkpoint_after_promote(Mock()) - - self.r.reset_state() - mock_controldata.side_effect = TypeError - self.r.ensure_checkpoint_after_promote(Mock()) - self.r.ensure_checkpoint_after_promote(Mock())