Skip to content

feat(storage): provision dedicated seekdb and oceanbase databases - #2031

Open
Lkx-JY wants to merge 15 commits into
oceanbase:masterfrom
Lkx-JY:feat/1795-default-database
Open

Lkx-JY wants to merge 15 commits into
oceanbase:masterfrom
Lkx-JY:feat/1795-default-database

Conversation

@Lkx-JY

@Lkx-JY Lkx-JY commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Which issue or RFC does this PR close?

Closes #1795.

Rationale for this change

Embedded seekDB previously accepted only the built-in test database, while OceanBase rejected URLs without a database and assumed the selected database already existed. Provision the selected database before application tables, preserve explicit selections, and require an explicit choice before switching away from existing seekDB data.

What changes are included in this PR?

  • Default seekDB and database-less OceanBase URLs to powercontext; accept custom database names and preserve explicit selections.
  • Connect to the target first, create it only after a confirmed unknown-database error, verify the selected database, and reuse existing databases without issuing database-creation statements.
  • Quote database identifiers, preserve connection settings, handle concurrent creation, and provide administrator provisioning instructions for permission failures without selecting a fallback database.
  • Use idempotent MySQL-compatible table DDL and verify duplicate-index races while preserving existing-schema checks and explicit migration boundaries.
  • Stop implicit seekDB database switches when test contains PowerContext-named tables. Explain how to keep using test or explicitly select a new database without moving or deleting existing data.
  • Pass the selected database to code graph connections, distinguish graph identities across databases, and preserve legacy test graph identities.
  • Add OceanBaseProfile.open_existing() and SeekDBProfile.open_existing() for connections that never provision a missing database. Route server atomic-memory-migrate and server processing-migrate actions plan and verify through these openers with tables=(). Report a missing database clearly and distinguish it from denied access.
  • Validate seekDB instance storage before loading or starting the native engine during read-only inspection. Reject missing paths, empty directories, files, and unrecognized nonempty directories without creating parents or modifying existing contents. Reuse the same native-storage markers as db-migrate and report an actionable CLI error.
  • Preserve provisioning for ordinary startup and writable maintenance. Keep db-migrate inspection free of database creation and allow explicit writable seekDB maintenance to provision its configured database.
  • Select the OceanBase database only through the URL path; reject db and database query parameters. Update benchmark resume tests to preserve effective-target identity checks and verify configuration errors do not expose sensitive values.
  • Add a database-name prompt to the configuration wizard and update English/Chinese installation and configuration guides, environment examples, behavior tests, and real-backend acceptance scenarios.
  • Reject backtick-containing names before writes and run real seekDB/OceanBase bootstrap acceptance in CI with skip rejection.
  • Verify seekDB backup/restore against default, explicit legacy, and custom database names.
  • Keep OceanBase test resource management local to the bootstrap acceptance module: check the endpoint before provisioning, protect pre-existing databases, retain cleanup diagnostics alongside failures, and drain concurrent initialization tasks before teardown.

Are there any user-facing changes?

Yes. New seekDB deployments use powercontext. Existing seekDB deployments should explicitly set POWERCONTEXT_SERVER_DATABASE_DATABASE=test before upgrading to keep using their data. Without an explicit name, detecting pc_ tables in test stops startup and provides configuration instructions.

OceanBase URLs may omit the database. Explicit database names in the URL path remain authoritative; db and database query parameters are rejected. OceanBaseProfile.attach() continues to require an explicit existing database in the caller-owned engine. Database names are validated before initialization.

For OceanBase and seekDB, migration plan and verify require an existing database and never create a missing target. A missing target produces an actionable error. Ordinary startup and writable maintenance retain their provisioning behavior.

Read-only seekDB inspection also requires an initialized native instance directory. Missing paths and empty directories are rejected before native startup, without creating parent directories or storage files. Files and unrecognized nonempty directories are rejected with their contents preserved.

No automatic data migration, retention reminders, database comparison, deletion, or existing-table schema upgrades are introduced. Embedded seekDB concurrency acceptance covers clients in one engine-owner process; it does not promise multiple owner processes. Initialization DDL does not provide distributed schema-migration coordination.

How was this change tested?

