From 065833de171f312f98dad8d20e4a785eca7a6c0d Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Tue, 13 Jan 2026 19:21:11 +0300 Subject: [PATCH] PostgresNode::start2 is added New method starts a node but does not touch 'is_started' flag. It is a special support for tests where node starts but implicitly stops within some time. For example server detects a corrupted data during recovery and stops. Test is added. --- src/node.py | 65 ++++++++++++++++++++++------------- tests/test_testgres_common.py | 28 +++++++++++++++ 2 files changed, 69 insertions(+), 24 deletions(-) diff --git a/src/node.py b/src/node.py index 477929e..0ea169d 100644 --- a/src/node.py +++ b/src/node.py @@ -1005,9 +1005,36 @@ def slow_start(self, replica=False, dbname='template1', username=None, max_attem raise return - def start(self, params=[], wait=True, exec_env=None): + def start(self, params=[], wait=True, exec_env=None) -> PostgresNode: """ - Starts the PostgreSQL node using pg_ctl if node has not been started. + Starts the PostgreSQL node using pg_ctl and set flag 'is_started'. + By default, it waits for the operation to complete before returning. + Optionally, it can return immediately without waiting for the start operation + to complete by setting the `wait` parameter to False. + + Args: + params: additional arguments for pg_ctl. + wait: wait until operation completes. + + Returns: + This instance of :class:`.PostgresNode`. + """ + self.start2() + + if not wait: + # Postmaster process is starting in background + self._manually_started_pm_pid = __class__._C_PM_PID__IS_NOT_DETECTED + else: + self._manually_started_pm_pid = self._get_node_state().pid + if self._manually_started_pm_pid is None: + self._raise_cannot_start_node(None, "Cannot detect postmaster pid.") + + assert type(self._manually_started_pm_pid) == int # noqa: E721 + return self + + def start2(self, params=[], wait=True, exec_env=None) -> None: + """ + Starts the PostgreSQL node using pg_ctl. By default, it waits for the operation to complete before returning. Optionally, it can return immediately without waiting for the start operation to complete by setting the `wait` parameter to False. @@ -1040,18 +1067,9 @@ def LOCAL__start_node(): if error and 'does not exist' in error: raise Exception(error) - def LOCAL__raise_cannot_start_node( - from_exception: typing.Optional[Exception], - msg: str - ): - assert from_exception is None or isinstance(from_exception, Exception) - assert type(msg) == str # noqa: E721 - files = self._collect_special_files() - raise_from(StartNodeException(msg, files), from_exception) - def LOCAL__raise_cannot_start_node__std(from_exception): assert isinstance(from_exception, Exception) - LOCAL__raise_cannot_start_node(from_exception, 'Cannot start node') + self._raise_cannot_start_node(from_exception, 'Cannot start node') if not self._should_free_port: try: @@ -1078,7 +1096,7 @@ def LOCAL__raise_cannot_start_node__std(from_exception): assert nAttempt > 0 assert nAttempt <= __class__._C_MAX_START_ATEMPTS if nAttempt == __class__._C_MAX_START_ATEMPTS: - LOCAL__raise_cannot_start_node(e, "Cannot start node after multiple attempts.") + self._raise_cannot_start_node(e, "Cannot start node after multiple attempts.") is_it_port_conflict = PostgresNodeUtils.delect_port_conflict(log_reader) @@ -1103,18 +1121,17 @@ def LOCAL__raise_cannot_start_node__std(from_exception): continue break self._maybe_start_logger() + return - if not wait: - # Postmaster process is starting in background - self._manually_started_pm_pid = __class__._C_PM_PID__IS_NOT_DETECTED - else: - self._manually_started_pm_pid = self._get_node_state().pid - if self._manually_started_pm_pid is None: - LOCAL__raise_cannot_start_node(None, "Cannot detect postmaster pid.") - - assert type(self._manually_started_pm_pid) == int # noqa: E721 - - return self + def _raise_cannot_start_node( + self, + from_exception: typing.Optional[Exception], + msg: str + ): + assert from_exception is None or isinstance(from_exception, Exception) + assert type(msg) == str # noqa: E721 + files = self._collect_special_files() + raise_from(StartNodeException(msg, files), from_exception) def stop(self, params=[], wait=True): """ diff --git a/tests/test_testgres_common.py b/tests/test_testgres_common.py index f4e12c8..8eb62fc 100644 --- a/tests/test_testgres_common.py +++ b/tests/test_testgres_common.py @@ -283,6 +283,34 @@ def test_uninitialized_start(self, node_svc: PostgresNodeService): assert node.status() == NodeStatus.Uninitialized return + def test_start2(self, node_svc: PostgresNodeService): + assert isinstance(node_svc, PostgresNodeService) + + with __class__.helper__get_node(node_svc) as node: + node.init() + assert not node.is_started + assert node.status() == NodeStatus.Stopped + node.start2() + assert not node.is_started + assert node.status() == NodeStatus.Running + + with pytest.raises(expected_exception=StartNodeException) as x: + # can't start node more than once + node.start2() + + assert x is not None + assert type(x.value) == StartNodeException # noqa: E721 + assert type(x.value.description) == str # noqa: E721 + assert type(x.value.message) == str # noqa: E721 + + assert x.value.description == "Cannot start node" + assert x.value.message.startswith(x.value.description) + + assert not node.is_started + assert node.status() == NodeStatus.Running + + return + def test_restart(self, node_svc: PostgresNodeService): assert isinstance(node_svc, PostgresNodeService)