mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-09 09:11:00 +08:00
Nondeterministic Windows reds came from races on _config.db:
test-windows-guards section_start_status_port / daemon_lifecycle ("the
daemon did not accept the UI configuration"), section_churn_stability
("could not load saved runtime settings"), the windows-2025 release
smoke whose `--ui=true --port` server exited about 5 s after start
(Phase 15, read as "no embedded UI assets"), and section_crash_recovery
("the stale daemon state never cleared").
- Every _config.db connection now waits out a committing writer
(cbm_runtime_settings_wait_for_writers: one busy handler, logs
config_db.busy_wait once per busy wait, a 3 s bound for a wedged
holder). Startup's 50 ms window let a daemon's routine commit end
unrelated CLI starts; cbm_config_open and the UI load had no wait at
all, so a concurrent commit failed the daemon's UI save and made the
load keep defaults.
- A process killed mid-write (crash, taskkill /F, power loss) left a hot
rollback journal that the read-only settings reads cannot roll back
(SQLITE_READONLY_ROLLBACK). Every later command then failed with
"could not load saved runtime settings", permanently. Settings startup
and the UI load now recover once through a read-write open that rolls
the interrupted write back (config_db.hot_journal_recovered); a failed
recovery is reported with the journal path and stays fatal.
- write_value no longer rewrites an unchanged value. The upsert fired the
revision trigger, so every `daemon start` on a UI build committed and
locked readers out although nothing changed.
- `daemon start` and the stdio server wait for the set_context and
set_ui_config replies within their startup budget instead of the 1 s
probe budget (MAIN_CONNECT_TIMEOUT_MS) that decided the command
whenever the daemon's save ran long.
Tests, red on 8df0639c and green here:
- runtime_settings suite: three single-threaded lock tests (a log sink
commits the held lock when the waiter logs config_db.busy_wait, so no
test depends on a duration) and two hot-journal tests (a portable
fixture snapshots the database and its journal mid-transaction and
checks the journal is hot before the code under test runs).
- tests/windows/test_daemon_stability.py: new section
start_port_waits_for_settings_writer holds the lock against a real
`daemon start --port` and releases it on events; the crash section
now prints the status output it saw.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
39 lines
1.7 KiB
C
39 lines
1.7 KiB
C
#ifndef CBM_RUNTIME_SETTINGS_H
|
|
#define CBM_RUNTIME_SETTINGS_H
|
|
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
|
|
/* Call once before starting threads or initializing environment-dependent
|
|
* subsystems. Saved environment values are immutable for this process; they
|
|
* never enter the inherited process environment. */
|
|
bool cbm_runtime_settings_init(const char *cache_dir);
|
|
bool cbm_runtime_settings_initialized(void);
|
|
|
|
/* JSON API. Returned strings use CBM_MEM_CLASS_OTHER; the caller releases them
|
|
* with cbm_free(CBM_MEM_CLASS_OTHER, json). HTTP status is
|
|
* 200, 400 (invalid batch), 409 (stale revision), or 500 (storage failure).
|
|
* A batch either commits completely or changes nothing. */
|
|
char *cbm_runtime_settings_get_json(const char *cache_dir);
|
|
char *cbm_runtime_settings_apply_json(const char *cache_dir, const char *body, size_t length,
|
|
int *status);
|
|
|
|
/* UI JSON is the legacy fallback. Once initialized, CLI/daemon UI writes use
|
|
* the same transactional store as the Config dialog. Last explicit write wins. */
|
|
void cbm_runtime_settings_load_ui(bool *enabled, int *port);
|
|
bool cbm_runtime_settings_save_ui(bool enabled, int port);
|
|
/* The HTTP handler supplies its actual bound port for truthful listener state. */
|
|
void cbm_runtime_settings_note_http_port(int port);
|
|
|
|
/* Every connection to _config.db waits for a committing writer instead of
|
|
* failing on its lock. Logs config_db.busy_wait once per busy wait. */
|
|
struct sqlite3;
|
|
void cbm_runtime_settings_wait_for_writers(struct sqlite3 *db);
|
|
|
|
#ifdef CBM_ENABLE_TEST_SEAMS
|
|
/* Single-threaded test teardown only; never exposed in production builds. */
|
|
void cbm_runtime_settings_reset_for_tests(void);
|
|
#endif
|
|
|
|
#endif
|