Avoid running CHECKPOINT on remote master if credentials are missing
Close
@@ -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)
