world_sync/skill/SKILL.md
2026-09-21 13:33:56 +08:00

5.0 KiB
Raw Blame History

name description
world_sync world_sync 模块技能文档。

world_sync 模块

概述

世界数据同步模块,管理世界数据的导入/导出/合并同步记录与执行。依赖 world 模块(world 表)、appbase(appcodes 字典 sync_type/sync_status)。通过 load_world_sync() 挂载。

自 M11b-2/M11b-3 起,本模块同时承载 scense_runtime 服务端权威写入薄扩展:单事务事件+状态写入(pbl_runtime_authority.write_event_with_state)与事务提交后广播(pbl_runtime_broadcast)。

数据模型

表 world_sync(models/world_sync.json)

字段 类型 说明
id str(32) 主键
world_id str(32) 所属世界(→world.id)
sync_type str(16) 同步类型(0导入/1导出/2合并)
source str(255) 数据来源
status str(16) 状态(appcodes sync_status)
result_json text 同步结果 JSON
sync_date date 同步日期(业务日期)
created_at timestamp 创建时间
  • 主键:["id"];索引:idx_sync_world(world_id)
  • 字典:world_id→world、sync_type→appcodes_kv(parentid='sync_type')、status→appcodes_kv(parentid='sync_status')

运行时权威表(M11b-2,models/)

pbl_runtime_event(append-only 事件)、pbl_entity_state(实体状态,state_version 服务端权威)、 pbl_world_state_snapshot(世界快照)。所有读写强制 tenant_id 打头,缺失 fail-closed。

关键接口

  • list_world_syncs / get_world_sync / create_world_sync / update_world_sync / delete_world_sync / execute_world_sync
  • wwwroot/api/world_sync.dspy(同步契约)、create/world_sync_update/world_sync_delete.dspy、get_search_*.dspy
  • M11b-2:write_event_with_state(event_data, state_update, ...) —— 事件 + 状态在同一事务内提交
  • M11b-3 广播 API(world_sync/pbl_runtime_broadcast.py,已在 __init__.py 导出):
    • install_broadcast_hook() / is_broadcast_hook_installed():把 write_event_with_state 包成 COMMIT 后广播(幂等,load_world_sync() 已自动调用)
    • emit_post_commit_broadcast(write_result, context=...):手动发一条
    • wrap_with_post_commit_broadcast(fn):包装任意返回事务结果的函数
    • configure_broadcast(channels=..., webhook_urls=..., redis_url=..., queue_size=2000, retry_attempts=2, retry_backoff_s=0.02, enabled=True):宿主启动时配置通道
    • subscribe_broadcast(handler) / unsubscribe_broadcast(handler):进程内订阅
    • broadcast_stats() / broadcast_last_message() / broadcast_latency_percentile_ms(99) / drain_broadcast(timeout=2.0) / shutdown_broadcast():观测与优雅退出
    • 通道类:InProcessChannel、WebhookChannel、RedisPubSubChannel、NoopChannel(均继承 BaseChannel,publish(message))

事务提交后广播(M11b-3)要点

  • 触发点:仅事务 COMMIT 成功之后(commit_ts 即延迟起点);事务回滚不广播。
  • 异步:单 dispatcher 线程 + 有界 queue.Queue(2000),submit 用 put_nowait 微秒返回,不阻塞事务返回;队列满降级为当前线程直发一次。
  • SLO:BROADCAST_LATENCY_SLO_MS = 300.0,口径 P99;实测 n=200 p50=2.4ms / p99=4.1ms / max=4.2ms(scripts/m11b3_selftest_output.txt)。
  • 失败语义:fire-and-forget + 有限重试;通道/订阅者异常一律吞掉并 ERROR 日志 + stats["failed"] 计数,绝不回滚已提交事务、绝不外抛给业务调用方。
  • 无轮询:广播路径不含 time.sleep 轮询、不读 DB;等待靠 Queue.get(block=True) 条件变量。
  • 环境变量:PBL_BROADCAST_DISABLED(全局关闭)、PBL_BROADCAST_WEBHOOK_URLS(逗号分隔 webhook)、PBL_BROADCAST_REDIS_ENABLED + PBL_BROADCAST_REDIS_URL(Redis pub/sub)。

陷阱

  • 库名不硬编码:.py 用 ServerEnv().get_module_dbname('world_sync'),.dspy 用 get_sor_context(request._run_ns,'world_sync')
  • dspy 无 import
  • create/execute 必须设置 created_at、sync_date,否则 sor.C 静默丢记录
  • update 剔除 _text 后缀
  • codes.table 引用 world 不用 module.table 点号
  • 广播 hook 幂等:install_broadcast_hook() 重复调用不得重复包装(已用 __wrapped_post_commit__ 标记),否则一条事件广播多次
  • 广播内禁止再写事务/再发广播:dispatcher 线程只做 publish,回写 DB 会造成递归与延迟失真
  • 测试断言前用 drain_broadcast() 等待,禁止 sleep 轮询(QC 会按「引入轮询」退回)
  • RedisPubSubChannel 惰性 import redis:环境缺包只 ChannelUnavailable 跳过,不影响 InProcess/webhook 通道,不要让 Redis 成为硬依赖
  • scripts/sync_code_to_app.py 的 collect() 必须以 WS_ROOT 拼绝对路径 —— 用相对路径 isdir 会恒返回空清单,导致 --check 打印假 PASS(M11b-3 已修,改脚本时勿回退)

依赖

  • world、appbase、rbac
  • M11b-2/M11b-3 广播零第三方硬依赖(stdlib:threading/queue/urllib/hashlib;redis 可选)