From afe1a2c92cf5a75607fe12d87ad80e56029fc139 Mon Sep 17 00:00:00 2001 From: Alexander Kukushkin Date: Fri, 18 Sep 2015 12:41:36 +0200 Subject: [PATCH 1/4] During shutdown sigterm is send to all processes every second First time is triggers some shutdown mechanisms in a patroni, but the second one just terminates patroni. Solution is pretty simple, we need to ignore all other sigterms which are coming after the first one. --- patroni/__init__.py | 1 + patroni/utils.py | 1 + tests/test_patroni.py | 3 +++ 3 files changed, 5 insertions(+) diff --git a/patroni/__init__.py b/patroni/__init__.py index c190e2de..5a8588ce 100644 --- a/patroni/__init__.py +++ b/patroni/__init__.py @@ -126,6 +126,7 @@ def main(): except KeyboardInterrupt: pass finally: + patroni.api.shutdown() patroni.touch_member(patroni.shutdown_member_ttl) # schedule member removal patroni.postgresql.stop() patroni.ha.dcs.delete_leader() diff --git a/patroni/utils.py b/patroni/utils.py index 45ada864..898b058e 100644 --- a/patroni/utils.py +++ b/patroni/utils.py @@ -46,6 +46,7 @@ def calculate_ttl(expiration): def sigterm_handler(signo, stack_frame): + signal.signal(signal.SIGTERM, signal.SIG_IGN) sys.exit() diff --git a/tests/test_patroni.py b/tests/test_patroni.py index 65293d03..7481a6fd 100644 --- a/tests/test_patroni.py +++ b/tests/test_patroni.py @@ -40,6 +40,9 @@ def keyboard_interrupt(*args): class Mock_BaseServer__is_shut_down: + def wait(self): + pass + def set(self): pass From 3145e94797af2e175fc7ccf8c017110c3b87cddb Mon Sep 17 00:00:00 2001 From: Alexander Kukushkin Date: Fri, 18 Sep 2015 15:27:09 +0200 Subject: [PATCH 2/4] check ignore_sigterm flag instead of setting new handler for SIGTERM --- patroni/utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/patroni/utils.py b/patroni/utils.py index 898b058e..19d5837a 100644 --- a/patroni/utils.py +++ b/patroni/utils.py @@ -8,6 +8,7 @@ import time from patroni.exceptions import DCSError +ignore_sigterm = False interrupted_sleep = False reap_children = False @@ -46,8 +47,10 @@ def calculate_ttl(expiration): def sigterm_handler(signo, stack_frame): - signal.signal(signal.SIGTERM, signal.SIG_IGN) - sys.exit() + global ignore_sigterm + if not ignore_sigterm: + ignore_sigterm = True + sys.exit() def sigchld_handler(signo, stack_frame): From 9dfb562a4f560c5c9d0349a9d25dad24e547688b Mon Sep 17 00:00:00 2001 From: Alexander Kukushkin Date: Fri, 18 Sep 2015 15:29:50 +0200 Subject: [PATCH 3/4] wait for event on leader key a little bit longer than timeout --- patroni/etcd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patroni/etcd.py b/patroni/etcd.py index c0950c85..fc6d5a97 100644 --- a/patroni/etcd.py +++ b/patroni/etcd.py @@ -251,7 +251,7 @@ class Etcd(AbstractDCS): while index and timeout >= 1: # when timeout is too small urllib3 doesn't have enough time to connect try: - self.client.watch(self.leader_path, index=index + 1, timeout=timeout) + self.client.watch(self.leader_path, index=index + 1, timeout=timeout + 0.5) # Synchronous work of all cluster members with etcd is less expensive # than reestablishing http connection every time from every replica. return True From 0a3a6d72cf98e8a81e212d45cba3a7b9f2b3ccc9 Mon Sep 17 00:00:00 2001 From: Alexander Kukushkin Date: Fri, 18 Sep 2015 15:33:58 +0200 Subject: [PATCH 4/4] Fix etcd unit tests --- tests/test_etcd.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_etcd.py b/tests/test_etcd.py index 10a602db..9ded883b 100644 --- a/tests/test_etcd.py +++ b/tests/test_etcd.py @@ -67,11 +67,11 @@ def requests_get(url, **kwargs): def etcd_watch(key, index=None, timeout=None, recursive=None): - if timeout == 1: + if timeout == 2.0: raise urllib3.exceptions.TimeoutError - elif timeout == 5: + elif timeout == 5.0: return etcd.EtcdResult('delete', {}) - elif timeout == 10: + elif timeout == 10.0: raise etcd.EtcdException elif index == 20729: return etcd.EtcdResult('set', {'value': 'postgresql1', 'modifiedIndex': index + 1}) @@ -281,7 +281,7 @@ class TestEtcd(unittest.TestCase): self.etcd.client.watch = etcd_watch self.etcd.watch(100) self.etcd.get_cluster() - self.etcd.watch(1) - self.etcd.watch(5) - self.etcd.watch(10) + self.etcd.watch(1.5) + self.etcd.watch(4.5) + self.etcd.watch(9.5) self.etcd.watch(100)