diff --git a/patroni/ctl.py b/patroni/ctl.py index 99d85dcf..73d0614a 100644 --- a/patroni/ctl.py +++ b/patroni/ctl.py @@ -332,6 +332,10 @@ def is_citus_cluster() -> bool: return click.get_current_context().obj['__mpp'].is_enabled() +# Cache DCS instances for given scope and group +__dcs_cache: Dict[Tuple[str, Optional[int]], AbstractDCS] = {} + + def get_dcs(scope: str, group: Optional[int]) -> AbstractDCS: """Get the DCS object. @@ -345,6 +349,8 @@ def get_dcs(scope: str, group: Optional[int]) -> AbstractDCS: :raises: :class:`PatroniCtlException`: if not suitable DCS configuration could be found. """ + if (scope, group) in __dcs_cache: + return __dcs_cache[(scope, group)] config = _get_configuration() config.update({'scope': scope, 'patronictl': True}) if group is not None: @@ -355,6 +361,7 @@ def get_dcs(scope: str, group: Optional[int]) -> AbstractDCS: if is_citus_cluster() and group is None: dcs.is_mpp_coordinator = lambda: True click.get_current_context().obj['__mpp'] = dcs.mpp + __dcs_cache[(scope, group)] = dcs return dcs except PatroniException as e: raise PatroniCtlException(str(e)) diff --git a/tests/test_ctl.py b/tests/test_ctl.py index a59a39ed..07359ca6 100644 --- a/tests/test_ctl.py +++ b/tests/test_ctl.py @@ -249,8 +249,8 @@ class TestCtl(unittest.TestCase): @patch('patroni.dynamic_loader.iter_modules', Mock(return_value=['patroni.dcs.dummy', 'patroni.dcs.etcd'])) def test_get_dcs(self): with click.Context(click.Command('list')) as ctx: - ctx.obj = {'__config': {'dummy': {}}, '__mpp': get_mpp({})} - self.assertRaises(PatroniCtlException, get_dcs, 'dummy', 0) + ctx.obj = {'__config': {'dummy2': {}}, '__mpp': get_mpp({})} + self.assertRaises(PatroniCtlException, get_dcs, 'dummy2', 0) @patch('patroni.psycopg.connect', psycopg_connect) @patch('patroni.ctl.query_member', Mock(return_value=([['mock column']], None)))