From c4535ae208569cb36dabf30a6e40957e95ebff42 Mon Sep 17 00:00:00 2001 From: Michael Banck Date: Mon, 14 Feb 2022 15:21:51 +0100 Subject: [PATCH] Avoid running CHECKPOINT on remote master if credentials are missing (#2195) Close #2194 --- patroni/postgresql/rewind.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/patroni/postgresql/rewind.py b/patroni/postgresql/rewind.py index 3d072444..b30d739f 100644 --- a/patroni/postgresql/rewind.py +++ b/patroni/postgresql/rewind.py @@ -65,6 +65,20 @@ class Rewind(object): except Exception: return logger.exception('Exception when working with leader') + @staticmethod + def check_leader_has_run_checkpoint(conn_kwargs): + try: + with get_connection_cursor(connect_timeout=3, options='-c statement_timeout=2000', **conn_kwargs) as cur: + cur.execute("SELECT NOT pg_catalog.pg_is_in_recovery()" + + " AND ('x' || pg_catalog.substr(pg_catalog.pg_walfile_name(" + + " pg_catalog.pg_current_wal_lsn()), 1, 8))::bit(32)::int = timeline_id" + + " FROM pg_catalog.pg_control_checkpoint()") + if not cur.fetchone()[0]: + return 'leader has not run a checkpoint yet' + except Exception: + logger.exception('Exception when working with leader') + return 'not accessible or not healty' + def _get_checkpoint_end(self, timeline, lsn): """The checkpoint record size in WAL depends on postgres major version and platform (memory alignment). Hence, the only reliable way to figure out where it ends, read the record from file with the help of pg_waldump @@ -337,9 +351,14 @@ class Rewind(object): # running a checkpoint or # waiting until Patroni on the master will expose checkpoint_after_promote=True checkpoint_status = leader.checkpoint_after_promote if isinstance(leader, Leader) else None - if checkpoint_status is None: # master still runs the old Patroni - leader_status = self._postgresql.checkpoint(self._conn_kwargs(leader, self._postgresql.config.superuser)) - if leader_status: + if checkpoint_status is None: # we are the standby-cluster leader or master still runs the old Patroni + # superuser credentials match rewind_credentials if the latter are not provided or we run 10 or older + if self._postgresql.config.superuser == self._postgresql.config.rewind_credentials: + leader_status = self._postgresql.checkpoint( + self._conn_kwargs(leader, self._postgresql.config.superuser)) + else: # we run 11+ and have a dedicated pg_rewind user + leader_status = self.check_leader_has_run_checkpoint(r) + if leader_status: # we tried to run/check for a checkpoint on the remote leader, but it failed return logger.warning('Can not use %s for rewind: %s', leader.name, leader_status) elif not checkpoint_status: return logger.info('Waiting for checkpoint on %s before rewind', leader.name)