ForcePilot/backend/package/yuxi/channels/infrastructure/scheduler.py
Kris bb1934023e refactor: 完成会话持久化与事务机制重构,清理冗余代码
本次提交是一次大型架构重构,核心变更包括:
1. 调整持久化适配器为无状态实现,通过session_factory按需获取会话
2. 重构事务共享与透传机制,统一使用_session_scope管理会话生命周期
3. 移除OutboxEntry聚合根内的版本自增逻辑,由持久化层统一管理
4. 优化微信插件会话类型枚举与配置对齐
5. 简化出站管道阶段依赖注入与流程逻辑
6. 删除健康检查自动释放会话的冗余代码
7. 重构多个定时任务处理器,移除显式会话工厂创建逻辑
8. 修复会话延迟加载异常问题,新增时区转换工具函数
2026-07-10 04:10:33 +08:00

165 lines
8.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""channels 模块的定时任务 handler 注册入口。
本模块是 channels 限界上下文接入 scheduler 限界上下文的唯一登记点,
对齐 scheduler 上下文设计方案 §14.3 的"业务域 container 主动注册"模式。
与 ``infrastructure/channel_use_cases.py`` 同级,遵循 channels 的分层约定:
装配逻辑集中在 ``infrastructure/``,包根不出现装配代码。
对外暴露:
- ``register_scheduler_handlers(registry)``:业务模块注册函数,由 worker
启动钩子(``services/run_worker.py`` 的 ``_worker_startup``)统一调用。
内部实例化本模块的 handler 并调用 ``registry.register(handler)``。
边界规范:
- 本模块是 channels 接入 scheduler 的**唯一装配点**
- handler 的依赖(如 ``session_factory``)在函数内部构造,不暴露给装配层
- 新增 handler 时,在本函数内追加 ``registry.register(...)`` 即可,无需改
``run_worker.py``(前提:本模块已登记在 ``_BUSINESS_HANDLER_REGISTRARS``
- handler 禁止依赖 channels api 进程内存态(``PluginRegistry`` /
``DegradationManager`` / ``EventBus`` 等),只能通过 ``session_factory``
+ DB 查询,确保在 worker 进程独立可运行
"""
from __future__ import annotations
from yuxi.channels.application.extension.scheduler_handlers.channel_audit_log_retention_handler import (
ChannelAuditLogRetentionHandler,
)
from yuxi.channels.application.extension.scheduler_handlers.channel_content_review_retention_handler import (
ChannelContentReviewRetentionHandler,
)
from yuxi.channels.application.extension.scheduler_handlers.channel_idempotency_cleanup_handler import (
ChannelIdempotencyCleanupHandler,
)
from yuxi.channels.application.extension.scheduler_handlers.channel_outbox_recovery_handler import (
ChannelOutboxRecoveryHandler,
)
from yuxi.channels.application.extension.scheduler_handlers.channel_outbox_terminal_cleanup_handler import (
ChannelOutboxTerminalCleanupHandler,
)
from yuxi.channels.application.extension.scheduler_handlers.channel_pairing_expiration_handler import (
ChannelPairingExpirationHandler,
)
from yuxi.channels.application.extension.scheduler_handlers.channel_pairing_terminal_cleanup_handler import (
ChannelPairingTerminalCleanupHandler,
)
from yuxi.channels.application.extension.scheduler_handlers.channel_session_inactive_cleanup_handler import (
ChannelSessionInactiveCleanupHandler,
)
from yuxi.channels.infrastructure.channel_use_cases import (
create_channel_audit_log_retention_handler_dependencies,
create_channel_content_review_retention_handler_dependencies,
create_channel_idempotency_cleanup_handler_dependencies,
create_channel_outbox_recovery_handler_dependencies,
create_channel_outbox_terminal_cleanup_handler_dependencies,
create_channel_pairing_expiration_handler_dependencies,
create_channel_pairing_terminal_cleanup_handler_dependencies,
create_channel_session_inactive_cleanup_handler_dependencies,
)
from yuxi.scheduler.framework.runtime import HandlerRegistry
from yuxi.services.run_queue_service import get_arq_pool
from yuxi.storage.postgres.manager import pg_manager
__all__ = ["register_scheduler_handlers"]
async def register_scheduler_handlers(registry: HandlerRegistry) -> None:
"""向 scheduler 注册 channels 模块的定时任务 handler。
由 worker 启动钩子在装配阶段统一调用。新增 handler 时在此函数内追加
``registry.register(...)`` 即可。
注册的 handler 分两类:
1. **运维型周期清理 handler**FR-04 ~ FR-08由 ``schedule_kind="interval"``
周期触发清理超期终态记录与回收卡死任务避免表无限增长。worker 进程
不注入 api 进程内存态(``EventBus`` / ``PluginRegistry`` 等),通过
``session_factory`` 创建独立 DB 会话执行清理。
2. **业务状态恢复/过期扫描 handler**FR-22 / FR-33 / FR-34替代原
api 进程后台扫描器,由 scheduler worker 周期触发,解决共享
``AsyncSession`` 并发使用与锁 TTL 不匹配问题。
所有 handler 通过分布式锁串行化多 worker 并发执行lock_key=
``scheduler:{class_name}``, TTL=300s锁获取失败时 skip不 fail-closed
下一周期重试)。
Args:
registry: worker 进程级 handler 注册表。
"""
from yuxi.channels.infrastructure.channel_use_cases import (
create_worker_cache_port,
)
# ``pg_manager.AsyncSession`` 为 ``async_sessionmaker`` 实例callable
# 调用返回 ``AsyncSession``),符合 ``Callable[[], AsyncSession]`` 协议。
# 由 ``run_worker._worker_startup`` 在调用本函数前通过 ``pg_manager.initialize()``
# 初始化完成。
session_factory = pg_manager.AsyncSession
arq_pool = await get_arq_pool()
cache_port = await create_worker_cache_port()
# ─────────────────────────────────────────────────────────────────────
# P1 运维型周期清理 handlerFR-04 ~ FR-08
# ─────────────────────────────────────────────────────────────────────
# FR-04清理长期 inactive 的临时会话is_temporary=true 且 last_message_at 超时)
session_cleanup_deps = create_channel_session_inactive_cleanup_handler_dependencies(
session_factory, cache_port=cache_port
)
registry.register(ChannelSessionInactiveCleanupHandler(**session_cleanup_deps))
# FR-05物理清理超期 DEAD / SENT 终态 Outbox 条目best-effort 发布
# OutboxEntryPurgedEventevent_publisher=Noneworker 进程未注入)
outbox_cleanup_deps = create_channel_outbox_terminal_cleanup_handler_dependencies(
session_factory, cache_port=cache_port
)
registry.register(ChannelOutboxTerminalCleanupHandler(**outbox_cleanup_deps))
# FR-06物理清理超期终态配对记录EXPIRED / REJECTED / REVOKED
pairing_cleanup_deps = create_channel_pairing_terminal_cleanup_handler_dependencies(
session_factory, cache_port=cache_port
)
registry.register(ChannelPairingTerminalCleanupHandler(**pairing_cleanup_deps))
# ─────────────────────────────────────────────────────────────────────
# FR-22 / FR-33 / FR-34业务恢复/过期扫描 handler
# 由 scheduler worker 触发,替代原 api 进程后台扫描器
# ─────────────────────────────────────────────────────────────────────
# FR-22Outbox 恢复扫描PENDING / SENT_UNCONFIRMED 入队重试 + 死信标记)
outbox_recovery_deps = create_channel_outbox_recovery_handler_dependencies(
session_factory,
arq_pool=arq_pool,
cache_port=cache_port,
)
registry.register(ChannelOutboxRecoveryHandler(**outbox_recovery_deps))
# FR-33配对过期扫描PENDING → EXPIRED
pairing_expiration_deps = create_channel_pairing_expiration_handler_dependencies(
session_factory, cache_port=cache_port
)
registry.register(ChannelPairingExpirationHandler(**pairing_expiration_deps))
# FR-34审计日志保留期清理
audit_log_retention_deps = create_channel_audit_log_retention_handler_dependencies(
session_factory, cache_port=cache_port
)
registry.register(ChannelAuditLogRetentionHandler(**audit_log_retention_deps))
# 内容审核记录保留期清理CR-STATS-01 配套 retention
# 物理删除超过 retention_days 的审核记录,避免表无限增长。默认 180 天,
# 比审计日志 90 天更长,因其用于运营分析与趋势统计。
content_review_retention_deps = create_channel_content_review_retention_handler_dependencies(
session_factory, cache_port=cache_port
)
registry.register(ChannelContentReviewRetentionHandler(**content_review_retention_deps))
# FR-19幂等记录过期清理
# 物理删除 expires_at 已过期的幂等记录,避免表无限增长。默认 TTL 24h。
idempotency_cleanup_deps = create_channel_idempotency_cleanup_handler_dependencies(
session_factory, cache_port=cache_port
)
registry.register(ChannelIdempotencyCleanupHandler(**idempotency_cleanup_deps))