Skip to content

Commit 7b246ed

Browse files
authored
Grpc upgrade: prevent from reusing same port too soon after server shutdown (#1111)
1 parent 6dfd061 commit 7b246ed

File tree

3 files changed

+39
-3
lines changed

3 files changed

+39
-3
lines changed

src/ansys/dpf/core/server.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@ def shutdown_all_session_servers():
121121
print(e.args)
122122
pass
123123
shutdown_global_server()
124-
_server_instances.clear()
125124

126125

127126
def start_local_server(

src/ansys/dpf/core/server_types.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ def launch_dpf(ansys_path, ip=LOCALHOST, port=DPF_DEFAULT_PORT, timeout=10):
202202
_wait_and_check_server_connection(
203203
process, port, timeout, lines, current_errors, stderr=None, stdout=None
204204
)
205+
return process
205206

206207

207208
def launch_dpf_on_docker(
@@ -368,6 +369,33 @@ def check_ansys_grpc_dpf_version(server, timeout):
368369
)
369370

370371

372+
class GhostServer:
373+
ip: str
374+
_port: int
375+
close_time: float
376+
377+
def __init__(self, ip: str, port: int, close_time: float = None):
378+
"""
379+
Internal class used to keep in memory the port used by previous servers.
380+
Adds a timeout before reusing ports of shutdown servers.
381+
"""
382+
self.ip = ip
383+
self._port = port
384+
self.closed_time = close_time
385+
if self.closed_time is None:
386+
self.closed_time = time.time()
387+
388+
@property
389+
def port(self) -> int:
390+
"""Returns the port of shutdown server if the shutdown happened less than 10s ago."""
391+
if time.time() - self.closed_time > 10:
392+
return -1
393+
return self._port
394+
395+
def __call__(self, *args, **kwargs):
396+
return self
397+
398+
371399
class BaseServer(abc.ABC):
372400
"""Abstract class for servers"""
373401

@@ -599,7 +627,14 @@ def __del__(self):
599627
if hasattr(core, "_server_instances") and core._server_instances is not None:
600628
for i, server in enumerate(core._server_instances):
601629
if server() == self:
602-
core._server_instances.remove(server)
630+
if hasattr(self, "_input_ip") and hasattr(self, "_input_port"):
631+
# keeps a ghost instance with the used port and ip to prevent
632+
# from reusing the port to soon after shutting down: bug
633+
core._server_instances[i] = GhostServer(
634+
self._input_ip, self._input_port
635+
)
636+
else:
637+
core._server_instances.remove(server)
603638
except:
604639
warnings.warn(traceback.format_exc())
605640

@@ -699,7 +734,6 @@ def __init__(
699734
self._local_server = True
700735

701736
self._client = GrpcClient(address)
702-
703737
# store port and ip for later reference
704738
self._address = address
705739
self._input_ip = ip

tests/entry/test_entry.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,11 @@ def test_apply_context_remote(remote_config_server_type):
8383
op.connect(0, field)
8484
op.connect(1, 0.0)
8585
op.eval()
86+
field = None
8687
assert dpf.SERVER.context == dpf.AvailableServerContexts.premium
8788
dpf.server.shutdown_all_session_servers()
89+
field = dpf.Field()
90+
field.append([0.0], 1)
8891
if conftest.SERVERS_VERSION_GREATER_THAN_OR_EQUAL_TO_6_1:
8992
with pytest.raises(errors.DPFServerException):
9093
op = dpf.Operator("core::field::high_pass")

0 commit comments

Comments
 (0)