mirror of
https://github.com/outbackdingo/patroni.git
synced 2026-08-27 16:10:10 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
720d08d1b4 | ||
|
|
d39bd4363d | ||
|
|
48e1b976b3 | ||
|
|
fb17eeaf94 | ||
|
|
0443844f65 | ||
|
|
3ece35c0a6 | ||
|
|
1ed91a93c6 |
+1
-1
@@ -5,7 +5,7 @@ python:
|
||||
- "3.4" # 2.7 and 3.5 are preinstalled by default
|
||||
env:
|
||||
global:
|
||||
- ETCDVERSION=3.0.15 ZKVERSION=3.4.9 CONSULVERSION=0.7.2
|
||||
- ETCDVERSION=3.0.17 ZKVERSION=3.4.9 CONSULVERSION=0.7.4
|
||||
- PYVERSIONS="2.7 3.4 3.5"
|
||||
matrix:
|
||||
- TEST_SUITE="python setup.py"
|
||||
|
||||
+3
-3
@@ -3,7 +3,7 @@
|
||||
Patroni: A Template for PostgreSQL HA with ZooKeeper, etcd or Consul
|
||||
------------------------------------------------------------
|
||||
|
||||
You can find a version of this documentation that is searchable and also easier to navigagate at `patroni.readthedocs.io <https://patroni.readthedocs.io>`__.
|
||||
You can find a version of this documentation that is searchable and also easier to navigate at `patroni.readthedocs.io <https://patroni.readthedocs.io>`__.
|
||||
|
||||
|
||||
There are many ways to run high availability with PostgreSQL; for a list, see the `PostgreSQL Documentation <https://wiki.postgresql.org/wiki/Replication,_Clustering,_and_Connection_Pooling>`__.
|
||||
@@ -12,7 +12,7 @@ Patroni is a template for you to create your own customized, high-availability s
|
||||
|
||||
We call Patroni a "template" because it is far from being a one-size-fits-all or plug-and-play replication system. It will have its own caveats. Use wisely.
|
||||
|
||||
**Note to Kubernetes users**: We're currently developing Patroni to be as useful as possible for teams running Kubernetes on top of Google Compute Engine; Patroni can be the HA solution for Postgres in such an environment. To this end, we've created a `Helm Chart <https://github.com/kubernetes/charts/tree/master/incubator/patroni>`__ that enables you to deploy a five-node Patroni cluster using a Kubernetes PetSet.
|
||||
**Note to Kubernetes users**: We're currently developing Patroni to be as useful as possible for teams running Kubernetes on top of Google Compute Engine; Patroni can be the HA solution for Postgres in such an environment. To this end, there is a `Helm chart <https://github.com/kubernetes/charts/tree/master/incubator/patroni>`__ that uses Patroni and `Spilo <https://github.com/zalando/spilo/>`__ to provision a five-node PostgreSQL HA cluster in a Kubernetes+GCE environment. (The Helm chart deploys Spilo Docker images, not just "bare" Patroni.)
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
@@ -95,7 +95,7 @@ Go `here <https://github.com/zalando/patroni/blob/master/docs/ENVIRONMENT.rst>`_
|
||||
Replication Choices
|
||||
===============
|
||||
|
||||
Patroni uses Postgres' streaming replication, which is asynchronous by default. Patroni's asynchronous replication configuration allows for ``maximum_lag_on_failover`` settings. This setting ensures failover will not occur if a follower is more than a certain number of bytes behind the leader. This setting should be increased or decreased based on business requirements. It's also possible to use synchronous replication for better durability guarantees. See `replication modes documentation <https://github.com/zalando/patroni/blob/master/docs/replication_modes.rst>` for details.
|
||||
Patroni uses Postgres' streaming replication, which is asynchronous by default. Patroni's asynchronous replication configuration allows for ``maximum_lag_on_failover`` settings. This setting ensures failover will not occur if a follower is more than a certain number of bytes behind the leader. This setting should be increased or decreased based on business requirements. It's also possible to use synchronous replication for better durability guarantees. See `replication modes documentation <https://github.com/zalando/patroni/blob/master/docs/replication_modes.rst>`__ for details.
|
||||
|
||||
===============================
|
||||
Applications Should Not Use Superusers
|
||||
|
||||
@@ -530,6 +530,8 @@ class Etcd(AbstractDCS):
|
||||
except etcd.EtcdWatchTimedOut:
|
||||
self._client.http.clear()
|
||||
return False
|
||||
except (etcd.EtcdEventIndexCleared, etcd.EtcdWatcherCleared): # Watch failed
|
||||
return True # leave the loop, because watch with the same parameters will fail anyway
|
||||
except etcd.EtcdException:
|
||||
logger.exception('watch')
|
||||
|
||||
|
||||
@@ -177,6 +177,8 @@ class Postgresql(object):
|
||||
parameters.pop('synchronous_standby_names', None)
|
||||
else:
|
||||
parameters['synchronous_standby_names'] = self._synchronous_standby_names
|
||||
if self._major_version >= 9.6 and parameters['wal_level'] == 'hot_standby':
|
||||
parameters['wal_level'] = 'replica'
|
||||
return {k: v for k, v in parameters.items() if not self._major_version or
|
||||
self._major_version >= self.CMDLINE_OPTIONS.get(k, (0, 1, 9.1))[2]}
|
||||
|
||||
@@ -249,8 +251,6 @@ class Postgresql(object):
|
||||
elif r[0] in changes:
|
||||
unit = changes['wal_segment_size'] if r[0] in ('min_wal_size', 'max_wal_size') else r[2]
|
||||
new_value = changes.pop(r[0])
|
||||
if self._major_version >= 9.6 and r[0] == 'wal_level' and new_value == 'hot_standby':
|
||||
new_value = 'replica'
|
||||
if new_value is None or not compare_values(r[3], unit, r[1], new_value):
|
||||
if r[4] == 'postmaster':
|
||||
pending_restart = True
|
||||
@@ -435,6 +435,7 @@ class Postgresql(object):
|
||||
if ret:
|
||||
self.write_pg_hba(config.get('pg_hba', []))
|
||||
self._major_version = self.get_major_version()
|
||||
self._server_parameters = self.get_server_parameters(self.config)
|
||||
else:
|
||||
self.set_state('initdb failed')
|
||||
return ret
|
||||
@@ -697,9 +698,7 @@ class Postgresql(object):
|
||||
self._write_postgresql_conf()
|
||||
self.resolve_connection_addresses()
|
||||
|
||||
opts = {p: self._server_parameters[p] for p, v in self.CMDLINE_OPTIONS.items() if self._major_version >= v[2]}
|
||||
if self._major_version >= 9.6 and opts['wal_level'] == 'hot_standby':
|
||||
opts['wal_level'] = 'replica'
|
||||
opts = {p: self._server_parameters[p] for p in self.CMDLINE_OPTIONS if p in self._server_parameters}
|
||||
options = ['--{0}={1}'.format(p, v) for p, v in opts.items()]
|
||||
|
||||
start_initiated = time.time()
|
||||
@@ -1209,6 +1208,7 @@ $$""".format(name, ' '.join(options)), name, password, password)
|
||||
ret = self.create_replica(clone_member) == 0
|
||||
if ret:
|
||||
self._major_version = self.get_major_version()
|
||||
self._server_parameters = self.get_server_parameters(self.config)
|
||||
self.delete_trigger_file()
|
||||
self.restore_configuration_files()
|
||||
return ret
|
||||
|
||||
@@ -133,8 +133,16 @@ class WALERestore(object):
|
||||
with psycopg2.connect(self.master_connection) as con:
|
||||
con.autocommit = True
|
||||
with con.cursor() as cur:
|
||||
cur.execute("SELECT pg_xlog_location_diff(pg_current_xlog_location(), %s)",
|
||||
(backup_start_lsn,))
|
||||
cur.execute("""SELECT CASE WHEN pg_is_in_recovery()
|
||||
THEN GREATEST(
|
||||
pg_xlog_location_diff(COALESCE(
|
||||
pg_last_xlog_receive_location(), '0/0'), %s)::bigint,
|
||||
pg_xlog_location_diff(
|
||||
pg_last_xlog_replay_location(), %s)::bigint)
|
||||
ELSE pg_xlog_location_diff(
|
||||
pg_current_xlog_location(), %s)::bigint
|
||||
END""", (backup_start_lsn, backup_start_lsn, backup_start_lsn))
|
||||
|
||||
diff_in_bytes = int(cur.fetchone()[0])
|
||||
except psycopg2.Error:
|
||||
logger.exception('could not determine difference with the master location')
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = '1.2.3'
|
||||
__version__ = '1.2.4'
|
||||
|
||||
@@ -61,6 +61,8 @@ def etcd_watch(self, key, index=None, timeout=None, recursive=None):
|
||||
return etcd.EtcdResult('delete', {})
|
||||
elif timeout == 10.0:
|
||||
raise etcd.EtcdException
|
||||
elif timeout == 20.0:
|
||||
raise etcd.EtcdEventIndexCleared
|
||||
|
||||
|
||||
def etcd_write(self, key, value, **kwargs):
|
||||
@@ -307,6 +309,7 @@ class TestEtcd(unittest.TestCase):
|
||||
self.etcd.watch(20729, 1.5)
|
||||
self.etcd.watch(20729, 4.5)
|
||||
with patch.object(AbstractDCS, 'watch', Mock()):
|
||||
self.assertTrue(self.etcd.watch(20729, 19.5))
|
||||
self.etcd.watch(20729, 9.5)
|
||||
|
||||
def test_other_exceptions(self):
|
||||
|
||||
@@ -41,8 +41,7 @@ class MockCursor(object):
|
||||
('search_path', 'public', None, 'string', 'user'),
|
||||
('port', '5433', None, 'integer', 'postmaster'),
|
||||
('listen_addresses', '*', None, 'string', 'postmaster'),
|
||||
('autovacuum', 'on', None, 'bool', 'sighup'),
|
||||
('wal_level', 'replica', None, 'enum', 'postmaster')]
|
||||
('autovacuum', 'on', None, 'bool', 'sighup')]
|
||||
else:
|
||||
self.results = [(None, None, None, None, None, None, None, None, None, None)]
|
||||
|
||||
@@ -766,7 +765,7 @@ class TestPostgresql(unittest.TestCase):
|
||||
self.assertEquals(value_in_conf(), None)
|
||||
|
||||
def test_get_server_parameters(self):
|
||||
config = {'synchronous_mode': True, 'parameters': {}, 'listen': '0'}
|
||||
config = {'synchronous_mode': True, 'parameters': {'wal_level': 'hot_standby'}, 'listen': '0'}
|
||||
self.p.get_server_parameters(config)
|
||||
self.p.set_synchronous_standby('foo')
|
||||
self.p.get_server_parameters(config)
|
||||
|
||||
Reference in New Issue
Block a user