fix(controller): stop flooding clients with transitional node statuses

Every node lifecycle call (start/stop) emitted a "starting"/"stopping"
node.updated notification on entry, and for the whole duration of the
call asdict() masked the real status with the transitional one — every
compute event forwarded in that window was re-emitted as a duplicate
transitional status.

Stop-all/start-all on a 13-node project pushed two transitional events
per node before the final state. Clients that do not render these
transitional statuses (the Web UI) showed nodes flickering through an
unexpected suspended-looking state before reaching started/stopped.

Remove the transitional notification and the status masking: clients
see the real statuses again (started/stopped), from the forwarded
compute events plus the controller-side confirmation. The wait option
on the lifecycle routes, the strict stop error handling and
wait_for_status() are unchanged.
This commit is contained in:
YueGuobin committed 2026-10-10 00:24:20 +08:00
1 parent 5c6478e77e
commit 82015dcb5c
2 files changed
+28 -43

No files matched your search

+6 -4
View File
@@ -486,7 +486,7 @@ class TestNodeRoutes:
response = await client.get(app.url_path_for("get_node", project_id=project.id, node_id=node.id))
assert response.json()["status"] == "started"
async def test_start_node_reports_starting_while_in_flight(
async def test_start_node_keeps_real_status_while_in_flight(
self, app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node
) -> None:
@@ -498,10 +498,11 @@ class TestNodeRoutes:
compute.post = post
response = await client.post(app.url_path_for("start_node", project_id=project.id, node_id=node.id), json={})
assert response.status_code == status.HTTP_200_OK
assert seen == ["starting"]
# no transitional status is reported while the call is in flight
assert seen == ["stopped"]
assert response.json()["status"] == "started"
async def test_stop_node_reports_stopping_while_in_flight(
async def test_stop_node_keeps_real_status_while_in_flight(
self, app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node
) -> None:
@@ -514,7 +515,8 @@ class TestNodeRoutes:
node._status = "started"
response = await client.post(app.url_path_for("stop_node", project_id=project.id, node_id=node.id))
assert response.status_code == status.HTTP_200_OK
assert seen == ["stopping"]
# no transitional status is reported while the call is in flight
assert seen == ["started"]
assert response.json()["status"] == "stopped"
async def test_start_node_wait_timeout(