Skip to content

Commit 69135bf

Browse files
ravwojdylaclaude
andauthored
fix(iris): fixup Starlette on_shutdown (#4155)
* regression from https://github.com/marin-community/marin/pull/4126/changes --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2a88ad6 commit 69135bf

3 files changed

Lines changed: 26 additions & 5 deletions

File tree

lib/iris/src/iris/cluster/controller/dashboard.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525

2626
import logging
2727
import os
28-
from http.cookies import SimpleCookie
2928
from collections.abc import Callable
29+
from http.cookies import SimpleCookie
3030
from urllib.parse import urlparse
3131

3232
import httpx
@@ -39,7 +39,7 @@
3939

4040
from iris.cluster.controller.actor_proxy import PROXY_ROUTE, ActorProxy
4141
from iris.cluster.controller.service import ControllerServiceImpl
42-
from iris.cluster.dashboard_common import html_shell, static_files_mount
42+
from iris.cluster.dashboard_common import html_shell, on_shutdown, static_files_mount
4343
from iris.rpc.auth import SESSION_COOKIE, NullAuthInterceptor, TokenVerifier, extract_bearer_token, resolve_auth
4444
from iris.rpc.cluster_connect import ControllerServiceWSGIApplication
4545
from iris.rpc.interceptors import RequestTimingInterceptor
@@ -281,9 +281,10 @@ async def _proxy_actor_rpc(request: Request) -> Response:
281281
Mount(rpc_wsgi_app.path, app=rpc_app),
282282
static_files_mount(),
283283
]
284+
284285
app: Starlette | _RouteAuthMiddleware = Starlette(
285286
routes=routes,
286-
on_shutdown=[self._actor_proxy.close],
287+
lifespan=on_shutdown(self._actor_proxy.close),
287288
)
288289
if self._auth_verifier is not None and self._auth_provider is not None:
289290
app = _RouteAuthMiddleware(app, self._auth_verifier, optional=self._auth_optional)
@@ -429,7 +430,8 @@ def _create_app(self) -> Starlette:
429430
Route("/iris.cluster.ControllerService/{method}", self._proxy_rpc, methods=["POST"]),
430431
static_files_mount(),
431432
]
432-
return Starlette(routes=routes, on_shutdown=[self._client.aclose])
433+
434+
return Starlette(routes=routes, lifespan=on_shutdown(self._client.aclose))
433435

434436
def _proxy_html(self, dashboard_type: str) -> HTMLResponse:
435437
html = html_shell("Iris Controller (Proxy)", dashboard_type)

lib/iris/src/iris/cluster/dashboard_common.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,34 @@
99
"""
1010

1111
import logging
12+
from collections.abc import AsyncGenerator, Awaitable, Callable
13+
from contextlib import AbstractAsyncContextManager, asynccontextmanager
1214
from pathlib import Path
1315
from typing import Any
1416

17+
from starlette.applications import Starlette
1518
from starlette.responses import PlainTextResponse
1619
from starlette.routing import Mount
1720
from starlette.staticfiles import StaticFiles
1821
from starlette.types import ASGIApp, Receive, Scope, Send
1922

2023
logger = logging.getLogger(__name__)
2124

25+
26+
def on_shutdown(
27+
*callbacks: Callable[[], Awaitable[None]],
28+
) -> Callable[[Starlette], AbstractAsyncContextManager[None]]:
29+
"""Build a Starlette ``lifespan`` that runs *callbacks* on shutdown."""
30+
31+
@asynccontextmanager
32+
async def _lifespan(_app: Starlette) -> AsyncGenerator[None, None]:
33+
yield
34+
for cb in callbacks:
35+
await cb()
36+
37+
return _lifespan
38+
39+
2240
# Vue dashboard build output. The path from this file (cluster/dashboard_common.py)
2341
# up to lib/iris/ is four parent directories, then down into dashboard/dist.
2442
VUE_DIST_DIR = Path(__file__).parent.parent.parent.parent / "dashboard" / "dist"

lib/iris/tests/actor/test_actor_proxy.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from iris.actor import ActorClient, ActorServer
1515
from iris.actor.resolver import ACTOR_ENDPOINT_HEADER, ProxyResolver
1616
from iris.cluster.controller.actor_proxy import PROXY_ROUTE
17+
from iris.cluster.dashboard_common import on_shutdown
1718
from iris.managed_thread import ThreadContainer
1819
from iris.time_utils import Duration, ExponentialBackoff
1920

@@ -109,7 +110,7 @@ def _start_proxy_server(proxy: StandaloneActorProxy, threads: ThreadContainer) -
109110

110111
app = Starlette(
111112
routes=[Route(PROXY_ROUTE, proxy.handle, methods=["POST"])],
112-
on_shutdown=[proxy.close],
113+
lifespan=on_shutdown(proxy.close),
113114
)
114115
config = uvicorn.Config(app, host="127.0.0.1", port=port, log_level="error", log_config=None)
115116
server = uvicorn.Server(config)

0 commit comments

Comments
 (0)