From 18699f684c19feab64689ebae4e1b4bd4095a69c Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Sun, 4 May 2025 15:13:42 +0300 Subject: [PATCH 1/4] [FIX] PostgresNode.__exit__ releases port after shutdown and cleanup We must release a port number after the shutdown and cleanup operation not before. It is a part of work for #249 --- testgres/node.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testgres/node.py b/testgres/node.py index 41504e8..8fdc116 100644 --- a/testgres/node.py +++ b/testgres/node.py @@ -231,8 +231,6 @@ def __enter__(self): return self def __exit__(self, type, value, traceback): - self.free_port() - # NOTE: Ctrl+C does not count! got_exception = type is not None and type != KeyboardInterrupt @@ -246,6 +244,8 @@ def __exit__(self, type, value, traceback): else: self._try_shutdown(attempts) + self.free_port() + def __repr__(self): return "{}(name='{}', port={}, base_dir='{}')".format( self.__class__.__name__, From 10ebf18f0c60b9e88db0d99e3823993045420adf Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Sun, 4 May 2025 15:23:46 +0300 Subject: [PATCH 2/4] PostgresNode::_release_resource is added This private method releases all the allocated node resources (port and so on). PostgresNode::__exit__ calls this new method instead free_port. --- testgres/node.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/testgres/node.py b/testgres/node.py index 8fdc116..65611b3 100644 --- a/testgres/node.py +++ b/testgres/node.py @@ -244,7 +244,7 @@ def __exit__(self, type, value, traceback): else: self._try_shutdown(attempts) - self.free_port() + self._release_resources() def __repr__(self): return "{}(name='{}', port={}, base_dir='{}')".format( @@ -663,6 +663,9 @@ def _try_shutdown(self, max_attempts, with_force=False): ps_output, ps_command) + def _release_resources(self): + self.free_port() + @staticmethod def _throw_bugcheck__unexpected_result_of_ps(result, cmd): assert type(result) == str # noqa: E721 From 102289bc7292d8bca6ded4fe430acce4fdd2cfca Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Sun, 4 May 2025 15:26:43 +0300 Subject: [PATCH 3/4] The new argument 'release_resources' of PostgresNode::cleanup method is added When this argument is True, cleanup calls _release_resources method. Default value is False. --- testgres/node.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/testgres/node.py b/testgres/node.py index 65611b3..defc0b4 100644 --- a/testgres/node.py +++ b/testgres/node.py @@ -1343,7 +1343,7 @@ def free_port(self): self._port = None self._port_manager.release_port(port) - def cleanup(self, max_attempts=3, full=False): + def cleanup(self, max_attempts=3, full=False, release_resources=False): """ Stop node if needed and remove its data/logs directory. NOTE: take a look at TestgresConfig.node_cleanup_full. @@ -1366,6 +1366,9 @@ def cleanup(self, max_attempts=3, full=False): self.os_ops.rmdirs(rm_dir, ignore_errors=False) + if release_resources: + self._release_resources() + return self @method_decorator(positional_args_hack(['dbname', 'query'])) From 02f7f00d380c7455d42ae17480880afdf2be733f Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Sun, 4 May 2025 15:33:27 +0300 Subject: [PATCH 4/4] [#249] NodeBackup::spawn_replica does not release a reserved port number during failure NodeBackup::spawn_replica uses an explict "rollback" code to destroy a newly allocated node to release a reserved port number. --- testgres/backup.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/testgres/backup.py b/testgres/backup.py index 388697b..857c46d 100644 --- a/testgres/backup.py +++ b/testgres/backup.py @@ -184,14 +184,19 @@ def spawn_replica(self, name=None, destroy=True, slot=None): """ # Build a new PostgresNode - with clean_on_error(self.spawn_primary(name=name, - destroy=destroy)) as node: + node = self.spawn_primary(name=name, destroy=destroy) + assert node is not None + try: # Assign it a master and a recovery file (private magic) node._assign_master(self.original_node) node._create_recovery_conf(username=self.username, slot=slot) + except: # noqa: E722 + # TODO: Pass 'final=True' ? + node.cleanup(release_resources=True) + raise - return node + return node def cleanup(self): """