Executed with the existing project virtual environment:

  • Bootstrap/profiles, maintenance/backup/CLI, configuration wizard, and Server: 227 passed; four opt-in seekDB probes skipped in that run were also exercised separately.
  • MySQL schema, migration schema/runner, and legacy startup protection: 37 passed, 25 skipped (opt-in probes).
  • Real embedded seekDB bootstrap acceptance: 7 passed, 5 OceanBase cases deselected, including existing-database inspection and confirmation that inspection leaves a missing target database absent.
  • Real embedded seekDB code graph acceptance: 30 passed.
  • Real seekDB maintenance/backup probes: 6 passed, including backup, DDL, restart, and restoration with default powercontext, explicit test, and a custom database.
  • Backup/maintenance provider-free regression: 25 passed, 1 skipped (OceanBase not configured); these passes overlap the first group.
  • Benchmark resume and ambiguous-routing regression cases: 14 passed.
  • Focused read-only migration CLI regressions: 24 passed. These cover both commands and actions, missing-database routing for both backends, and preservation of missing, empty, file, and unrecognized seekDB paths. The 16 directory-protection cases fail without the guard and pass with it; a native-binding guard verifies that invalid targets are rejected before engine startup.
  • The complete affected migration CLI, bootstrap, OceanBase/seekDB profile, and maintenance-connection test files: 130 passed, 5 skipped. This run emitted one aiosqlite thread-cleanup warning (Event loop is closed).
  • Ruff and formatting checks on the original 20 changed Python files, type checks on affected runtime files, the bootstrap acceptance module and backup provider tests, and git diff --check passed. Ruff, formatting, type checks, and diff checks also passed for the directory-protection changes.

Test counts from separate runs overlap and are not additive. The complete affected test files and real seekDB bootstrap scenarios ran outside the execution sandbox. Real OceanBase acceptance, the full repository test suite, and documentation builds were not executed.

Coverage includes configuration, identifier quoting, target identity, permission/error handling, legacy database selection, resource disposal, maintenance identity, wizard persistence, code graph database selection, non-provisioning read-only migration inspection, and preservation of uninitialized instance paths. Opt-in real-backend scenarios cover initial creation, custom names, restart/readback, concurrent profile initialization, legacy data retention, OceanBase default selection, restricted-account behavior, and existing/missing database inspection.

Commands to run:

uv sync --locked --extra cli --extra server --extra seekdb --extra code

uv run python -m pytest \
  tests/builtin/persistence/test_database_bootstrap.py \
  tests/builtin/persistence/test_seekdb_profile.py \
  tests/builtin/persistence/test_oceanbase_profile.py \
  tests/builtin/persistence/test_database_migration_connections.py \
  tests/builtin/persistence/test_database_backup_providers.py \
  tests/test_database_migration_cli.py \
  tests/test_atomic_memory_migration_cli.py \
  tests/test_processing_migration_cli.py \
  tests/test_server.py \
  tests/test_config_wizard.py

uv run python -m pytest -q \
  tests/test_locomo_plus_runner.py::test_oceanbase_resume_preserves_effective_target_when_credentials_or_spelling_change \
  tests/test_locomo_plus_runner.py::test_oceanbase_resume_rejects_effective_target_changes_before_opening_services \
  tests/test_locomo_plus_runner.py::test_oceanbase_rejects_ambiguous_routing_before_opening_services

POWERCONTEXT_TEST_BOOTSTRAP_SEEKDB=1 \
  uv run python -m pytest tests/e2e/test_database_bootstrap.py -k seekdb

uv run python -m pytest tests/e2e/test_native_code_seekdb.py

POWERCONTEXT_TEST_MIGRATION_SEEKDB=1 \
  uv run python -m pytest \
    tests/builtin/persistence/test_database_migration_connections.py \
    tests/builtin/persistence/test_database_backup_providers.py -k real_seekdb

With POWERCONTEXT_TEST_BOOTSTRAP_OCEANBASE_URL set to an administrator connection to a disposable MySQL user tenant:

uv run python -m pytest tests/e2e/test_database_bootstrap.py -k oceanbase

POWERCONTEXT_TEST_BOOTSTRAP_OCEANBASE_DEFAULT=1 \
  uv run python -m pytest tests/e2e/test_database_bootstrap.py -k omitted_database

POWERCONTEXT_TEST_BOOTSTRAP_PRIVILEGES=1 \
  uv run python -m pytest tests/e2e/test_database_bootstrap.py -k without_create_privilege

The default-name test requires an exclusive tenant without an existing powercontext database. The privileges test requires disposable user creation/grant privileges. These tests create and drop their own databases/users.

make check
make test PYTEST_WORKERS=0
make docs-test

AI usage statement

Implemented and reviewed with OpenAI Codex. AI assistance was used for source tracing, implementation, regression-test design, documentation, and validation. Validation limits are listed above.

@hidb4ai

hidb4ai commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

CI failed

