fix: evict stale project engine delegate on delete to prevent assert self._db crash
Deleting a project closed its store but left the delegate cached in _project_engine_delegates; re-creating a same-name project then reused the zombie engine and crashed in get_session. delete() now closes and evicts via _close_project_engine_store (also covers non-active deletes), the delegate cache self-heals when a cached store is closed, and _engine_for_project reopens a closed store for the non-evictable root engine case. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -240,14 +240,13 @@ class ProjectService:
|
||||
deleted_channels = int(await delete_chat(project_id) or 0)
|
||||
logger.info(f"Deleted {deleted_channels} channels for project '{project_id}'")
|
||||
|
||||
# Close every engine bound to this project AND evict its delegate from
|
||||
# the root engine's cache; a stale delegate would otherwise be reused
|
||||
# (with a closed store) if a project with the same id is re-created.
|
||||
await self._close_project_engine_store(project_id)
|
||||
|
||||
projects_dir = self.context.project_dir(project_id)
|
||||
if projects_dir.is_dir():
|
||||
active_engine = self.context.engine
|
||||
if was_active and getattr(active_engine, "store", None):
|
||||
try:
|
||||
await active_engine.store.close()
|
||||
except Exception:
|
||||
logger.opt(exception=True).debug("Failed to close active project store before delete")
|
||||
shutil.rmtree(str(projects_dir), ignore_errors=True)
|
||||
|
||||
workplace = self.context.project_workplace(project_id)
|
||||
|
||||
@@ -763,6 +763,15 @@ class WSHandler:
|
||||
)
|
||||
maybe_engine = delegate_getter(normalized)
|
||||
engine = await maybe_engine if inspect.isawaitable(maybe_engine) else maybe_engine
|
||||
# Self-heal a closed store (project deleted then re-created while this
|
||||
# engine instance stayed bound to it — e.g. the root engine, which can
|
||||
# never be evicted from its own delegate cache).
|
||||
store = getattr(engine, "store", None)
|
||||
if store is not None and not getattr(store, "is_ready", True):
|
||||
ensure_ready = getattr(store, "ensure_ready", None)
|
||||
if callable(ensure_ready):
|
||||
logger.warning(f"Reopening closed store for project '{normalized}'")
|
||||
await ensure_ready()
|
||||
self._wire_engine_callbacks(engine)
|
||||
return engine
|
||||
|
||||
|
||||
Reference in New Issue
Block a user