From 0980838cb3df9d4810743deba872d1b719360912 Mon Sep 17 00:00:00 2001 From: Ants Aasma Date: Wed, 26 Jan 2022 14:52:25 +0200 Subject: [PATCH] Fix port in use error on certificate replacement (#2185) When switching certificates there is a race condition with a concurrent API request. If there is one active during the replacement period then the replacement will error out with a port in use error and Patroni gets stuck in a state without an active API server. Fix is to call server_close after shutdown which will wait for already running requests to complete before returning. Close #2184 --- patroni/api.py | 2 ++ tests/test_api.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/patroni/api.py b/patroni/api.py index 505852a9..07db06ae 100644 --- a/patroni/api.py +++ b/patroni/api.py @@ -768,6 +768,8 @@ class RestApiServer(ThreadingMixIn, HTTPServer, Thread): reloading_config = self.__listen is not None # changing config in runtime if reloading_config: self.shutdown() + # Rely on ThreadingMixIn.server_close() to have all requests terminate before we continue + self.server_close() self.__listen = listen self.__ssl_options = ssl_options diff --git a/tests/test_api.py b/tests/test_api.py index 2d901a1f..e83edfef 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -549,7 +549,8 @@ class TestRestApiServer(unittest.TestCase): self.assertRaises(ValueError, MockRestApiServer, None, '', bad_config) self.assertRaises(ValueError, self.srv.reload_config, bad_config) self.assertRaises(ValueError, self.srv.reload_config, {}) - with patch.object(socket.socket, 'setsockopt', Mock(side_effect=socket.error)): + with patch.object(socket.socket, 'setsockopt', Mock(side_effect=socket.error)), \ + patch.object(MockRestApiServer, 'server_close', Mock()): self.srv.reload_config({'listen': ':8008'}) @patch.object(MockPatroni, 'dcs')