mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-05-25 17:36:00 +00:00
fix(actor): harden lifecycle, supervision, Redis mailbox, and add comprehensive tests
- Fix spawn() zombie cell: clean up registry on start() failure - Fix shutdown(): cancel + await tasks that exceed graceful timeout - Fix _shutdown(): await mailbox.close() to release backend resources - Fix escalate directive: stop failing child before propagating to grandparent - Fix RedisMailbox.put(): wrap Redis errors in try/except, return False on failure - Fix retry.py: replace assert with proper raise for last_exc - Add put_batch() to Mailbox abstraction for single-roundtrip bulk enqueue - Add RedisMailbox.put_batch() with atomic Lua script for bounded queues - Add MailboxFullError exception type for semantic backpressure handling - Add redis>=7.4.0 dependency with public PyPI sources in uv.lock Tests added (31 total, up from 27): - test_middleware_on_restart_hook: verifies middleware.on_restart() on supervision restart - test_ask_propagates_actor_exception: ask() re-raises original exception type - test_ask_propagates_exception_while_supervised: exception propagates; root actor survives - test_ask_timeout_late_reply_no_exception: late reply after timeout is silent no-op - test_actor_backpressure.py: MailboxFullError + dead letter on full mailbox - test_actor_retry.py: ask_with_retry with exponential backoff - test_mailbox_redis.py: RedisMailbox put/get/batch/close - bench_actor_redis.py: RedisMailbox throughput benchmarks
This commit is contained in:
@@ -12,6 +12,12 @@ import asyncio
|
||||
from typing import Any
|
||||
|
||||
|
||||
BACKPRESSURE_BLOCK = "block"
|
||||
BACKPRESSURE_DROP_NEW = "drop_new"
|
||||
BACKPRESSURE_FAIL = "fail"
|
||||
BACKPRESSURE_POLICIES = {BACKPRESSURE_BLOCK, BACKPRESSURE_DROP_NEW, BACKPRESSURE_FAIL}
|
||||
|
||||
|
||||
class Mailbox(abc.ABC):
|
||||
"""Abstract mailbox — the message queue for an actor.
|
||||
|
||||
@@ -44,6 +50,18 @@ class Mailbox(abc.ABC):
|
||||
def full(self) -> bool:
|
||||
"""Return True if mailbox is at capacity."""
|
||||
|
||||
async def put_batch(self, msgs: list[Any]) -> int:
|
||||
"""Enqueue multiple messages. Returns count accepted.
|
||||
|
||||
Default implementation falls back to sequential ``put`` calls.
|
||||
Backends like Redis should override this for efficient bulk push.
|
||||
"""
|
||||
count = 0
|
||||
for msg in msgs:
|
||||
if await self.put(msg):
|
||||
count += 1
|
||||
return count
|
||||
|
||||
async def close(self) -> None:
|
||||
"""Release resources. Default is no-op."""
|
||||
|
||||
@@ -55,23 +73,32 @@ class Empty(Exception):
|
||||
class MemoryMailbox(Mailbox):
|
||||
"""In-process mailbox backed by ``asyncio.Queue``."""
|
||||
|
||||
def __init__(self, maxsize: int = 256) -> None:
|
||||
def __init__(self, maxsize: int = 256, *, backpressure_policy: str = BACKPRESSURE_BLOCK) -> None:
|
||||
if backpressure_policy not in BACKPRESSURE_POLICIES:
|
||||
raise ValueError(
|
||||
f"Invalid backpressure_policy={backpressure_policy!r}, "
|
||||
f"expected one of {sorted(BACKPRESSURE_POLICIES)}"
|
||||
)
|
||||
self._queue: asyncio.Queue[Any] = asyncio.Queue(maxsize=maxsize)
|
||||
self._maxsize = maxsize
|
||||
self._backpressure_policy = backpressure_policy
|
||||
|
||||
async def put(self, msg: Any) -> bool:
|
||||
try:
|
||||
if self._backpressure_policy == BACKPRESSURE_BLOCK:
|
||||
await self._queue.put(msg)
|
||||
return True
|
||||
except asyncio.QueueFull:
|
||||
return False
|
||||
|
||||
def put_nowait(self, msg: Any) -> bool:
|
||||
try:
|
||||
if self._backpressure_policy in (BACKPRESSURE_DROP_NEW, BACKPRESSURE_FAIL):
|
||||
if self._queue.full():
|
||||
return False
|
||||
self._queue.put_nowait(msg)
|
||||
return True
|
||||
except asyncio.QueueFull:
|
||||
return False
|
||||
|
||||
def put_nowait(self, msg: Any) -> bool:
|
||||
if self._queue.full():
|
||||
return False
|
||||
self._queue.put_nowait(msg)
|
||||
return True
|
||||
|
||||
async def get(self) -> Any:
|
||||
return await self._queue.get()
|
||||
|
||||
Reference in New Issue
Block a user