The resumable-target fingerprint accepted db/database query parameters as
authority overrides, which the OceanBase config validator now rejects. Take
the database from the URL path, refuse the overrides at this boundary too, and
cover the policy directly so the runner no longer documents a capability it
cannot reach.
@Lkx-JY
Lkx-JY requested a review from Alanxtl as a code owner October 16, 2026 15:40
async with _initialized_profile(cls(database=database, tables=tables)) as profile:
yield profile
try:
await ensure_database(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Prevent read-only migration commands from provisioning databases

The plan and verify actions of server atomic-memory-migrate and server processing-migrate still call this opener with tables=(), so an absent target now triggers CREATE DATABASE during read-only inspection; the seekDB opener has the same regression. Provide a non-provisioning connection path for these actions and reserve database creation for startup or writable maintenance.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

补上调用方证据,确认只读命令确实走到新建库这条路径(同根因,不另开线程)。

  1. src/powercontext/server/cli.py 的原子记忆迁移命令里 read_only = action in {"plan", "verify"} 只对 SQLite 生效:SQLite 分支用 SQLiteProfile.open_readonly(...),而 OceanBaseConfig 与 SeekDBConfig 分支在 read_only 为真时仍调用 OceanBaseProfile.open(database, tables=()) / SeekDBProfile.open(database, tables=())。本 PR 让这两个 open() 无条件执行 ensure_database(...),因此 plan / verify 会先发 CREATE DATABASE IF NOT EXISTS ...,随后在写事务里跑 plan_processing_migration。
  2. 同文件里处理迁移的另一入口(action == "plan" → plan_processing_migration)同样通过 OceanBaseProfile.open / SeekDBProfile.open,路径一致。
  3. SeekDBProfile.open 在 ensure_database 之前还会执行 _require_explicit_legacy_database:数据库名未显式设置时它连接 legacy test 库查 information_schema.TABLES。只读校验命令因此多了一次与实际检查目标无关的库连接。
  4. 这不是「设计如此」:src/powercontext/builtin/persistence/migrations/connections.py 里就区分了 writable,writable=False 时对 seekdb 走 elif not await database_is_available(engine): return operation(None, identity, lambda: None),而 _oceanbase 完全不调用 ensure_database。同一语义在两个入口实现不一致。

建议:OceanBaseProfile.open / SeekDBProfile.open 增加 provision: bool = True(或等价的只读开关),read_only 分支传 False,此时目标库不存在直接按 database_is_available(...) 的结果报错而不是创建;并在两条 CLI 上各补一条「目标库缺失时 plan / verify 不创建库、不产生 DDL」的回归断言。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复审说明:本条上一条回复基于 5e760476,在该条发布后不到一分钟 head 已前移到 b719edfe(fix(storage): prevent read-only migration commands from creating databases),上一条指出的路径在新 head 上已被覆盖,结论更新如下。

已核对 b719edfe:

  1. OceanBaseProfile._connect(provision=...) 与 SeekDBProfile._connect(provision=...) 拆出 open_existing(...);provision=False 时走新增的 require_existing_database(...),缺库抛 MissingDatabaseError,不再发 CREATE DATABASE。
  2. _processing_maintenance 与 _atomic_memory_maintenance 都按 read_only = action in {"plan", "verify"} 选择 open_existing,两个入口一致;两个 CLI 命令都把 MissingDatabaseError 转成可读的 typer.BadParameter,且 apply 不受影响。
  3. SeekDBGraphStore / open_code_service 未受影响,仍用 open()(provision 默认开),符合运行时需要。

残留一处,请确认是否需要一并处理:SeekDBProfile._connect 在 provision=False 时仍然先执行 path.parent.mkdir(parents=True, exist_ok=True),再 _open_instance(module, path),之后才 require_existing_database(...)。也就是说,对不存在的 seekdb 路径执行 plan / verify,DB 层已经不再建库,但本地可能仍会留下父目录与一个空实例目录。本机没有 Linux + pylibseekdb,无法实测 aopen() 在路径缺失时是否落盘创建,因此这里只作为待确认项:若 aopen() 会创建,建议在只读分支先判 path.exists() 并用与 MigrationConnections 相同的 unsupported_target 语义拒绝,或在 MissingDatabaseError 前记录并清理本次新建的目录。

@AlexStocks AlexStocks added the 1.4.1 v1.4.1 release tracking label Oct 17, 2026

def creation_statement(engine: AsyncEngine, name: str) -> str:
"""Render the exact statement an administrator runs to provision this database."""
quoted = engine.dialect.identifier_preparer.quote_identifier(validate_database_name(name))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Render administrator SQL without driver percent escaping

When provisioning is denied for an accepted name such as team%ctx, the MySQL dialect's quote_identifier() produces team%%ctx, which is embedded verbatim in the administrator instructions. Executing that SQL in a SQL client creates the wrong database and leaves startup failing; render administrator-facing SQL separately from the statement escaped for DBAPI execution.

config.model_copy(update={"echo": False}), instance.connection_options()
)
try:
await seekdb_profile._require_explicit_legacy_database(config, instance.connection_options())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Translate legacy selection failures into migration errors

