Skip to content

Relational DB (cognee_db) grows unbounded — no eviction on results/queries tables #2548

@king-sheol

Description

@king-sheol

Description

The internal SQLite database (cognee_db) grows without bound when Cognee is used as a long-running service with automated search queries. There is no eviction, TTL, or size cap on the results and queries tables, and SQLite does not auto-VACUUM, so the file only grows — never shrinks.

This is a companion to #2538 (unbounded log growth). Same root pattern — write-only accumulation with no rotation — but in the relational store rather than log files.

Impact

On our production system running Cognee as an HTTP daemon with automated monitoring and ingestion pipelines:

Metric Value
DB file size 28 GB
Period 9 days
Growth rate ~3 GB/day
results rows 42,152
queries rows 42,174
nodes rows 23,450
edges rows 48,029
data rows 1,409
SQLite freelist pages 0 (no reclaimable space)

The 28 GB file filled the disk to 89%, causing memory pressure (the daemon had to swap), degraded search latency, and eventually a cascading service restart.

Root cause

modules/search/operations/log_result.py and log_query.py append a row on every search call with no cleanup:

# log_result.py — write-only, no eviction
async def log_result(query_id, result, user_id):
    db_engine = get_relational_engine()
    async with db_engine.get_async_session() as session:
        session.add(Result(value=result, query_id=query_id, user_id=user_id))
        await session.commit()

Result.value is a Text column that stores serialized search results including full node payloads. At ~4,700 queries/day (typical for a daemon with health probes and automated ingestion), the table reaches tens of gigabytes within days.

Additionally, the nodes and edges tables mirror graph data that already lives in the graph DB (FalkorDB/Neo4j), creating a second unbounded copy.

Environment

  • Cognee v0.5.5 (same issue likely exists in v0.5.7 — the log rotation PR fix: log rotation, safe default dir, and opt-out file logging #2541 did not touch the relational layer)
  • VECTOR_DB_PROVIDER=falkor (graph + vectors in FalkorDB)
  • HTTP daemon mode (~4,700 search queries/day from automated pipelines)
  • SQLite as the default relational backend

Proposed solution

Short-term (cache eviction):

  • Add a configurable max row count or TTL for results and queries tables (e.g., COGNEE_MAX_CACHED_RESULTS=10000, COGNEE_QUERY_HISTORY_TTL_DAYS=30)
  • FIFO eviction: delete oldest rows when the cap is exceeded
  • Run eviction as part of the search call or as a periodic background task

Medium-term (VACUUM):

  • Periodic auto-VACUUM or PRAGMA auto_vacuum = INCREMENTAL to reclaim space from deleted rows
  • Alternatively, COGNEE_DB_VACUUM_INTERVAL env var for daemon deployments

Optional:

  • Make query/result logging opt-out via COGNEE_LOG_QUERIES=false for daemon deployments that don't need search history
  • Consider whether nodes/edges tables need to exist when an external graph DB is configured — they duplicate data and contribute to growth

Workaround

Manual cleanup (requires stopping the daemon):

# Export tracking data
sqlite3 cognee_db ".dump data" > /tmp/tracking_backup.sql
sqlite3 cognee_db ".dump datasets" >> /tmp/tracking_backup.sql

# Delete and restart — daemon recreates empty DB
rm cognee_db
systemctl restart cognee-daemon

# Restore tracking
grep "^INSERT" /tmp/tracking_backup.sql | \
  sed 's/INSERT INTO/INSERT OR REPLACE INTO/' | \
  sqlite3 cognee_db

Or periodic VACUUM via cron (does not require restart, but only reclaims space from deleted rows):

0 3 * * 0 sqlite3 /path/to/cognee_db "DELETE FROM results WHERE created_at < datetime('now', '-7 days'); DELETE FROM queries WHERE created_at < datetime('now', '-7 days'); VACUUM;"

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