mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-26 13:16:24 +00:00
* fix(sandbox): leave SIGTERM/SIGINT teardown to the Gateway lifespan AIO and E2B providers built on the event-loop thread installed SIGTERM/SIGINT handlers that force-destroyed every sandbox synchronously, before uvicorn started the run drain. The Gateway lifespan now holds sandbox shutdown ownership from its first step, so providers skip those handlers and the existing hook closes the provider after run drain and MCP teardown. SIGHUP keeps the provider fallback. AIO acquire now refuses once shutdown began. * docs(sandbox): document Gateway-owned sandbox shutdown and AIO admission fence * test(gateway): inspect the lifespan body now that lifespan wraps it * docs(changelog): reference #7560 for the sandbox SIGTERM drain-order fix * ci: retrigger sandbox image smoke after a registry pull timeout
38 lines
1.2 KiB
Python
38 lines
1.2 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import inspect
|
|
import logging
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
import pytest
|
|
from fastapi import FastAPI
|
|
|
|
from app.gateway import app as gateway_app
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_scheduled_task_service_shutdown_is_bounded(monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture) -> None:
|
|
async def hang() -> None:
|
|
await asyncio.sleep(3600)
|
|
|
|
app = FastAPI()
|
|
service = MagicMock()
|
|
service.stop = AsyncMock(side_effect=hang)
|
|
app.state.scheduled_task_service = service
|
|
monkeypatch.setattr(gateway_app, "_SHUTDOWN_HOOK_TIMEOUT_SECONDS", 0.01)
|
|
|
|
started = asyncio.get_running_loop().time()
|
|
with caplog.at_level(logging.WARNING, logger="app.gateway.app"):
|
|
await gateway_app._shutdown_scheduled_task_service(app)
|
|
elapsed = asyncio.get_running_loop().time() - started
|
|
|
|
assert elapsed < 0.5
|
|
service.stop.assert_awaited_once()
|
|
assert "Scheduled task service shutdown exceeded" in caplog.text
|
|
|
|
|
|
def test_lifespan_uses_bounded_scheduled_task_shutdown() -> None:
|
|
source = inspect.getsource(gateway_app._gateway_lifespan)
|
|
assert "await _shutdown_scheduled_task_service(app)" in source
|