8.4 KiB
8.4 KiB
Supabase 统一服务生命周期设计(优化版)
Date: 2026-03-06
Status: Draft
0. Intake Contract
- Objective: 将 Supabase 客户端纳入统一服务生命周期管理,避免每次请求重复创建客户端。
- Deliverable: 新增
SupabaseService,并基于service_interface.py的ServiceRegistry提供统一初始化/关闭路径,完成 auth 侧迁移。 - Constraints:
- 保持现有
core.config.settings的配置读取行为不变。 - 不引入
os.environ直接读取。 - 不改变现有 API 语义。
- 保持现有
- Verification target:
- 通过单元测试证明 Supabase 服务初始化、关闭、健康检查行为。
- 通过应用启动测试证明统一初始化流程可用。
- 通过 auth 相关测试证明迁移后业务行为一致。
1. 复杂度与风险分级
- Complexity:
S2- 原因:涉及多文件改造(
services/base、app.py、v1/auth、测试)。
- 原因:涉及多文件改造(
- Risk Tier:
L1- 原因:涉及应用启动链路和认证网关依赖,但不改变对外接口契约。
L1 Gate 要求:执行 refactor-cleaner 审视冗余与结构风险(code-reviewer 可选)。
2. 现状与问题
2.1 当前现状
SupabaseAuthGateway在__init__内直接create_client(...),每次实例化都会创建 anon/admin 客户端。get_auth_service()当前每次请求都会 newSupabaseAuthGateway(),导致客户端重复构造。ServiceRegistry已存在,但目前主要用于注册,应用启动仍是手写逐个初始化。
2.2 核心问题
- 生命周期不统一:Supabase 没有接入应用启动/关闭的统一管理。
- 初始化代码重复趋势:服务增多后,
app.py的 lifespan 会继续膨胀。 - 网关构造时机风险:若在应用未初始化阶段取客户端,可能抛运行时异常。
3. 优化设计(推荐方案)
3.1 方案摘要
在 service_interface.py 基础上新增统一生命周期函数,按服务名列表批量初始化/关闭;app.py 仅声明服务顺序,减少样板代码。Supabase 使用 config.supabase 作为默认配置来源,保持 settings 行为一致。
3.2 目标文件结构
backend/src/services/base/
├── __init__.py
├── service_interface.py # 扩展:统一生命周期函数
├── redis.py
└── supabase.py # 新增
3.3 service_interface 统一初始化能力(新增)
在 service_interface.py 新增以下函数(建议命名):
resolve_registered_services(service_names: list[str]) -> list[BaseServiceProvider]initialize_registered_services(service_names: list[str]) -> tuple[bool, list[BaseServiceProvider]]close_registered_services(services: list[BaseServiceProvider]) -> bool
约束与行为:
- 初始化按
service_names顺序执行。 - 任一服务初始化失败时:
- 返回
False。 - 对已成功初始化的服务按逆序执行关闭回滚。
- 返回
- 关闭按逆序执行,最大化依赖安全性。
- 日志必须包含失败服务名和错误摘要。
这样 app.py 只需声明:
SERVICE_STARTUP_ORDER = ["redis", "supabase"]
并调用统一函数,减少重复初始化样板。
3.4 SupabaseService 设计
supabase.py 关键点:
- 继承
BaseServiceProvider。 - 构造函数签名:
def __init__(self, settings: SupabaseSettings | None = None) -> None- 默认
settings or config.supabase,确保与当前配置源一致。
initialize():创建 anon/admin 两个 client,失败返回False。close():- 清空
_client、_admin_client。 self._set_initialized(False)。
- 清空
health_check():- 必须进行至少一个轻量真实请求验证,不仅检查本地对象存在。
- 返回结构与
RedisService.health_check()风格一致(status + details)。
注册方式:
supabase_service: SupabaseService = register_service_instance(
"supabase", SupabaseService()
)
3.5 app.py 改造
当前手写 redis_service.initialize() 改为调用统一初始化函数。
目标行为:
- 启动阶段:
- 调用
initialize_registered_services(["redis", "supabase"])。 - 失败则
raise RuntimeError("Service initialization failed")。
- 调用
- 关闭阶段:
- 调用
close_registered_services(initialized_services)。
- 调用
3.6 AuthGateway 迁移策略(避免构造时机问题)
不建议在 SupabaseAuthGateway.__init__ 里立即绑定 client;改为按需获取:
- 保留网关对象轻量化。
- 在每个业务方法内部通过
supabase_service.get_client()/get_admin_client()取实例。
优点:
- 避免模块导入或依赖构建阶段误触未初始化 client。
- 对
users/dependencies.py中全局缓存 gateway 的场景更安全。 - 不改变业务层接口。
4. 配置与兼容性保证
4.1 settings/config 行为不变
迁移后依然通过 core.config.settings.config.supabase 读取:
urlanon_keyservice_role_keyjwt_secret(JWT 校验现有逻辑继续使用)
4.2 环境变量兼容
由于 Settings + env_nested_delimiter 机制不变,现有环境变量命名与 .env 内容无需修改。
4.3 对现有代码影响
- API 层 schema/路由不变。
- 认证行为不变。
- 仅优化客户端生命周期与启动流程。
5. 实施计划(可执行)
Task 1: 扩展统一生命周期接口
Files
- Modify:
backend/src/services/base/service_interface.py - Test:
backend/tests/unit/services/base/test_service_interface.py(新增)
Steps
- 写失败测试:初始化顺序、失败回滚、关闭逆序。
- 实现生命周期函数。
- 跑单测确认通过。
Task 2: 新增 SupabaseService
Files
- Create:
backend/src/services/base/supabase.py - Modify:
backend/src/services/base/__init__.py - Test:
backend/tests/unit/services/base/test_supabase.py
Steps
- 写失败测试(init success/fail、close、health_check)。
- 实现
SupabaseService与实例注册。 - 跑单测。
Task 3: 接入 app lifespan 统一初始化
Files
- Modify:
backend/src/app.py - Test:
backend/tests/integration/test_app_lifespan.py(新增或扩展)
Steps
- 写失败测试(supabase init fail 时应用启动失败)。
- 替换手写初始化为统一函数。
- 跑集成测试。
Task 4: 迁移 AuthGateway 获取 client 方式
Files
- Modify:
backend/src/v1/auth/gateway.py - Optional Modify:
backend/src/v1/auth/dependencies.py - Optional Modify:
backend/src/v1/users/dependencies.py - Test:
backend/tests/unit/v1/auth/test_gateway.py(扩展)
Steps
- 写失败测试(未初始化时错误、初始化后正常调用)。
- 改为方法内按需取 client。
- 跑 auth 相关单测。
Task 5: 全量验证与门禁
Commands
uv run ruff check backend/src backend/testsuv run basedpyrightuv run pytest backend/tests/unit/services/base -quv run pytest backend/tests/unit/v1/auth -quv run pytest backend/tests/integration -q
输出要求:记录每条命令 pass/fail 与关键摘要。
6. 验收标准(更新)
SupabaseService继承BaseServiceProvider并注册到ServiceRegistryservice_interface.py提供统一初始化/关闭函数app.py通过统一函数初始化redis + supabase- Supabase 配置读取仍仅来自
core.config.settings.config auth/gateway.py不再在__init__新建客户端- 初始化失败具备回滚关闭逻辑
- 单元/集成测试覆盖核心迁移路径并通过
7. 风险与缓解
| 风险 | 级别 | 缓解 |
|---|---|---|
| 统一初始化函数引入顺序错误 | 中 | 显式 SERVICE_STARTUP_ORDER + 顺序测试 |
| Supabase 健康检查误报 | 中 | 使用真实轻量请求,不只做对象检查 |
| gateway 与生命周期耦合导致运行时错误 | 中 | 改为方法内按需取 client,并覆盖未初始化测试 |
| 迁移影响现有 auth 行为 | 中 | 保持 service 接口不变,补充回归测试 |
8. 完成定义(Completion Contract)
- Complexity:
S2 - Risk Tier:
L1 - Gates:
- 必需:
refactor-cleaner - 可选:
code-reviewer(建议在合并前执行)
- 必需:
- Verification evidence:
- 提供 lint/typecheck/unit/integration 命令结果
- Remaining risks/follow-ups:
- 若后续新增第三方服务,沿用
ServiceRegistry + 统一生命周期函数接入,不再在app.py手写初始化。
- 若后续新增第三方服务,沿用