When seekDB has pc_ tables in test and no explicit database name, this probe raises LegacySeekDBDatabaseError, which db-migrate's _operator_errors() does not handle. The commands therefore produce an uncaught exception instead of the documented JSON error; translate this expected rejection into a MigrationError while preserving the explicit-selection guidance.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

补充该调用确由本 PR 新增,以及异常确实不被收敛的证据。

  • connections.py:144 的 _require_explicit_legacy_database(...) 由本 PR 加入(git diff master...2fef5fab 的 hunk @@ -136,6 +141,17 @@)。
  • LegacySeekDBDatabaseError 继承 PersistenceError(RuntimeError);server/database_migration.py::_operator_errors 只捕获 MigrationError / ServerConfigurationError / SQLAlchemyError / OSError / ServiceError,同文件另一处也只有 except (ServiceError, OSError) ⇒ 该异常以 traceback 冒出,不会产生文档里的 {"state":"blocked","error":...} JSON。
  • 现有测试只覆盖 Server/e2e 启动路径(tests/builtin/persistence/test_database_bootstrap.py、tests/e2e/test_database_bootstrap.py),db-migrate 这一支无覆盖。本机无真实 seekdb 实例,本结论为源码推导,未做真实实例复现。

建议在 _seekdb 内把这个预期拒绝转成 MigrationError("explicit_database_required", str(error))(保留原文里的 database 选择指引),并补一条 CLI 级断言 JSON 输出的用例。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复审 b0e379f6(fix(storage): preserve legacy seekdb migration errors):本条在 db-migrate 支已闭合,但同一异常在其余 CLI 入口仍有残留。

已闭合部分

  • migrations/connections.py 新增 _require_explicit_database(),把 LegacySeekDBDatabaseError 转成 MigrationError("explicit_database_required", str(error)),保留原指引文本;_operator_errors() 因此能产出文档里的 {"state":"blocked","error":...}。
  • P3 线程(4309776833)的 echo 问题同批修掉:探针引擎改用 config.model_copy(update={"echo": False})。
  • 镜像 head 树 12 条新用例全绿(test_database_migration_connections.py、test_seekdb_profile.py、test_database_migration_cli.py、test_atomic_memory_migration_cli.py、test_processing_migration_cli.py)。
  • 变异反证(probes/mutate_r14.py,一次性副本内):把 _require_explicit_database 换回直接调用后,test_seekdb_legacy_database_refusal_is_a_maintenance_error 恰好转红并复现原签名 LegacySeekDBDatabaseError;把 model_copy(echo=False) 换回 config 后 test_legacy_database_probe_never_echoes_sql 转红(assert True is False);删掉 atomic_memory_migrate 的 LegacySeekDBDatabaseError 分支后该用例 3/4 参数红、rebuild-projection 因自带 catch 仍绿。

残留:server identity-reset 未转换

探针 probes/probe_identity_reset_r14.py(stub SeekDBProfile.open/open_existing 直接抛 LegacySeekDBDatabaseError,不打开任何数据库):

atomic-memory-migrate --action verify --maintenance-confirmed → exit 2,输出含 POWERCONTEXT_SERVER_DATABASE_DATABASE 指引
identity-reset --maintenance-confirmed                        → exit 1,stdout 为空,异常原样逃出(终端表现为 traceback)

identity_reset() 里 asyncio.run(_reset_server_identity(settings)) 外层没有对应 catch,而 server/identity.py::open_server_identity_repository 走的是同一个 SeekDBProfile.open ⇒ 同一份拒绝消息在 identity 轮换路径上仍以未捕获异常出现。身份轮换本身未执行,无数据风险,影响限于操作者可见的错误形态不一致。

建议:在该命令外层加同样的 except LegacySeekDBDatabaseError(powercontext code 也经 SeekDBProfile.open,如需一致可一并处理)。定级 P2,不阻断本提交。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rechecked at 0cab7347 with a real embedded seekDB instance. The db-migrate JSON issue is fixed. One non-blocking P3 remains: with pc_ tables in test and no explicit database name, server identity-reset --maintenance-confirmed raises an uncaught LegacySeekDBDatabaseError with empty stdout, before rotating the identity (src/powercontext/server/cli.py:222). Please translate it to typer.BadParameter, preserving the database-selection guidance.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified fixed at d8da2bca with real embedded seekDB. Implicit legacy selection now returns exit 2 with the database-selection guidance and no traceback; readback confirmed the legacy record was preserved. The separate real CLI acceptance also passed explicit identity rotation and readback of the new identity with the original record intact.

