mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-16 07:19:13 +08:00
Listen on the reserved test server port right away
Linux lets several sockets with SO_REUSEADDR bind the same port as long as none of them listens, so two xdist workers that drew the same random port could both reserve it while their apps were building. Listening right after the bind makes the reservation exclusive.
This commit is contained in:
@@ -568,10 +568,12 @@ def attempt_ports(port=None, set_galaxy_web_port=True) -> str:
|
||||
|
||||
|
||||
def reserve_port(host: str, port: str | None = None) -> socket.socket:
|
||||
"""Bind the server socket up front and keep it until the server serves on it.
|
||||
"""Bind and listen on the server socket now, and keep it until the server serves on it.
|
||||
|
||||
Releasing a probed port while the app builds would let another server (say, another
|
||||
pytest-xdist worker's) take it, and the readiness check would then reach that server.
|
||||
A port released while the app builds can be taken by another test server, whose
|
||||
responses would then satisfy this server's readiness check. Listening makes the
|
||||
reservation exclusive: Linux lets several SO_REUSEADDR sockets bind one port while
|
||||
none of them listens.
|
||||
"""
|
||||
candidates = [int(port)] if port is not None else [random.randint(8000, 10000) for _ in range(9)]
|
||||
for candidate in candidates:
|
||||
@@ -579,6 +581,7 @@ def reserve_port(host: str, port: str | None = None) -> socket.socket:
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
try:
|
||||
sock.bind((host, candidate))
|
||||
sock.listen()
|
||||
except OSError:
|
||||
sock.close()
|
||||
continue
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from fastapi import FastAPI
|
||||
from starlette.routing import Mount
|
||||
|
||||
@@ -27,6 +28,16 @@ def test_reserved_port_stays_taken_until_closed():
|
||||
assert attempt_port(port) == port
|
||||
|
||||
|
||||
def test_reserved_port_cannot_be_reserved_twice():
|
||||
sock = reserve_port("127.0.0.1")
|
||||
port = sock.getsockname()[1]
|
||||
try:
|
||||
with pytest.raises(Exception, match="bound to specified test server port"):
|
||||
reserve_port("127.0.0.1", str(port))
|
||||
finally:
|
||||
sock.close()
|
||||
|
||||
|
||||
def _gx_app(new_file_path: str) -> Mock:
|
||||
# Spell out every setting caching_fast_app_factory reads: an unset Mock attribute is
|
||||
# a truthy Mock, which sends the factory down the uncached path.
|
||||
|
||||
Reference in New Issue
Block a user