From 18699f684c19feab64689ebae4e1b4bd4095a69c Mon Sep 17 00:00:00 2001
From: "d.kovalenko" <dmitry.lipetsk@gmail.com>
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 41504e89..8fdc116e 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" <dmitry.lipetsk@gmail.com>
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 8fdc116e..65611b3c 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" <dmitry.lipetsk@gmail.com>
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 65611b3c..defc0b40 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" <dmitry.lipetsk@gmail.com>
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 388697b7..857c46d4 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):
"""