Skip to content

fix: thread safety — query_relationship(), timeline(), stats() missing self._lock in KnowledgeGraph #883

@shafdev

Description

@shafdev

What happened?

Three read methods in KnowledgeGraph (query_relationship, timeline, stats) access the shared sqlite3.Connection object without acquiring self._lock. All write methods (add_entity, add_triple, invalidate) and the query_entity read method correctly hold self._lock before touching the connection, but these three were missed.

When the MCP server handles concurrent tool calls (e.g. mempalace_kg_timeline and mempalace_kg_add running at the same time), a write can interleave mid-read on the same connection object, which can produce:

  • Garbled / inconsistent results (most silent and dangerous)
  • A ProgrammingError: recursive use of cursor crash on some SQLite builds

Note: sqlite3.connect(..., check_same_thread=False) silences the thread-origin check but does not make the connection thread-safe — a Python-level lock is still required.

What did you expect?

All methods that access the shared sqlite3.Connection in KnowledgeGraph should hold self._lock for the full duration of their database operation, matching the pattern already used by add_entity, add_triple, invalidate, and query_entity.

How to reproduce:

# Code-level proof — shows which methods are missing the lock
import inspect
from mempalace.knowledge_graph import KnowledgeGraph

for method in ["add_triple", "query_entity", "query_relationship", "timeline", "stats"]:
    src = inspect.getsource(getattr(KnowledgeGraph, method))
    has = "self._lock" in src
    print(f"{method:<25} {'✓ locked' if has else '✗ MISSING LOCK'}")

Expected output (buggy):

add_triple                ✓ locked
query_entity              ✓ locked
query_relationship        ✗ MISSING LOCK
timeline                  ✗ MISSING LOCK
stats                     ✗ MISSING LOCK

Fix:
Wrap each of the three methods in with self._lock: and move conn = self._conn() inside the block, exactly as query_entity does.

Environment:

  • OS: OS: Linux (Ubuntu 24.04)
  • Python version: 3.12
  • MemPal version: 3.3.0 (current develop branch, commit 4de9e13)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions