已关闭
[MVP-03] Implement the YDB schema, migrations and transactional state store #5
urandon创建于  7月28日关闭于  7月29日
urandon
urandon成员
7月28日 创建

Parent and architecture

  • Implementation epic: #6
  • Architecture: #1
  • Depends on: #4

Outcome

Provide the authoritative YDB persistence layer for Telegram ingestion, runs, leases, quota accounting, context manifests, audit and outboxes.

Estimate

  • 8 SP / 5 engineering days
  • Risk: high

Scope

  • Design row-oriented YDB tables and primary/partition keys for tenants, users, chats, context epochs, Telegram updates, runs, attempts, checkpoints, subscription connections, quota reservations/observations, usage events, manifests, dispatch outbox, delivery outbox and audit events.
  • Add TTL policies for bounded idempotency, expired attempts/leases and retention-controlled audit data.
  • Implement Goose YDB migrations and migration status checks.
  • Implement atomic operations for:
    • Telegram update deduplication + run + dispatch outbox;
    • conditional run/lease claim and renewal;
    • quota reservation/commit/release;
    • checkpoint updates;
    • result + artifact manifest + delivery outbox;
    • outbox publication acknowledgement;
    • lease expiry/recovery.
  • Add retry policy only for YDB retryable errors and preserve idempotency across retries.
  • Add RU-efficient access patterns and prevent full-table scans on hot paths.

Design requirements

  • No Terraform-managed application tables; Terraform creates only the database/infrastructure.
  • Avoid globally monotonically increasing hot keys.
  • Every tenant-scoped operation must include the resolved internal tenant identifier.
  • Store credential references only, never auth-cache plaintext.
  • Migrations follow expand/migrate/contract; destructive production down migrations are not automatic.

Verification

  • Run migrations against a clean YDB Local instance and repeatedly against an already-migrated instance.
  • Concurrent lease-claim test proves exactly one winner.
  • Duplicate Telegram update test proves one run/outbox row.
  • Inject transaction retryable errors and prove no duplicate usage/outbox records.
  • Two-tenant tests prove no key/prefix mix-up.

Acceptance criteria

  • The domain state-store contract from #4 has a working YDB implementation.
  • All hot operations are point/key-prefix operations with documented keys.
  • Migration and rollback policy is documented and executable locally.
  • The schema exposes enough metadata for reconciler, admin and quota views without scanning arbitrary tenant payloads.
likedislike
urandonurandon成员
7月28日 修改了issue 的描述
urandonurandon成员
7月28日 关联了pull request:MVP-01: bootstrap Go repository foundation
urandon
urandon成员
7月29日 评论:

Migration-tool decision: Goose vs ydb-platform/golang-migrate

Short conclusion: for the MVP, keep upstream Goose, but use it through our Go migration binary rather than as an uncontrolled CLI invocation in multiple replicas. ydb-platform/golang-migrate should remain a fallback/spike option, but should not become the primary production dependency now.

Correction to the previous research

The statement “Goose has no YDB driver” was incorrect. YDB officially documents Goose support, and upstream Goose explicitly includes the ydb driver:

Comparison

Criterion Upstream Goose ydb-platform/golang-migrate
YDB support Officially documented by YDB; uses the YDB database/sql driver and scripting mode Its own YDB driver inside the fork
Release/pinning Versioned upstream releases The fork has no published releases; we would have to pin a commit
Dependency shape Regular Go dependency / ready-made CLI The fork retains the module path github.com/golang-migrate/migrate/v4, so it would require replacing the entire upstream module or maintaining our own fork build
Migration history Stores the history of applied versions Stores one current version plus a dirty record
Concurrency No YDB migration lock by default; requires our own SessionLocker/single-flight deployment Includes schema_lock for YDB and a serializable lock transaction
Failure model YDB documentation explicitly warns that a fake transaction in scripting mode may leave a migration step in an intermediate state The dirty flag provides better visibility into an interrupted migration, but DDL still does not become transactional
Authentication We can open the YDB SDK connection inside our Go binary using environment/metadata credentials The DSN path supports anonymous/static/access-token credentials; metadata/service-account authentication is convenient only through WithInstance and our wrapper
Maintenance Active upstream YDB fork: head dated 2026-01-13, while upstream has already moved forward; we would have to maintain the YDB-specific code together with the entire fork

Sources for the fork: YDB driver, module declaration, releases — none published.

Important limitation of YDB itself

The tool choice does not change a fundamental YDB property: schema transactions are not supported. DDL executes outside a transaction; in multi-statement DDL, earlier statements are not rolled back when a later statement fails. This is documented in YDB query execution and YDB transactions.

Therefore, safety must come from our protocol, not from the name of the migration tool.

