The async-only direction was already the only one used in production: * create_storage never accepted mode=sync; build_storage_config always emitted mode=async; zero callers referenced StorageBackend / SyncData / S3StorageBackend.sync / LocalStorageBackend.sync anywhere. * Drop the parallel sync base class, the sync concrete classes in backends/local.py and backends/s3.py, and the boto3 dependency. * Drop example_usage.py (zero importers; demonstration code, not part of the public surface). * Rename LocalAsyncStorageBackend -> LocalStorageBackend, S3AsyncStorageBackend -> S3StorageBackend to reflect the single remaining class per type. * Tighten create_storage: any mode=... key now raises StorageConfigError with the new pointer (settings.storage_backend controls behavior). * Cleanup call sites: schedule.application.service.build_object_store no longer passes mode=async to create_storage. * Cosmetic touch-ups in backend/services/storage.py and common/config.py docstrings where they still said "boto3" instead of "S3 client". Public API surface preserved: AsyncStorageBackend / ObjectMeta / create_storage / build_storage_config / register_backend all keep their names and call signatures. backend tests: 136 passed.
Schedule Executor
独立调度执行服务,内置 APScheduler。
- Cron 任务持久化到 MySQL 的
apscheduler_jobs表; - FastAPI Backend 创建运行记录和 Outbox 事件后,通过 HTTP 尝试立即推送;
- HTTP 推送失败时,Executor 继续轮询 MySQL
outbox_events,保证任务不会丢失; - Executor 负责 DAG 节点派发、稳定版本执行、重试、状态推进和结果回写;
- 不依赖 Redis,MySQL 是调度状态与幂等状态的唯一权威。