config.model_copy(update={"echo": False}), instance.connection_options()
)
try:
await seekdb_profile._require_explicit_legacy_database(config, instance.connection_options())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] Disable SQL echo for the legacy maintenance probe

With POWERCONTEXT_SERVER_DATABASE_ECHO=true and an implicit seekDB database name, this call passes the original configuration to a second engine that enables SQL echo. Its logs precede the db-migrate JSON on stdout and break JSON consumers; pass the same echo-disabled configuration used for the maintenance engine.



@pytest.mark.skipif(not _SEEKDB, reason="set POWERCONTEXT_TEST_BOOTSTRAP_SEEKDB=1")
def test_seekdb_legacy_migration_cli_preserves_json_with_sql_echo(short_tmp_path: Path) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 该用例使 code-seekdb (3.14) 的 bootstrap 步骤超出 300 s 预算,并跳过后续迁移验收步骤

head b0e379f6 的 code-seekdb (3.14)(job 118152425141)在 Test database bootstrap with embedded seekdb without skips 步骤失败:15:44:27 开始,15:49:39 被步骤超时终止(312 s,步骤上限 300 s),紧随其后的 Test unified migrations with embedded seekdb 因此被 skipped,这一份迁移验收在本 head 上从未执行。

时间对照(同一 job 定义、同一 runner 规模):

引用 3.14 bootstrap 步骤 3.11 bootstrap 步骤
本 head b0e379f6 超时(>300 s),job failure 88 s,success
上一 head 2fef5fab(job 117677155323) 41 s,success success
master e4efb39b(job 118011655961) success success

2fef5fab...b0e379f6 只有 11 个文件,其中落在该步骤(-k seekdb 跑该文件)里的唯一变更就是本用例,因此可复现性判断集中在本用例;请重跑一次确认。

机制线索(源码推导,未在 Linux 实测):本文件既有的 test_seekdb_concurrent_profiles_initialize_one_database 注释写明并发只覆盖“one embedded owner process, not multiple engine-owner processes”,而本用例把同一 seekdb 目录交给 4 个独立子进程(for action in ("status", "plan", "verify", "apply") 各一次 subprocess.run(..., timeout=45))外加末尾一次 SeekDBProfile.open_existing。两处打开的等待没有上界:asyncio.run(seed()) 与 verify_preserved() 都没有 asyncio.timeout;一旦子进程被 45 s 上限 kill 掉而引擎目录/锁未释放,末尾的 open_existing 会无界等待,从而吃掉整个步骤预算。

建议方向:让四个 action 复用同一个引擎 owner(进程内直接调 CLI 入口,或一次子进程内跑完四个 action),只保留一次跨进程开库以覆盖“CLI 是独立进程”这一点;给 seed() 与 verify_preserved() 加 asyncio.timeout;如果单测时长无法压到预算内,把它拆到独立 step,避免挤掉同 job 的迁移验收步骤。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复审 0cab7347:该 P1 已解除。

bf34f3e5 把跨进程开库收敛为"一个子进程持有引擎":新增 tests/fixtures/seekdb_legacy_migration_cli.py,seed() 外层加 asyncio.timeout(30),测试侧只保留一次 subprocess.run(..., timeout=90) 并在 TimeoutExpired 时 pytest.fail 带 stdout/stderr;master.yml 的 bootstrap step 维持 timeout-minutes: 5,另加 -vv、faulthandler_timeout=60、--durations=20 便于定位。这正是原先建议的"跨进程开库次数显式且最小 + 所有 asyncio.run(<open>) 加超时"。

证据:head 0cab7347 的 check 为 41 success / 1 skipped、无 failure,code-seekdb (3.11) 与 code-seekdb (3.14) 均通过,bootstrap 步骤未再超时,后续迁移验收步骤不再被 skip。

与本条无关但影响合并的一点,记录在此线程以便定位:该 head 当前 mergeable=false / mergeable_state=dirty,与 master(fe50f894)在 e2e/bub/tests/test_harbor_job_config.py 存在内容冲突(git merge-tree 复现),需要先同步 master 再谈入队。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 新 head d8da2bca 把同类越界从"步骤预算"移到"用例自身预算",code-seekdb (3.11) 在 head 上红

