Skip to content

lightrag-memgraph: shared Memgraph driver lifecycle gap in _connection.py #223

Description

@antejavor

What's wrong

integrations/lightrag-memgraph/src/lightrag_memgraph/_connection.py caches a single shared AsyncMemgraph driver per event loop:

_clients: dict[int, AsyncMemgraph] = {}

keyed by id(asyncio.get_running_loop()). Two issues with this:

  1. No reference counting. Every KV/vector/doc-status storage instance's finalize() independently calls the shared close_driver(), which unconditionally pops-and-closes the one cached client for the loop. A single LightRAG instance owns ~10-11 separate storage objects sharing this one driver; today this is only safe because finalize_storages() calls them all sequentially in one LightRAG instance and close_driver()'s pop(..., None) is idempotent. It breaks if two MemgraphLightRAGWrapper/LightRAG instances are ever alive concurrently on the same event loop (e.g. a multi-tenant server keeping several workspace-scoped RAG instances resident in one process) — not exercised anywhere in this codebase's current tests/examples today, so this is a latent architectural gap rather than an active bug.

  2. id()-reuse risk (lower confidence, flagged for a closer look). id() is a memory address; if a closed event loop is garbage-collected before its cache entry is ever explicitly removed via close_driver() (e.g. a retry loop, or a long-lived worker wrapping each request in its own asyncio.run()), a later, unrelated loop could reuse the same address and get handed a driver bound to the dead loop. Two independent review passes disagreed on whether the cached client's internal state keeps the loop referenced long enough to prevent this in practice — worth resolving with a definitive test rather than reasoning alone.

Acceptance criteria

  • Driver lifecycle is owned by whichever component created the set of storage instances (e.g. the wrapper), not implicitly shared/torn-down by any one storage's finalize() — reference-count storages per loop, or close once after all of a LightRAG instance's storages are done
  • Test (or documented rationale) resolving whether the id(event loop) cache key can collide across a discarded and a new loop in this codebase's actual usage patterns

Blocked by

None - can start immediately

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions