* debian/patches/behave_failure_logging.patch: Removed, applied upstream.

* debian/patches/acceptance_tests_coverage_binary.patch: Likewise.
  * debian/patches/automatic_primary_reinit.patch: Likewise.
  * debian/patches/automatic_primary_reinit.patch: Likewise.
  * debian/patches/patronictl_timeline_info.patch: Likewise.
  * debian/patches/acceptance_tests_postgres_port.patch: Likewise.
  * debian/patches/fix_replication_connection.patch: Likewise.
  * debian/patches/consul_import.patch: Refreshed.
This commit is contained in:
Michael Banck 2019-02-15 20:29:59 +01:00
parent caaeb33188
commit 7d1e81e853
9 changed files with 14 additions and 471 deletions

15
debian/changelog vendored
View file

@ -1,7 +1,18 @@
patroni (1.5.4-9) UNRELEASED; urgency=medium
patroni (1.5.5-1) UNRELEASED; urgency=medium
*
[ Debian PostgreSQL Maintainers ]
* New upstream release.
[ Michael Banck ]
* debian/patches/behave_failure_logging.patch: Removed, applied upstream.
* debian/patches/acceptance_tests_coverage_binary.patch: Likewise.
* debian/patches/automatic_primary_reinit.patch: Likewise.
* debian/patches/automatic_primary_reinit.patch: Likewise.
* debian/patches/patronictl_timeline_info.patch: Likewise.
* debian/patches/acceptance_tests_postgres_port.patch: Likewise.
* debian/patches/fix_replication_connection.patch: Likewise.
* debian/patches/consul_import.patch: Refreshed.
-- Debian PostgreSQL Maintainers <team+postgresql@tracker.debian.org> Fri, 08 Feb 2019 17:45:11 +0100
patroni (1.5.4-8) unstable; urgency=medium

View file