上一条回复(4322207186)针对 test_seekdb_legacy_migration_cli_preserves_json_with_sql_echo,bf34f3e5 收敛为单进程持有引擎后确已解除(3.14 实测 28.53 s)。但在 head d8da2bca 上,同一文件的另一个新增用例重新越界:

  • tests/e2e/test_database_bootstrap.py::test_seekdb_legacy_choice_preserves_old_data_and_separates_new_database
    在 code-seekdb (3.11)(job 119624150671,03:47:02 起)跑满 tests/fixtures/seekdb_legacy_choice.py 自身的
    asyncio.timeout(60) 后失败;同 head code-seekdb (3.14)(job 119624150681)该用例 37.01 s 通过,
    即已用掉 60 s 预算的 62%,只剩 23 s 余量。
  • 失败阶段是 old data: reopening test(第 3 次整体 schema 初始化)。子进程 stderr 的
    SHOW FULL PROCESSLIST 显示该会话的 CREATE TABLE IF NOT EXISTS pc_agent_skill_targets (...) 已 Time=22 且仍 ACTIVE,
    同时 oceanbase.__all_virtual_lock_wait_stat 为空 ⇒ 是逐个建内置表的真实耗时,不是锁等待。
  • 归因:base c0c38dea 上该文件不存在(git cat-file -e 报 path does not exist),失败面只有本 PR 新增的用例与夹具,
    同 head 3.14 绿 ⇒ 属本 PR 引入的 3.11 差异,不是基线或抖动。
  • 机制:该夹具在一个子进程内串行做 7 次引擎 owner 打开(SeekDBProfile.open ×4 + 3 次 app.router.lifespan_context),
    每次 lifespan 都对同一嵌入式引擎重复整套 CREATE TABLE IF NOT EXISTS。run_seekdb_scenario 的
    subprocess.run(timeout=90) 大于夹具内的 asyncio.timeout(60),父进程的 90 s 永远不会先到,失败面因此只报阶段名
    (AssertionError: seed: opening test 等),拿不到"哪一次初始化超预算"。

建议:把三次 lifespan 收敛为一次(旧库显式选择 + 默认库可用同一 profile 顺序驱动,避免重复建表),
并把父进程 timeout=90 与夹具内部 asyncio.timeout(60) 统一到同一预算;若保留 60 s,需要说明 3.11 上
"3 次全量 schema 初始化 + 4 次引擎开关"为何能稳定落在 60 s 内。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rechecked at d8da2bca. The current Python 3.11 CI job still fails when the legacy-choice scenario exhausts its shared 60-second deadline during table creation. A local Python 3.14 run also exhausted that deadline, after explicit legacy Server startup succeeded, during the subsequent native reopen. Root cause remains unresolved. Please investigate initialization/reopen costs and establish a workable scenario budget while retaining real Server startup, legacy-data preservation, and database-isolation assertions, then rerun both versions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复核 e5d882ba(test(seekdb): reduce startup DDL exposure and retry once past the engine stall):本条 P1 已解除。

step 级证据(同 head、同 job)

  • code-seekdb (3.11)(job 119925972770)在 head e5d882ba 上 SUCCESS(16:05:53Z→16:11:03Z)。其中 tests/e2e/test_database_bootstrap.py 8 passed in 75.86 s,test_seekdb_legacy_choice_preserves_old_data_and_separates_new_database 的 call 耗时 25.74 s。
  • 上一轮 d8da2bca 的同一 job 是在该用例上跑满夹具自身 60 s 预算失败、并跳过后续迁移验收步骤;现在离预算有 ~34 s 余量。
  • 成因与改法对得上:tests/fixtures/seekdb_legacy_choice.py 的 seed 从"只取 pc_topic_memory_work_budgets 一张表"改为 SeekDBProfile.open(old, tables=(*BUILTIN_TABLES, records)),把表 DDL 全部移出启动观察阶段,启动期只做存在性检查。
  • 顺带核对 test_seekdb_creates_selected_database_and_preserves_data 新增的事务回滚断言:assert_record 断言的是 value == "preserved"(不是仅"行存在"),所以 raise RuntimeError("abort business write") 之后的检查确实证明回滚生效,不是空断言。

残留风险(不另开线程,仅说明,未达到 Finding 证据门槛)

run_seekdb_scenario 的重试判据是 stalled or any(signature in output for signature in _STALL_SIGNATURES),其中 _STALL_SIGNATURES = ("Timeout (0:", "TimeoutError:") 是子串匹配。真正的产品超时(例如迁移命令自身的 TimeoutError)也会命中,于是被静默重试一次:

  • 若第一轮 stall、第二轮通过,CI 全绿,而 stall 不留痕——_retain_scenario_diagnostics 只在 POWERCONTEXT_TEST_BOOTSTRAP_DIAGNOSTICS 设置时落盘,默认不写。
  • 也就是说"引擎 stall 已被消除"与"stall 仍在只是被重试掩盖"在默认 CI 输出上不可区分。

建议在重试时无条件输出一行原因(attempt-1 failed: stall signature <sig>),或把 attempt-1 的尾部日志写进 step summary,让这个取舍可观测、可回溯。

