fix(kanban): serialize first-use schema initialization - #21395
fix(kanban): serialize first-use schema initialization#21395qWaitCrypto wants to merge 2 commits into
Conversation
|
I reproduced the same class of Kanban gateway startup failure locally on v0.13.0 / Linux: The gateway service itself stayed active, but the embedded Kanban dispatcher lost its first tick while opening the board through: Local mitigation testedI locally hardened Specifically, the local patch:
Validation from my local environment: Earlier local targeted test run after the hotfix passed: Design feedback on this PRI think the RLock / One possible complementary hardening: keep the migration itself idempotent/tolerant of duplicate-column races as a defensive layer. The lock in this PR protects concurrent threads inside one Hermes process, but duplicate-column races can still happen if:
So my suggestion is:
That combination would cover both observed symptoms:
Local noteI re-ran the currently relevant targeted suite in my checkout after reviewing this PR: Current result in my checkout: The failing test is: I confirmed the same failure reproduces in a clean detached worktree at my current HEAD without the local |
Thanks — I agreed with that scope expansion and updated the branch accordingly. This PR now has both layers:
I also incorporated the indexed-column detail: the migration now runs |
|
Closing as superseded. The duplicate-column race this PR addressed was fixed on The complementary half of the bug (the redundant Really appreciated the thoroughness here, especially the threading test. Thanks for the work @qWaitCrypto. |
What does this PR do?
Fixes a kanban SQLite initialization race during gateway startup.
The gateway can start the kanban notifier watcher and dispatcher watcher at nearly the same time. On their first tick, both paths can touch the same board DB and race through
hermes_cli.kanban_db.connect()/init_db(). Before this change, the module-level_INITIALIZED_PATHScache was checked and updated without synchronization, so two threads in the same process could both decide that the same DB path still needed schema initialization and then run_migrate_add_optional_columns()concurrently.Depending on timing, startup could fail with either:
sqlite3.OperationalError: duplicate column name: consecutive_failuressqlite3.OperationalError: database is lockedThis PR applies the proper fix in
hermes_cli/kanban_db.pyby serializing the full first-usecheck -> init -> migrate -> setpath with a module-levelthreading.RLock. Normal post-initialization connections remain unlocked.In addition to serializing in-process first-use initialization, this PR also makes optional-column migrations tolerate duplicate-column races. If another process adds an additive migration column between
PRAGMA table_info(...)andALTER TABLE ... ADD COLUMN,_ensure_column()re-reads the schema and suppresses only confirmed duplicate-column races.Related Issue
Fixes #21374.
Fixes #21378.
Likely addresses #21708.
Type of Change
Changes Made
threading.RLockin hermes_cli/kanban_db.py.connect()andinit_db()use the same locked initialization path instead of duplicatingcheck/init/setlogic.connect()calls off the global lock._ensure_column()to make additive migrations duplicate-column tolerant after schema re-read confirmation._migrate_add_optional_columns()to use_ensure_column()for task/task_events optional columns.How to Test
connect()calls against the same fresh DB no longer race into duplicate-column or early locked-DB failures.Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/AScreenshots / Logs
Relevant test command:
Observed result: