From c6e61b952acdb0488ccac4b5535e02b5821f33cb Mon Sep 17 00:00:00 2001 From: Ildar Musin Date: Mon, 2 Apr 2018 14:22:02 +0300 Subject: [PATCH 1/6] Add PostgresNode.promote() method which promotes standby node to master --- testgres/node.py | 22 ++++++++++++++++++++++ tests/test_simple.py | 14 ++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/testgres/node.py b/testgres/node.py index d68f3d34..b3e2d56b 100644 --- a/testgres/node.py +++ b/testgres/node.py @@ -670,6 +670,28 @@ def reload(self, params=[]): return self + def promote(self): + """ + Promote standby instance to master using pg_ctl. + + Returns: + This instance of :class:`.PostgresNode`. + """ + + _params = [ + get_bin_path("pg_ctl"), + "-D", self.data_dir, + "-w", # wait + "promote" + ] # yapf: disable + + execute_utility(_params, self.utils_log_file) + + # Node becomes master itself + self._master = None + + return self + def pg_ctl(self, params): """ Invoke pg_ctl with params. diff --git a/tests/test_simple.py b/tests/test_simple.py index f639e92a..cb6b30c3 100755 --- a/tests/test_simple.py +++ b/tests/test_simple.py @@ -407,6 +407,20 @@ def test_incorrect_catchup(self): with self.assertRaises(TestgresException): node.catchup() + def test_promotion(self): + with get_new_node() as master: + master.init().start() + master.safe_psql('create table abc(id serial)') + + with master.replicate().start() as replica: + master.stop() + replica.promote() + + # make standby becomes writable master + replica.safe_psql('insert into abc values (1)') + res = replica.safe_psql('select * from abc') + self.assertEqual(res, b'1\n') + def test_dump(self): query_create = 'create table test as select generate_series(1, 2) as val' query_select = 'select * from test order by val asc' From 06d10056909249fa5ea1df09fe70cd7e8b4d5e56 Mon Sep 17 00:00:00 2001 From: Ildar Musin Date: Mon, 2 Apr 2018 14:57:32 +0300 Subject: [PATCH 2/6] Add synchronous promotion support on pg of version < 10 --- testgres/node.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/testgres/node.py b/testgres/node.py index b3e2d56b..6e3797a4 100644 --- a/testgres/node.py +++ b/testgres/node.py @@ -662,7 +662,6 @@ def reload(self, params=[]): _params = [ get_bin_path("pg_ctl"), "-D", self.data_dir, - "-w", # wait "reload" ] + params # yapf: disable @@ -670,9 +669,12 @@ def reload(self, params=[]): return self - def promote(self): + def promote(self, dbname=None, username=None): """ - Promote standby instance to master using pg_ctl. + Promote standby instance to master using pg_ctl. For PostgreSQL versions + below 10 some additional actions required to ensure that instance + became writable and hence `dbname` and `username` parameters may be + needed. Returns: This instance of :class:`.PostgresNode`. @@ -687,7 +689,19 @@ def promote(self): execute_utility(_params, self.utils_log_file) - # Node becomes master itself + # for versions below 10 `promote` is asynchronous so we need to wait + # until it actually becomes writable + if not pg_version_ge("10"): + check_query = "SHOW transaction_read_only" + + self.poll_query_until( + query=check_query, + expected="on", + dbname=dbname, + username=username, + max_attempts=0) # infinite + + # node becomes master itself self._master = None return self From 5ea4bd8d0f611762a3f545a8ec17b1e2422796ab Mon Sep 17 00:00:00 2001 From: Ildar Musin Date: Mon, 2 Apr 2018 14:22:02 +0300 Subject: [PATCH 3/6] Add PostgresNode.promote() method which promotes standby node to master --- testgres/node.py | 22 ++++++++++++++++++++++ tests/test_simple.py | 14 ++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/testgres/node.py b/testgres/node.py index 9978dcf6..99a32e6f 100644 --- a/testgres/node.py +++ b/testgres/node.py @@ -691,6 +691,28 @@ def reload(self, params=[]): return self + def promote(self): + """ + Promote standby instance to master using pg_ctl. + + Returns: + This instance of :class:`.PostgresNode`. + """ + + _params = [ + get_bin_path("pg_ctl"), + "-D", self.data_dir, + "-w", # wait + "promote" + ] # yapf: disable + + execute_utility(_params, self.utils_log_file) + + # Node becomes master itself + self._master = None + + return self + def pg_ctl(self, params): """ Invoke pg_ctl with params. diff --git a/tests/test_simple.py b/tests/test_simple.py index 230cff47..91348f34 100755 --- a/tests/test_simple.py +++ b/tests/test_simple.py @@ -529,6 +529,20 @@ def test_incorrect_catchup(self): with self.assertRaises(TestgresException): node.catchup() + def test_promotion(self): + with get_new_node() as master: + master.init().start() + master.safe_psql('create table abc(id serial)') + + with master.replicate().start() as replica: + master.stop() + replica.promote() + + # make standby becomes writable master + replica.safe_psql('insert into abc values (1)') + res = replica.safe_psql('select * from abc') + self.assertEqual(res, b'1\n') + def test_dump(self): query_create = 'create table test as select generate_series(1, 2) as val' query_select = 'select * from test order by val asc' From 7c48faebc13e4a8ac5c363f60c1da8ca67b82928 Mon Sep 17 00:00:00 2001 From: Ildar Musin Date: Mon, 2 Apr 2018 14:57:32 +0300 Subject: [PATCH 4/6] Add synchronous promotion support on pg of version < 10 --- testgres/node.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/testgres/node.py b/testgres/node.py index 99a32e6f..ada2f950 100644 --- a/testgres/node.py +++ b/testgres/node.py @@ -683,7 +683,6 @@ def reload(self, params=[]): _params = [ get_bin_path("pg_ctl"), "-D", self.data_dir, - "-w", # wait "reload" ] + params # yapf: disable @@ -691,9 +690,12 @@ def reload(self, params=[]): return self - def promote(self): + def promote(self, dbname=None, username=None): """ - Promote standby instance to master using pg_ctl. + Promote standby instance to master using pg_ctl. For PostgreSQL versions + below 10 some additional actions required to ensure that instance + became writable and hence `dbname` and `username` parameters may be + needed. Returns: This instance of :class:`.PostgresNode`. @@ -708,7 +710,19 @@ def promote(self): execute_utility(_params, self.utils_log_file) - # Node becomes master itself + # for versions below 10 `promote` is asynchronous so we need to wait + # until it actually becomes writable + if not pg_version_ge("10"): + check_query = "SHOW transaction_read_only" + + self.poll_query_until( + query=check_query, + expected="on", + dbname=dbname, + username=username, + max_attempts=0) # infinite + + # node becomes master itself self._master = None return self From 838a80053a42715020b5dbccc5927de78b1156ee Mon Sep 17 00:00:00 2001 From: Ildar Musin Date: Mon, 4 Jun 2018 14:56:45 +0300 Subject: [PATCH 5/6] fix promote() method for Postgres versions below 10 --- testgres/node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testgres/node.py b/testgres/node.py index ada2f950..97ce3a78 100644 --- a/testgres/node.py +++ b/testgres/node.py @@ -717,7 +717,7 @@ def promote(self, dbname=None, username=None): self.poll_query_until( query=check_query, - expected="on", + expected="off", dbname=dbname, username=username, max_attempts=0) # infinite From f5fe963b0966e33d7e4da3fac35a93f14c3872f5 Mon Sep 17 00:00:00 2001 From: Ildar Musin Date: Mon, 4 Jun 2018 19:11:21 +0300 Subject: [PATCH 6/6] Use pg_is_in_recovery() instead of relying on transaction_read_only GUC variable in promote() func --- testgres/node.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testgres/node.py b/testgres/node.py index 64419483..b597dd69 100644 --- a/testgres/node.py +++ b/testgres/node.py @@ -713,11 +713,11 @@ def promote(self, dbname=None, username=None): # for versions below 10 `promote` is asynchronous so we need to wait # until it actually becomes writable if self._pg_version < '10': - check_query = "SHOW transaction_read_only" + check_query = "SELECT pg_is_in_recovery()" self.poll_query_until( query=check_query, - expected="off", + expected=False, dbname=dbname, username=username, max_attempts=0) # infinite