未覆盖:本轮未在本机复跑 embedded seekDB 场景(Windows 下该引擎行为与 CI 不同),结论依据的是 head 上 code-seekdb (3.11) / (3.14) 两个 job 的真实 step 耗时与结论。

it does not provision an OceanBase instance, tenant, or account. Ensure network access and install the Server extra:

```bash
uv tool install --force "powercontext[cli,server]==1.3.0"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 新增 “Use OceanBase” 段落里的 ==1.3.0 使 quality 的 make version-check 失败

head b0e379f6 的 quality(job 118151454292,make check)输出:

Release version mismatch: docs/en/docs/get-started/install-and-run.md (expected 1.4.0)
Release version mismatch: docs/zh/docs/get-started/install-and-run.md (expected 1.4.0)
make: *** [Makefile:234: version-check] Error 1

expected 取自 openapi/powercontext.yaml 的 version:;该 job 在上一 head 2fef5fab 与 master e4efb39b 上都是 success,因此这条红是 master 已发布 1.4.0 与本 PR 新增段落组合的结果。

合并结果(merge 04a53f41)里本文件只剩这一处旧版本号,正是 bf043440 新增的本段代码块(en 第 195 行 / zh 第 180 行):

uv tool install --force "powercontext[cli,server]==1.3.0"

本文件已在 scripts/release_version.py::REFERENCE_PREFIXES 内,因此 rebase 到当前 master 后执行 make version-bump VERSION=1.4.0 会自动改写这两行;不需要改实现,也不需要手工比对其他版本引用。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复审 0cab7347:该 P2 已解除。

bf34f3e5 同步修改了 docs/{en,zh}/docs/get-started/install-and-run.md;head 上两份文档的 description、安装命令、--ref、seekdb extra 与升级段全部为 1.4.0 / powercontext-v1.4.0(en 第 27/31/32/44/50/125/282/285 行,zh 第 25/29/30/42/48/115/252 行),==1.3.0 残留 0 处,quality 在本 head 通过(该 job 内含 make version-check)。

@hireview001 hireview001 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • CI failed: code-seekdb (3.14) (failure).
  • CI failed: quality (failure).
  • CI failed: validate (failure).
  • CI failed: E2E harness (failure). Failed steps: validate / Validate harness and Compose definitions.
  • CI failed: Main (failure). Failed steps: quality / Run checks; code-seekdb (3.14) / Test database bootstrap with embedded seekdb without skips.
  • CI did not complete: Kimi Code acceptance (cancelled).

@hidb4ai hidb4ai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed 0cab7347: both migration JSON issues are fixed, and the bootstrap timeout and release-version failures are cleared. The remaining identity-reset diagnostic issue is non-blocking (P3 follow-up).

Before merging, resolve the conflict with master (6d3db04f) in e2e/bub/tests/test_harbor_job_config.py and rerun the affected checks. Real seekDB was exercised locally; OceanBase evidence comes from CI.

@hireview001 hireview001 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Resolve merge conflicts with the target branch.

This probe is diagnostic: it never echoes SQL, because the caller may own the
process's machine-readable output and the echo handler writes to stdout.
"""
if "database" in config.model_fields_set:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Preserve implicit database selection through ServerSettings

ServerSettings(database=SeekDBConfig(path=...)) serializes nested defaults, adding database to model_fields_set, so this early return bypasses legacy detection although the caller never selected a database. Existing programmatic Server deployments can consequently switch silently from populated test storage to a new powercontext database; preserve explicit-selection provenance through settings normalization so startup still requires a choice.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified fixed at b9760f6f with real embedded seekDB. ServerSettings(database=SeekDBConfig(path=...)) now preserves the implicit database choice and refuses startup when legacy pc_ data exists in test. Readback confirmed that the legacy record remained intact and no powercontext database was created.

@hidb4ai

hidb4ai commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Resolve conflicts

@hidb4ai hidb4ai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed b9760f6f: the programmatic implicit-selection P1 is fixed, and the merge conflict is resolved. The non-blocking identity-reset P3 remains reproducible with real embedded seekDB: legacy selection raises an uncaught LegacySeekDBDatabaseError before identity rotation.

Local full Server startup did not pass: explicit test startup twice hit the 60-second timeout while creating pc_agent_skill_targets. Minimal current and pre-change DDL probes both succeeded, so attribution remains unresolved. The corresponding seekDB CI case passes; OceanBase validation is CI-only.

The code commands lost their JSON error contract and the offline maintenance
commands ended in tracebacks when an implicit legacy seekdb choice or a
provisioning failure reached the command boundary.

- Report `explicit_database_required` from the code entry point as its own
  stderr JSON payload instead of an uncaught exception.
- Report `DatabaseAccessError` and `DatabaseCreationError` from
  processing-migrate, atomic-memory-migrate, atomic-memory-rebuild-projection
  and identity-reset verbatim, because the rich error panel truncates the
  administrator statement these messages carry.
- Translate the same provisioning failures for writable seekdb db-migrate
  into `database_access_denied` and `database_creation_required` maintenance
  errors, and document both codes.
tests/test_cli.py kept both sides: this branch's code and maintenance
database-error tests and master's command-provider tests.

@hireview001 hireview001 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • CI failed: code-seekdb (3.11) (failure).
  • CI failed: code-seekdb (3.14) (failure).
  • CI failed: Main (failure). Failed steps: code-seekdb (3.14) / Test database bootstrap with embedded seekdb without skips; code-seekdb (3.11) / Test database bootstrap with embedded seekdb without skips.

@hidb4ai hidb4ai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed d8da2bca: the identity-reset P3 is fixed. Real embedded seekDB verified operator-facing refusal, explicit identity rotation, and preserved-data readback.

Merge readiness remains blocked by the legacy-choice acceptance timeout and a confirmed conflict with master (854e0836) in e2e/bub/tests/test_harbor_job_config.py (_kimi_installation). Resolve both and rerun the affected checks. The full local bootstrap batch did not complete successfully; the CLI scenario passed separately. OceanBase was not run locally.

tests/test_processing_migration_cli.py keeps both sides' tests, e2e/bub
takes master's version, and processing-migrate keeps plan and verify as
read-only inspection with master's rationale.
…ine stall

The embedded seekDB can stall indefinitely on one CREATE TABLE during the
startup schema burst; no client-side timeout bounds it, and two client
variants (no IF NOT EXISTS, autocommit DDL) were measured without effect.

Seed the legacy-choice fixture with the complete schema so the real startup
path issues no table DDL, and retry a failing scenario child once in a fresh
directory only when the output carries the stall signature. Genuine failures
still fail immediately, and per-attempt diagnostics are retained.

Also keep the KIMI guidance normalization alongside master's structure and
assert that a failed seekDB transaction rolls back.
@Lkx-JY

Lkx-JY commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

On the seekDB bootstrap stall: what it is, what we ruled out, and what this PR does about it

The intermittent failure of Test database bootstrap with embedded seekdb is an engine-side stall in embedded seekDB, not a defect in this PR. The failing statement is always one CREATE TABLE that the server accepts and then never completes — SHOW FULL PROCESSLIST shows it ACTIVE for 20 s and beyond (pc_agent_skill_targets, pc_atomic_memory_current and pc_server_identity have all been observed), while __all_virtual_lock_wait_stat is empty (so it is not a row-lock wait) and __all_virtual_trans_stat holds internal transactions with pending_write = 1. The client is blocked in aiomysql reading the reply. On an idle machine the whole 52-table schema initializes in ~3.3 s, so this is not DDL volume.

We ruled out the two plausible client-side causes by toggling one variable at a time and running interleaved rounds of the full selection (8 tests per round):

dropping the redundant IF NOT EXISTS after the existing has_table guard — 2 stalls in 7 rounds, no improvement;
running startup DDL on a dedicated autocommit = 1 engine instead of START TRANSACTION — 1 stall in 5 rounds versus 1 in 5 for the baseline.
Both changes were reverted; no product code is modified for this. Session timeouts cannot bound it either: ob_query_timeout is enforced for queries (2 s makes SELECT SLEEP(10) raise 4012) but does not apply to DDL — the stuck statement ran far past the 10 s default and past 60 s — and the instance exposes no ob_ddl_timeout. A client asyncio.timeout fires but cannot interrupt the native socket read, so raising budgets only converts a test failure into a step timeout.

This PR therefore handles it on the test side only: seekdb_legacy_choice.py now seeds the complete schema so the real startup path issues no table DDL (removing the window where the stall occurs), and run_seekdb_scenario retries a failing child once — in a fresh directory, and only when the output carries the stall signature or the process bound expired, so genuine failures still fail immediately. The retry path is verified: a stalling probe was retried twice, a plain assertion failure was not retried. After this change, 14 consecutive rounds of the full selection passed at ~70 s each, none needing the retry; the full unit suite is green (5538 passed, 101 skipped) and ruff/ty are clean.

The underlying defect is in embedded seekDB (a CREATE TABLE during a burst of schema DDL can block forever with no applicable timeout); it is not recoverable from the client and is noted here as a known engineering issue.

@hireview001 hireview001 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Resolve merge conflicts with the target branch.

schema.py keeps both sides: this branch's MySQL table-creation path and
master's portable timestamp precision upgrade, which composition calls
after create_tables.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1.4.1 v1.4.1 release tracking

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(storage): create a default database for seekDB and OceanBase and update deployment docs

4 participants