Decision for #5

  1. Add cmd/schema-migrate in Go with embedded migrations/ydb/*.sql and the upstream Goose library.
  2. Open the YDB connection through SDK/database/sql with environment/metadata credentials; do not pass an IAM token through argv/DSN.
  3. Add a YDB-backed single-flight lock through Goose SessionLocker or a separate lease table. Two migration runners must produce exactly one winner.
  4. Use one idempotent schema operation per recoverable migration step; large data backfills must be separate resumable jobs.
  5. Use expand → migrate/backfill → contract; prohibit destructive down/reset operations in production.
  6. Prohibit changes to already-applied files. Add checksum/drift validation because the standard version history is insufficient to detect rewritten SQL.
  7. Test crash recovery: failure after DDL but before recording the version; repeated up; manual repair/runbook.
  8. Run the same suite against pinned YDB Local and cloud-dev serverless YDB.

When to revisit ydb-platform/golang-migrate

Run a short comparative spike within #5 if Goose fails the concurrent-runner/crash-recovery acceptance criteria. Compare both tools using the same fixtures:

  • two parallel migrators;
  • crash between DDL and version update;
  • restart after partial DDL;
  • metadata authentication in a Serverless Container;
  • drift in modified applied SQL.

For now, the fork wins on its built-in lock/dirty state, but loses on release discipline, full-history/auditability, and the ownership cost of an entire fork. For the MVP, it is not a cost-effective default.

likedislike
urandonurandon成员
7月29日 关联了pull request:MVP-02: define harness-neutral domain and runtime contracts
urandonurandon成员
7月29日 关联了pull request:[MVP-03] Implement the YDB schema and transactional state store
urandon
urandon成员
7月29日 评论:

Implementation update — YDB state store

Implementation is published in MR !4, branch ai/mvp-03-ydb-state-store, commit 992478d.

Implemented

  • 22 ordered YDB row-table migrations with tenant-first primary keys, explicit TTL columns, and dedicated time-keyed ready/expiry tables for reconciler access without JSON or full-table scans.
  • A repository-owned schema-migrate binary using embedded Goose migrations, a fenced YDB lease, pre-execution SHA-256 checksums, migration status output, and forward-only crash recovery.
  • The MVP-02 StateStore port over YDB serializable transactions with official SDK retry classification and tenant mismatch rejection.
  • Atomic Telegram update deduplication plus run, initial attempt, and dispatch outbox creation.
  • Exactly-one fenced lease claim, renewal, stale-fence rejection for checkpoints/results, and tenant/time lease recovery reads.
  • Atomic quota transitions, usage observations, artifact manifests, Telegram delivery outbox state, and dispatch acknowledgement.
  • Official environment/metadata authentication; credentials remain outside DSNs, command lines, images, and repository files.
  • A pinned YDB Local Compose service and a mandatory GitHub Actions job that applies the schema twice and runs concurrency/isolation tests.
  • Public documentation for key/access patterns, TTL behavior, expand/migrate/contract deployment, and migration incident repair.
  • The remaining worker-codex skeleton name was changed to harness-neutral worker-runtime.

Verified locally

  • make ci — formatting, go vet, race tests, five binaries, and foundation integration tests passed.
  • git diff --check passed.
  • Shell and YAML syntax validation passed.
  • The ydbintegration package compiles with its build tag.

Docker is not installed on the development workstation, so no local YDB execution is claimed. Issue #5 remains open until the GitCode branch reaches the GitHub mirror and the new YDB integration job proves repeatable migrations, duplicate-ingress rollback, exactly one concurrent lease winner, tenant isolation, and runtime image builds.

likedislike
urandonurandon成员
7月29日 关闭了 issue
urandonurandon成员
7月29日 重新打开了 issue
urandonurandon成员
7月29日 关联了pull request:[MVP-03] Prepare isolated Go temp directory before YDB migrations
urandon
urandon成员
7月29日 评论:

Post-merge CI follow-up

The first post-merge mirror run reached the new YDB Local job but failed before opening a YDB connection: GitHub Actions run 30444678066, job YDB schema and state store.

Root cause: the Makefile exports GOTMPDIR=.build/tmp, but make migrate-local did not depend on prepare in the fresh isolated job. Go therefore failed with stat .../.build/tmp: no such file or directory; this is a CI setup defect, not a schema or YDB failure.

The one-line fix is published in MR !5, commit 3a4a886: migrate-local: prepare.

Local make ci, make -n migrate-local, and git diff --check pass. Issue #5 remains open until !5 is merged, mirrored, and the YDB job reaches and passes the real migration/concurrency suite.

likedislike
urandon
urandon成员
7月29日 评论:

Completion report — MVP-03

MVP-03 is complete and verified on the merged source-of-truth branch.

Delivered

  • The authoritative YDB schema for tenants, frontend identities and conversations, context epochs, runs and attempts, leases, subscription connections, quota reservations and observations, usage events, artifact manifests, dispatch and delivery outboxes, and audit events.
  • Twenty-two ordered, forward-only YDB migrations with tenant-first primary keys, explicit TTL columns, and dedicated ready/expiry access paths.
  • A repository-owned schema-migrate binary using embedded upstream Goose migrations, a fenced YDB migration lease, pre-execution SHA-256 drift checks, status output, and documented crash-recovery procedures.
  • A transactional YDB implementation of the harness-neutral StateStore contract, including atomic ingress deduplication, run/outbox creation, fenced lease operations, checkpoint/result commits, quota transitions, usage observations, artifact manifests, delivery state, and dispatch acknowledgements.
  • Official environment/metadata authentication with no credentials in DSNs, command lines, images, or repository files.
  • Pinned YDB Local integration infrastructure and mandatory CI coverage for repeatable migrations, concurrent lease claims, duplicate ingress, retry idempotency, tenant isolation, and runtime image builds.
  • Harness-neutral runtime naming: the former worker-codex skeleton is now worker-runtime.

Merge and verification evidence

The YDB job applied the schema repeatedly and completed the integration suite after the merged migrate-local: prepare fix. This closes the only post-merge CI failure observed after !4.

All acceptance criteria in this issue are now satisfied.

likedislike
urandonurandon成员
7月29日 关闭了 issue
urandonurandon成员
16 天前 添加了label:mvpydb
urandonurandon成员
16 天前 关联了里程碑:MVP — Core platform (#6)