多 agent 并行批次的协调提交(消除全部共享文件冲突面): - 迁移 0004:id_session + ob_event 两表(T3/T4 用,任务不再写迁移) - ORM:UserSession / ObEvent;错误码:九个新码(40xxx/60006,含 meta) - 配置:四档 tiers / refresh TTL / 任务奖励注册表 / 埋点上限 / COS 直传全套 - 骨架:packages/analytics + packages/storage + events/uploads 空路由(已注册) - deps:AnalyticsServiceDep / StorageServiceDep;DailyBudgetGuard 接收 tiers - uv add cos-python-sdk-v5;import-linter/CI/Makefile 纳入两个新包
64 lines
2.6 KiB
Python
64 lines
2.6 KiB
Python
"""Sessions and client events for the Sprint-0 parallel batch.
|
|
|
|
Two tables that the parallel task batch (T3/T4) needs, landed together in one
|
|
revision so no task ever writes a migration:
|
|
|
|
1. ``id_session`` — server-revocable refresh sessions (架构文档 §6.2, FR-ACC-05).
|
|
2. ``ob_event`` — client telemetry (架构文档 §6.2, FR-SYS-06).
|
|
|
|
Partitioning (架构文档 says "按月分区") is deliberately deferred for both —
|
|
same call as ``ob_ai_call``: plain tables until volume justifies it.
|
|
|
|
Reversible: 是。降级直接 drop 两表。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Sequence
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
from sqlalchemy.dialects import postgresql
|
|
|
|
revision: str = "0004"
|
|
down_revision: str | None = "0003"
|
|
branch_labels: str | Sequence[str] | None = None
|
|
depends_on: str | Sequence[str] | None = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.create_table(
|
|
"id_session",
|
|
sa.Column("session_id", sa.BigInteger(), nullable=False),
|
|
sa.Column("user_id", sa.BigInteger(), nullable=False),
|
|
sa.Column("refresh_token_hash", sa.String(length=64), nullable=False),
|
|
sa.Column("expire_at", sa.DateTime(timezone=True), nullable=False),
|
|
sa.Column("revoked", sa.Boolean(), nullable=False, server_default=sa.text("false")),
|
|
sa.Column("revoked_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
|
|
sa.PrimaryKeyConstraint("session_id"),
|
|
sa.UniqueConstraint("refresh_token_hash", name="uq_id_session_token_hash"),
|
|
)
|
|
op.create_index("ix_id_session_user", "id_session", ["user_id"])
|
|
|
|
op.create_table(
|
|
"ob_event",
|
|
sa.Column("event_id", sa.BigInteger(), nullable=False),
|
|
sa.Column("user_id", sa.BigInteger(), nullable=False),
|
|
sa.Column("event_name", sa.String(length=64), nullable=False),
|
|
sa.Column("props", postgresql.JSONB(), nullable=False),
|
|
sa.Column("client_ts", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("server_ts", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
|
|
sa.PrimaryKeyConstraint("event_id"),
|
|
)
|
|
op.create_index("ix_ob_event_name_ts", "ob_event", ["event_name", "server_ts"])
|
|
op.create_index("ix_ob_event_user_ts", "ob_event", ["user_id", "server_ts"])
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_index("ix_ob_event_user_ts", table_name="ob_event")
|
|
op.drop_index("ix_ob_event_name_ts", table_name="ob_event")
|
|
op.drop_table("ob_event")
|
|
op.drop_index("ix_id_session_user", table_name="id_session")
|
|
op.drop_table("id_session")
|