Fix blocking sync I/O on the event loop across containers, jobs, and xmlrpc
DevPlace CI / test (push) Failing after 26m53s
DevPlace CI / test (push) Failing after 26m53s
The container/workspace reachability probes (socket connect + HTTP check) ran synchronously with real timeouts inside async request handlers and the live-view relay's 3-4s ticks, freezing the whole event loop whenever a container wasn't cleanly reachable - the likely cause of the periodic app-wide stalls. Converted the probe chain (api._port_reachable/_http_probe, editor_reachable, instance_runtime, provision.editor_ready/view) to real async I/O and parallelized the admin container/workspace list decorators. Also fixes: XmlrpcService.on_disable blocked up to 10s on a synchronous subprocess.wait inside an async method (now matches TelegramService's async-subprocess pattern); JobService._sweep_expired ran every job kind's cleanup() - including shutil.rmtree on large directories - synchronously on every tick, now offloaded via asyncio.to_thread for all job kinds at once; and several smaller blocking reads/writes on request/service paths (attachment-to-gitea mirroring, stealth chunked downloads, dbapi/isslop file reads, job payload/report I/O) moved off the loop thread. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
import asyncio
|
||||
import itertools
|
||||
import socket
|
||||
|
||||
@@ -117,7 +118,7 @@ def test_transitional_phases_are_exactly_starting_and_stopping():
|
||||
def test_editor_ready_when_the_editor_port_accepts_connections(listener):
|
||||
port = listener.getsockname()[1]
|
||||
row = _instance(container_ip="127.0.0.1", editor_port=port)
|
||||
assert provision.editor_ready(row) is True
|
||||
assert asyncio.run(provision.editor_ready(row)) is True
|
||||
|
||||
|
||||
def test_editor_ready_is_false_when_nothing_listens():
|
||||
@@ -126,12 +127,12 @@ def test_editor_ready_is_false_when_nothing_listens():
|
||||
port = probe.getsockname()[1]
|
||||
probe.close()
|
||||
row = _instance(container_ip="127.0.0.1", editor_port=port)
|
||||
assert provision.editor_ready(row) is False
|
||||
assert asyncio.run(provision.editor_ready(row)) is False
|
||||
|
||||
|
||||
def test_editor_ready_is_false_without_a_reachable_target(listener):
|
||||
row = _instance(container_ip="", ports_json="[]")
|
||||
assert provision.editor_ready(row) is False
|
||||
assert asyncio.run(provision.editor_ready(row)) is False
|
||||
|
||||
|
||||
def test_editor_ready_is_false_unless_the_container_runs(listener):
|
||||
@@ -140,7 +141,7 @@ def test_editor_ready_is_false_unless_the_container_runs(listener):
|
||||
if status == store.ST_RUNNING:
|
||||
continue
|
||||
row = _instance(container_ip="127.0.0.1", editor_port=port, status=status)
|
||||
assert provision.editor_ready(row) is False
|
||||
assert asyncio.run(provision.editor_ready(row)) is False
|
||||
|
||||
|
||||
def test_editor_ready_is_false_while_suspended(listener):
|
||||
@@ -148,7 +149,7 @@ def test_editor_ready_is_false_while_suspended(listener):
|
||||
row = _instance(
|
||||
container_ip="127.0.0.1", editor_port=port, suspended_at="2026-01-01T00:00:00"
|
||||
)
|
||||
assert provision.editor_ready(row) is False
|
||||
assert asyncio.run(provision.editor_ready(row)) is False
|
||||
|
||||
|
||||
def test_view_carries_the_phase_its_label_readiness_and_the_owner(listener):
|
||||
@@ -166,13 +167,13 @@ def test_view_carries_the_phase_its_label_readiness_and_the_owner(listener):
|
||||
"ports_json": "[]",
|
||||
}
|
||||
)
|
||||
view = provision.view(instance)
|
||||
view = asyncio.run(provision.view(instance))
|
||||
assert view["phase"] == provision.PHASE_READY
|
||||
assert view["phase_label"] == "Ready"
|
||||
assert view["editor_ready"] is True
|
||||
assert view["owner_uid"] == OWNER
|
||||
|
||||
store.update_instance(instance["uid"], {"desired_state": store.DESIRED_STOPPED})
|
||||
view = provision.view(store.get_instance(instance["uid"]))
|
||||
view = asyncio.run(provision.view(store.get_instance(instance["uid"])))
|
||||
assert view["phase"] == provision.PHASE_STOPPING
|
||||
assert view["editor_ready"] is True
|
||||
|
||||
Reference in New Issue
Block a user