fish_async_task.performance.batch_updater 源代码

"""批量状态更新器

实现批量状态更新功能,减少锁竞争,支持高吞吐量任务提交。
使用 collections.deque 作为更新缓冲区,支持自动和手动刷新。
"""

import threading
import time
from collections import deque
from typing import Deque, Dict, Optional, Tuple

from fish_async_task.performance._logging import get_logger
from fish_async_task.types import TaskStatusDict


[文档] class BatchedStatusUpdater: """ 批量状态更新器 将多个状态更新缓存到缓冲区,然后批量刷新到底层存储, 减少锁竞争和提高吞吐量。 Attributes: buffer_size: 触发自动刷新的缓冲区大小 flush_interval: 触发自动刷新的时间间隔(秒) underlying_store: 底层任务状态存储(可选,用于测试) Examples: >>> store = {} >>> updater = BatchedStatusUpdater( ... buffer_size=100, ... flush_interval=1.0, ... underlying_store=store ... ) >>> updater.queue_update("task-1", {"status": "running"}) >>> updater.flush() # 手动刷新 1 >>> updater.close() # 关闭并刷新所有待处理更新 """
[文档] def __init__( self, buffer_size: int = 100, flush_interval: float = 1.0, underlying_store: Optional[Dict[str, TaskStatusDict]] = None, ) -> None: """ 初始化批量状态更新器 Args: buffer_size: 触发自动刷新的缓冲区大小,默认 100 flush_interval: 触发自动刷新的时间间隔(秒),默认 1.0 underlying_store: 底层任务状态存储(可选,用于测试) Raises: ValueError: 如果 buffer_size < 1 或 flush_interval <= 0 """ if buffer_size < 1: raise ValueError(f"buffer_size 必须 >= 1,当前值: {buffer_size}") if flush_interval <= 0: raise ValueError(f"flush_interval 必须 > 0,当前值: {flush_interval}") self.buffer_size = buffer_size self.flush_interval = flush_interval # 底层存储(可选) if underlying_store is not None: self._underlying_store = underlying_store else: self._underlying_store = {} # 更新缓冲区:存储 (task_id, status) 元组 self._buffer: Dict[str, TaskStatusDict] = {} # 线程安全锁 self._queue_lock = threading.Lock() # 最后刷新时间 self._last_flush_time = time.time() # 是否已关闭 self._closed = False # 日志记录器 self.logger = get_logger() self.logger.info( f"初始化批量状态更新器:buffer_size={buffer_size}, " f"flush_interval={flush_interval}s" )
[文档] def queue_update(self, task_id: str, status: TaskStatusDict) -> None: """ 将状态更新排队到缓冲区 如果缓冲区达到 buffer_size,会自动触发刷新。 Args: task_id: 任务 ID status: 任务状态字典 Raises: RuntimeError: 如果更新器已关闭 Thread-Safety: 线程安全 Examples: >>> updater = BatchedStatusUpdater() >>> updater.queue_update("task-1", {"status": "running"}) """ if self._closed: raise RuntimeError("BatchedStatusUpdater 已关闭,无法接受新更新") with self._queue_lock: # 检查是否需要自动刷新(基于时间) current_time = time.time() time_since_last_flush = current_time - self._last_flush_time # 如果距离上次刷新超过间隔,先刷新旧数据 if time_since_last_flush >= self.flush_interval and self._buffer: self._flush_unlocked() self._last_flush_time = current_time # 将更新添加到缓冲区(覆盖同一任务的旧更新) self._buffer[task_id] = status # 检查是否需要自动刷新(基于大小) if len(self._buffer) >= self.buffer_size: self._flush_unlocked() self._last_flush_time = current_time
[文档] def flush(self) -> int: """ 手动刷新缓冲区到底层存储 Returns: 刷新的任务数量 Thread-Safety: 线程安全 Examples: >>> updater = BatchedStatusUpdater() >>> updater.queue_update("task-1", {"status": "running"}) >>> flushed = updater.flush() >>> print(f"刷新了 {flushed} 个任务") """ with self._queue_lock: return self._flush_unlocked()
def _flush_unlocked(self) -> int: """ 内部方法:无锁刷新(调用者必须持有 _queue_lock) Returns: 刷新的任务数量 """ if not self._buffer: return 0 # 批量更新到底层存储 flushed_count = 0 for task_id, status in self._buffer.items(): self._underlying_store[task_id] = status flushed_count += 1 # 清空缓冲区 self._buffer.clear() if flushed_count > 0: self.logger.info(f"批量刷新: 数量={flushed_count}, 缓冲区大小={self.buffer_size}") return flushed_count
[文档] def update_sync(self, task_id: str, status: TaskStatusDict) -> None: """ 同步更新(立即写入底层存储,不经过缓冲区) 用于需要立即更新的场景,例如关键状态变更。 Args: task_id: 任务 ID status: 任务状态字典 Raises: RuntimeError: 如果更新器已关闭 Thread-Safety: 线程安全 Examples: >>> updater = BatchedStatusUpdater() >>> updater.update_sync("task-1", {"status": "completed"}) """ if self._closed: raise RuntimeError("BatchedStatusUpdater 已关闭,无法接受新更新") with self._queue_lock: # 直接写入底层存储 self._underlying_store[task_id] = status
[文档] def get_buffer_length(self) -> int: """ 获取当前缓冲区长度 Returns: 缓冲区中的任务数量 Thread-Safety: 线程安全(返回近似值,但非常准确) Examples: >>> updater = BatchedStatusUpdater() >>> updater.queue_update("task-1", {"status": "running"}) >>> updater.get_buffer_length() 1 """ with self._queue_lock: return len(self._buffer)
[文档] def close(self) -> None: """ 关闭更新器并刷新所有待处理的更新 关闭后不再接受新更新,但会确保所有已排队的更新都被刷新。 Thread-Safety: 线程安全 Examples: >>> updater = BatchedStatusUpdater() >>> updater.queue_update("task-1", {"status": "running"}) >>> updater.close() # 刷新所有待处理更新并关闭 """ with self._queue_lock: if not self._closed: # 刷新所有待处理的更新 self._flush_unlocked() self._closed = True self.logger.info("批量状态更新器已关闭")