fix(core): prevent asyncpg engine-dispose crash on Postgres backend (#902)

Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul Hernandez
2026-06-07 17:37:23 -05:00
committed by GitHub
parent 3ba3a9504d
commit 816ee85fb9
11 changed files with 280 additions and 12 deletions
+8
View File
@@ -57,6 +57,14 @@ def app_callback(
container = CliContainer.create()
set_container(container)
# Trigger: Postgres backend resolved at CLI startup, before any asyncio.run().
# Why: uvloop must own the event-loop policy before the loop is created so the
# asyncpg engine-dispose race (#831/#877) cannot fire. No-op for SQLite.
# Outcome: subsequent asyncio.run() calls in CLI commands use uvloop on Postgres.
from basic_memory.db import maybe_install_uvloop
maybe_install_uvloop(container.config)
# Trigger: first-run init confirmation before command output.
# Why: informational "initialized" message belongs above command results, not in the upsell panel.
# Outcome: one-time plain line printed before the subcommand runs.
+12
View File
@@ -98,6 +98,18 @@ def mcp(
if transport == "stdio":
threading.Thread(target=_run_background_auto_update, daemon=True).start()
# Trigger: MCP server startup on the Postgres backend, before the transport
# creates its event loop.
# Why: the watcher/lifespan path runs startup migrations + engine.dispose() on
# asyncpg, which races stdlib asyncio teardown and crashes the container loop
# (#831/#877). uvloop's C scheduler structurally avoids that race and must own
# the loop policy before the loop is created. No-op for SQLite.
# Outcome: `basic-memory mcp` on Postgres runs on uvloop. (The CLI callback also
# installs it; this keeps the server startup seam explicit and self-contained.)
from basic_memory.db import maybe_install_uvloop
maybe_install_uvloop(ConfigManager().config)
# Run the MCP server (blocks)
# Lifespan handles: initialization, migrations, file sync, cleanup
logger.info(f"Starting MCP server with {transport.upper()} transport")