@ -1,54 +0,0 @@
Index: patroni/features/environment.py
===================================================================
--- patroni.orig/features/environment.py
+++ patroni/features/environment.py
@@ -11,6 +11,7 @@ import shutil
import signal
import six
import subprocess
+import sys
import tempfile
import threading
import time
@@ -141,7 +142,8 @@ class PatroniController(AbstractControll
if isinstance(self._context.dcs_ctl, KubernetesController):
self._context.dcs_ctl.create_pod(self._name[8:], self._scope)
os.environ['PATRONI_KUBERNETES_POD_IP'] = '10.0.0.' + self._name[-1]
- return subprocess.Popen(['coverage', 'run', '--source=patroni', '-p', 'patroni.py', self._config],
+ return subprocess.Popen([sys.executable, '-m', 'coverage', 'run',
+ '--source=patroni', '-p', 'patroni.py', self._config],
stdout=self._log, stderr=subprocess.STDOUT, cwd=self._work_directory)
def stop(self, kill=False, timeout=15, postgres=False):
@@ -811,8 +813,8 @@ def before_all(context):
def after_all(context):
context.dcs_ctl.stop()
- subprocess.call(['coverage', 'combine'])
- subprocess.call(['coverage', 'report'])
+ subprocess.call([sys.executable, '-m', 'coverage', 'combine'])
+ subprocess.call([sys.executable, '-m', 'coverage', 'report'])
def before_feature(context, feature):
Index: patroni/features/steps/patroni_api.py
===================================================================
--- patroni.orig/features/steps/patroni_api.py
+++ patroni/features/steps/patroni_api.py
@@ -5,6 +5,7 @@ import parse
import requests
import shlex
import subprocess
+import sys
import time
import yaml
@@ -95,7 +96,7 @@ def do_request(context, request_method,
@step('I run {cmd}')
def do_run(context, cmd):
- cmd = ['coverage', 'run', '--source=patroni', '-p'] + shlex.split(cmd)
+ cmd = [sys.executable, '-m', 'coverage', 'run', '--source=patroni', '-p'] + shlex.split(cmd)
try:
# XXX: Dirty hack! We need to take name/passwd from the config!
env = os.environ.copy()

View file

@ -1,13 +0,0 @@
Index: patroni/features/environment.py
===================================================================
--- patroni.orig/features/environment.py
+++ patroni/features/environment.py
@@ -84,7 +84,7 @@ class AbstractController(object):
class PatroniController(AbstractController):
- __PORT = 5440
+ __PORT = 5340
PATRONI_CONFIG = '{}.yml'
""" starts and stops individual patronis"""

View file

@ -1,286 +0,0 @@
From 3a509cab07da84e980b3e3f78db5f09f3798031b Mon Sep 17 00:00:00 2001
From: Alexander Kukushkin <alexander.kukushkin@zalando.de>
Date: Fri, 25 Jan 2019 10:41:22 +0100
Subject: [PATCH] Make it possible to automatically reinit the former master
If the pg_rewind is disabled or can't be used, the former master could
fail to start as a new replica due to diverged timelines. In this case
the only way to fix it is wiping the data directory and reinitilizing.
So far Patroni was able to remove the data directory only after failed
attempt to run pg_rewind. This commit fixes it.
If the `postgresql.remove_data_directory_on_diverged_timelines` is set,
Patroni will wipe the data directory and reinitialize the former master
automatically.
Fixes: https://github.com/zalando/patroni/issues/941
---
patroni/ha.py | 30 +++++++++++++++++++-----------
patroni/postgresql.py | 27 +++++++++++++++++++--------
tests/test_ha.py | 18 ++++++++++++++----
tests/test_postgresql.py | 18 ++++++++++--------
4 files changed, 62 insertions(+), 31 deletions(-)
diff --git a/patroni/ha.py b/patroni/ha.py
index b7ac3494..3989c71b 100644
--- a/patroni/ha.py
+++ b/patroni/ha.py
@@ -265,13 +265,22 @@ def bootstrap_standby_leader(self):
return result
- def _handle_rewind(self):
+ def _handle_rewind_or_reinitialize(self):
leader = self.get_remote_master() if self.is_standby_cluster() else self.cluster.leader
- if self.state_handler.rewind_needed_and_possible(leader):
+ if not self.state_handler.rewind_or_reinitialize_needed_and_possible(leader):
+ return None
+
+ if self.state_handler.can_rewind:
self._async_executor.schedule('running pg_rewind from ' + leader.name)
self._async_executor.run_async(self.state_handler.rewind, (leader,))
return True
+ # remove_data_directory_on_diverged_timelines is set
+ if not self.is_standby_cluster():
+ self._async_executor.schedule('reinitializing due to diverged timelines')
+ self._async_executor.run_async(self._do_reinitialize, args=(self.cluster, ))
+ return True
+
def recover(self):
# Postgres is not running and we will restart in standby mode. Watchdog is not needed until we promote.
self.watchdog.disable()
@@ -304,7 +313,7 @@ def recover(self):
if self.is_standby_cluster() or not self.has_lock():
if not self.state_handler.rewind_executed:
self.state_handler.trigger_check_diverged_lsn()
- if self._handle_rewind():
+ if self._handle_rewind_or_reinitialize():
return self._async_executor.scheduled_action
if self.has_lock(): # in standby cluster
@@ -338,9 +347,7 @@ def _get_node_to_follow(self, cluster):
else:
node_to_follow = cluster.leader
- return (node_to_follow if
- node_to_follow and
- node_to_follow.name != self.state_handler.name else None)
+ return node_to_follow if node_to_follow and node_to_follow.name != self.state_handler.name else None
def follow(self, demote_reason, follow_reason, refresh=True):
if refresh:
@@ -351,7 +358,8 @@ def follow(self, demote_reason, follow_reason, refresh=True):
node_to_follow = self._get_node_to_follow(self.cluster)
if self.is_paused():
- if not (self.state_handler.need_rewind and self.state_handler.can_rewind) or self.cluster.is_unlocked():
+ if not (self.state_handler.need_rewind and self.state_handler.can_rewind_or_reinitialize_allowed)\
+ or self.cluster.is_unlocked():
self.state_handler.set_role('master' if is_leader else 'replica')
if is_leader:
return 'continue to run as master without lock'
@@ -361,7 +369,7 @@ def follow(self, demote_reason, follow_reason, refresh=True):
self.demote('immediate-nolock')
return demote_reason
- if self._handle_rewind():
+ if self._handle_rewind_or_reinitialize():
return self._async_executor.scheduled_action
if not self.state_handler.check_recovery_conf(node_to_follow):
@@ -733,7 +741,7 @@ def demote(self, mode):
else:
if self.is_synchronous_mode():
self.state_handler.set_synchronous_standby(None)
- if self.state_handler.rewind_needed_and_possible(leader):
+ if self.state_handler.rewind_or_reinitialize_needed_and_possible(leader):
return False # do not start postgres, but run pg_rewind on the next iteration
self.state_handler.follow(node_to_follow)
@@ -1253,8 +1261,8 @@ def _run_cycle(self):
# the demote code follows through to starting Postgres right away, however, in the rewind case
# it returns from demote and reaches this point to start PostgreSQL again after rewind. In that
# case it makes no sense to continue to recover() unless rewind has finished successfully.
- elif (self.state_handler.rewind_failed or
- not (self.state_handler.need_rewind and self.state_handler.can_rewind)):
+ elif self.state_handler.rewind_failed or not self.state_handler.need_rewind \
+ or not self.state_handler.can_rewind_or_reinitialize_allowed:
return 'postgres is not running'
# try to start dead postgres
diff --git a/patroni/postgresql.py b/patroni/postgresql.py
index 546b9982..bd057f91 100644
--- a/patroni/postgresql.py
+++ b/patroni/postgresql.py
@@ -16,7 +16,7 @@
from patroni.exceptions import PostgresConnectionException, PostgresException
from patroni.utils import compare_values, parse_bool, parse_int, Retry, RetryFailedError, polling_loop, split_host_port
from patroni.postmaster import PostmasterProcess
-from patroni.dcs import slot_name_from_member_name, RemoteMember
+from patroni.dcs import slot_name_from_member_name, RemoteMember, Leader
from requests.structures import CaseInsensitiveDict
from six import string_types
from six.moves.urllib.parse import quote_plus
@@ -404,6 +404,10 @@ def can_rewind(self):
return False
return self.configuration_allows_rewind(self.controldata())
+ @property
+ def can_rewind_or_reinitialize_allowed(self):
+ return self.config.get('remove_data_directory_on_diverged_timelines') or self.can_rewind
+
@property
def sysid(self):
if not self._sysid and not self.bootstrapping:
@@ -1323,7 +1327,11 @@ def _check_timeline_and_lsn(self, leader):
if local_timeline is None or local_lsn is None:
return
- if not self.check_leader_is_not_in_recovery(**leader.conn_kwargs(self._superuser)):
+ if isinstance(leader, Leader):
+ if leader.member.data.get('role') != 'master':
+ return
+ # standby cluster
+ elif not self.check_leader_is_not_in_recovery(**leader.conn_kwargs(self._superuser)):
return
history = need_rewind = None
@@ -1403,19 +1411,21 @@ def rewind(self, leader):
else:
logger.error('Failed to rewind from healty master: %s', leader.name)
- if self.config.get('remove_data_directory_on_rewind_failure', False):
- logger.warning('remove_data_directory_on_rewind_failure is set. removing...')
- self.remove_data_directory()
- self._rewind_state = REWIND_STATUS.INITIAL
+ for name in ('remove_data_directory_on_rewind_failure', 'remove_data_directory_on_diverged_timelines'):
+ if self.config.get(name):
+ logger.warning('%s is set. removing...', name)
+ self.remove_data_directory()
+ self._rewind_state = REWIND_STATUS.INITIAL
+ break
else:
self._rewind_state = REWIND_STATUS.FAILED
return False
def trigger_check_diverged_lsn(self):
- if self.can_rewind and self._rewind_state != REWIND_STATUS.NEED:
+ if self.can_rewind_or_reinitialize_allowed and self._rewind_state != REWIND_STATUS.NEED:
self._rewind_state = REWIND_STATUS.CHECK
- def rewind_needed_and_possible(self, leader):
+ def rewind_or_reinitialize_needed_and_possible(self, leader):
if leader and leader.name != self.name and leader.conn_url and self._rewind_state == REWIND_STATUS.CHECK:
self._check_timeline_and_lsn(leader)
return leader and leader.conn_url and self._rewind_state == REWIND_STATUS.NEED
@@ -1652,6 +1662,7 @@ def clone(self, clone_member):
base backup)
"""
+ self._rewind_state = REWIND_STATUS.INITIAL
ret = self.create_replica(clone_member) == 0
if ret:
self._post_restore()
diff --git a/tests/test_ha.py b/tests/test_ha.py
index faf49034..758c6b17 100644
--- a/tests/test_ha.py
+++ b/tests/test_ha.py
@@ -241,12 +241,20 @@ def test_crash_recovery(self):
self.p.controldata = lambda: {'Database cluster state': 'in production', 'Database system identifier': SYSID}
self.assertEqual(self.ha.run_cycle(), 'doing crash recovery in a single user mode')
- @patch.object(Postgresql, 'rewind_needed_and_possible', Mock(return_value=True))
+ @patch.object(Postgresql, 'rewind_or_reinitialize_needed_and_possible', Mock(return_value=True))
+ @patch.object(Postgresql, 'can_rewind', PropertyMock(return_value=True))
def test_recover_with_rewind(self):
self.p.is_running = false
self.ha.cluster = get_cluster_initialized_with_leader()
self.assertEqual(self.ha.run_cycle(), 'running pg_rewind from leader')
+ @patch.object(Postgresql, 'rewind_or_reinitialize_needed_and_possible', Mock(return_value=True))
+ @patch.object(Postgresql, 'create_replica', Mock(return_value=1))
+ def test_recover_with_reinitialize(self):
+ self.p.is_running = false
+ self.ha.cluster = get_cluster_initialized_with_leader()
+ self.assertEqual(self.ha.run_cycle(), 'reinitializing due to diverged timelines')
+
@patch('sys.exit', return_value=1)
@patch('patroni.ha.Ha.sysid_valid', MagicMock(return_value=True))
def test_sysid_no_match(self, exit_mock):
@@ -345,7 +353,8 @@ def test_follow_in_pause(self):
self.p.is_leader = false
self.assertEqual(self.ha.run_cycle(), 'PAUSE: no action')
- @patch.object(Postgresql, 'rewind_needed_and_possible', Mock(return_value=True))
+ @patch.object(Postgresql, 'rewind_or_reinitialize_needed_and_possible', Mock(return_value=True))
+ @patch.object(Postgresql, 'can_rewind', PropertyMock(return_value=True))
def test_follow_triggers_rewind(self):
self.p.is_leader = false
self.p.trigger_check_diverged_lsn()
@@ -459,7 +468,7 @@ def test_manual_failover_from_leader(self):
f = Failover(0, self.p.name, '', None)
self.ha.cluster = get_cluster_initialized_with_leader(f)
self.assertEqual(self.ha.run_cycle(), 'manual failover: demoting myself')
- self.p.rewind_needed_and_possible = true
+ self.p.rewind_or_reinitialize_needed_and_possible = true
self.assertEqual(self.ha.run_cycle(), 'manual failover: demoting myself')
self.ha.fetch_node_status = get_node_status(nofailover=True)
self.assertEqual(self.ha.run_cycle(), 'no action. i am the leader with the lock')
@@ -681,7 +690,8 @@ def test_process_unhealthy_standby_cluster_as_standby_leader(self):
msg = 'promoted self to a standby leader because i had the session lock'
self.assertEqual(self.ha.run_cycle(), msg)
- @patch.object(Postgresql, 'rewind_needed_and_possible', Mock(return_value=True))
+ @patch.object(Postgresql, 'rewind_or_reinitialize_needed_and_possible', Mock(return_value=True))
+ @patch.object(Postgresql, 'can_rewind', PropertyMock(return_value=True))
def test_process_unhealthy_standby_cluster_as_cascade_replica(self):
self.p.is_leader = false
self.p.name = 'replica'
diff --git a/tests/test_postgresql.py b/tests/test_postgresql.py
index 9b7bf754..339f962e 100644
--- a/tests/test_postgresql.py
+++ b/tests/test_postgresql.py
@@ -345,10 +345,10 @@ def test__get_local_timeline_lsn(self):
Mock(return_value={'Database cluster state': 'shut down in recovery',
'Minimum recovery ending location': '0/0',
"Min recovery ending loc's timeline": '0'})):
- self.p.rewind_needed_and_possible(self.leader)
+ self.p.rewind_or_reinitialize_needed_and_possible(self.leader)
with patch.object(Postgresql, 'is_running', Mock(return_value=True)):
with patch.object(MockCursor, 'fetchone', Mock(side_effect=[(False, ), Exception])):
- self.p.rewind_needed_and_possible(self.leader)
+ self.p.rewind_or_reinitialize_needed_and_possible(self.leader)
@patch.object(Postgresql, 'start', Mock())
@patch.object(Postgresql, 'can_rewind', PropertyMock(return_value=True))
@@ -357,21 +357,23 @@ def test__get_local_timeline_lsn(self):
def test__check_timeline_and_lsn(self, mock_check_leader_is_not_in_recovery):
mock_check_leader_is_not_in_recovery.return_value = False
self.p.trigger_check_diverged_lsn()
- self.assertFalse(self.p.rewind_needed_and_possible(self.leader))
+ self.assertFalse(self.p.rewind_or_reinitialize_needed_and_possible(self.leader))
+ self.leader = self.leader.member
+ self.assertFalse(self.p.rewind_or_reinitialize_needed_and_possible(self.leader))
mock_check_leader_is_not_in_recovery.return_value = True
- self.assertFalse(self.p.rewind_needed_and_possible(self.leader))
+ self.assertFalse(self.p.rewind_or_reinitialize_needed_and_possible(self.leader))
self.p.trigger_check_diverged_lsn()
with patch('psycopg2.connect', Mock(side_effect=Exception)):
- self.assertFalse(self.p.rewind_needed_and_possible(self.leader))
+ self.assertFalse(self.p.rewind_or_reinitialize_needed_and_possible(self.leader))
self.p.trigger_check_diverged_lsn()
with patch.object(MockCursor, 'fetchone', Mock(side_effect=[('', 2, '0/0'), ('', b'3\t0/40159C0\tn\n')])):
- self.assertFalse(self.p.rewind_needed_and_possible(self.leader))
+ self.assertFalse(self.p.rewind_or_reinitialize_needed_and_possible(self.leader))
self.p.trigger_check_diverged_lsn()
with patch.object(MockCursor, 'fetchone', Mock(return_value=('', 1, '0/0'))):
with patch.object(Postgresql, '_get_local_timeline_lsn', Mock(return_value=(1, '0/0'))):
- self.assertFalse(self.p.rewind_needed_and_possible(self.leader))
+ self.assertFalse(self.p.rewind_or_reinitialize_needed_and_possible(self.leader))
self.p.trigger_check_diverged_lsn()
- self.assertTrue(self.p.rewind_needed_and_possible(self.leader))
+ self.assertTrue(self.p.rewind_or_reinitialize_needed_and_possible(self.leader))
@patch.object(MockCursor, 'fetchone', Mock(side_effect=[(True,), Exception]))
def test_check_leader_is_not_in_recovery(self):

View file

@ -1,19 +0,0 @@
Index: patroni/features/environment.py
===================================================================
--- patroni.orig/features/environment.py
+++ patroni/features/environment.py
@@ -611,6 +611,8 @@ class PatroniPoolController(object):
feature_dir = os.path.join(self.patroni_path, 'features/output', feature_name.replace(' ', '_'))
if os.path.exists(feature_dir):
shutil.rmtree(feature_dir)
+ if os.path.exists(feature_dir + '_failed'):
+ shutil.rmtree(feature_dir + '_failed')
os.makedirs(feature_dir)
self._output_dir = feature_dir
@@ -823,3 +823,5 @@ def after_feature(context, feature):
context.pctl.stop_all()
shutil.rmtree(os.path.join(context.pctl.patroni_path, 'data'))
context.dcs_ctl.cleanup_service_tree()
+ if feature.status == 'failed':
+ shutil.copytree(context.pctl.output_dir, context.pctl.output_dir + '_failed')

View file

@ -12,7 +12,7 @@ Index: patroni/features/environment.py
import datetime
import etcd
import kazoo.client
@@ -370,6 +369,7 @@ class AbstractDcsController(AbstractCont
@@ -372,6 +371,7 @@ class AbstractDcsController(AbstractCont
class ConsulController(AbstractDcsController):
def __init__(self, context):

View file

@ -1,27 +0,0 @@
From e285e1592750bc9f5f9a73a5a7dc0f1274f545a9 Mon Sep 17 00:00:00 2001
From: Alexander Kukushkin <alexander.kukushkin@zalando.de>
Date: Mon, 28 Jan 2019 12:25:47 +0100
Subject: [PATCH] Always use replication=1, otherwise it is considered a
logical walsender
Fixes: https://github.com/zalando/patroni/issues/894
Fixes: https://github.com/zalando/patroni/issues/951
---
patroni/postgresql.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
Index: patroni/patroni/postgresql.py
===================================================================
--- patroni.orig/patroni/postgresql.py
+++ patroni/patroni/postgresql.py
@@ -1254,9 +1254,7 @@ class Postgresql(object):
@contextmanager
def _get_replication_connection_cursor(self, host='localhost', port=5432, database=None, **kwargs):
- database = database or self._database
- replication = 'database' if self._major_version >= 90400 else 1
- with self._get_connection_cursor(host=host, port=int(port), database=database, replication=replication,
+ with self._get_connection_cursor(host=host, port=int(port), database=database or self._database, replication=1,
user=self._replication['username'], password=self._replication['password'],
connect_timeout=3, options='-c statement_timeout=2000') as cur:
yield cur

View file

@ -1,63 +0,0 @@
From fcc28c1b6e3a4bd0ab67847e9cb9c75ea27faf7b Mon Sep 17 00:00:00 2001
From: Alexander Kukushkin <alexander.kukushkin@zalando.de>
Date: Fri, 25 Jan 2019 13:42:06 +0100
Subject: [PATCH] Show information about timelines in patronictl list
This information will help to detect stale replicas
In addition to that Host will include ':{port}' if the port value is not
default or more than one member running on the same host.
Fixes: https://github.com/zalando/patroni/issues/942
---
patroni/ctl.py | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/patroni/ctl.py b/patroni/ctl.py
index 32ee99b9..7a2fc2d8 100644
--- a/patroni/ctl.py
+++ b/patroni/ctl.py
@@ -180,7 +180,7 @@ def print_output(columns, rows=None, alignment=None, fmt='pretty', header=True,
if fmt == 'tsv':
if columns is not None and header:
- click.echo(delimiter.join(columns) + '\n')
+ click.echo(delimiter.join(columns))
for r in rows:
c = [str(c) for c in r]
@@ -716,6 +716,10 @@ def output_members(cluster, name, extended=False, fmt='pretty'):
has_scheduled_restarts = any(m.data.get('scheduled_restart') for m in cluster.members)
has_pending_restarts = any(m.data.get('pending_restart') for m in cluster.members)
+ # Show Host as 'host:port' if somebody is running on non-standard port or two nodes are running on the same host
+ append_port = any(str(m.conn_kwargs()['port']) != '5432' for m in cluster.members) or\
+ len(set(m.conn_kwargs()['host'] for m in cluster.members)) < len(cluster.members)
+
for m in cluster.members:
logging.debug(m)
@@ -732,7 +736,11 @@ def output_members(cluster, name, extended=False, fmt='pretty'):
elif xlog_location_cluster >= xlog_location:
lag = round((xlog_location_cluster - xlog_location)/1024/1024)
- row = [name, m.name, m.conn_kwargs()['host'], role, m.data.get('state', ''), lag]
+ host = m.conn_kwargs()['host']
+ if append_port:
+ host += ':{0}'.format(m.conn_kwargs()['port'])
+
+ row = [name, m.name, host, role, m.data.get('state', ''), m.data.get('timeline', ''), lag]
if extended or has_pending_restarts:
row.append('*' if m.data.get('pending_restart') else '')
@@ -749,8 +757,8 @@ def output_members(cluster, name, extended=False, fmt='pretty'):
rows.append(row)
- columns = ['Cluster', 'Member', 'Host', 'Role', 'State', 'Lag in MB']
- alignment = {'Lag in MB': 'r'}
+ columns = ['Cluster', 'Member', 'Host', 'Role', 'State', 'TL', 'Lag in MB']
+ alignment = {'Lag in MB': 'r', 'TL': 'r'}
if extended or has_pending_restarts:
columns.append('Pending restart')

View file

@ -1,11 +1,5 @@
startup_scripts.patch
consul_import.patch
behave_failure_logging.patch
check_postmaster.patch
acceptance_tests_coverage_binary.patch
acceptance_tests_system_patroni.patch
automatic_primary_reinit.patch
patronictl_timeline_info.patch
acceptance_tests_postgres_port.patch
fix_replication_connection.patch
reclone-unable-pg_rewind.patch