Skip to content

bug: metrics flush loop constructs a new CacheDB every 60s, logging spurious 'Schema initialized' and re-deriving AES key on each tick #48

@barrygfox

Description

@barrygfox

Summary

The optimize metrics flush loop in unified_daemon.py calls CacheDB() on every iteration rather than reusing a single instance. Every 60 seconds this:

  1. Constructs a new DatabaseManager and calls initialize() → logs "Schema initialized at llmcache.db" even though the schema already exists
  2. Re-runs the SQLite corruption check
  3. Reloads or regenerates the AES-256-GCM salt and derives the encryption key from scratch

None of this work is needed after the first open. It's purely noise, but it fills the daemon log at a rate of once per minute indefinitely.

Observed behaviour

2026-06-16 19:23:51 Schema initialized at /Users/barrygfo/.superlocalmemory/llmcache.db
2026-06-16 19:24:51 Schema initialized at /Users/barrygfo/.superlocalmemory/llmcache.db
2026-06-16 19:25:51 Schema initialized at /Users/barrygfo/.superlocalmemory/llmcache.db
... (every 60s, indefinitely)

Root cause

server/unified_daemon.py_metrics_flush_loop():

async def _metrics_flush_loop():
    while True:
        await asyncio.sleep(60)
        try:
            MetricsPersistence().flush(
                MetricsCollector.get_instance(), CacheDB()  # ← new instance each tick
            )

CacheDB.__init__ always runs self._db.initialize(_schema) (which logs "Schema initialized"), the corruption integrity check, and AES key derivation — all of which are one-time setup operations, not per-flush ones.

The startup load call on the line before the loop has the same issue:

MetricsPersistence().load(MetricsCollector.get_instance(), CacheDB())  # ← also a fresh instance

Fix

CacheDB already provides a thread-safe singleton via CacheDB.get_default() (used correctly at shutdown). The flush loop should use it:

# startup load
MetricsPersistence().load(MetricsCollector.get_instance(), CacheDB.get_default())

# flush loop
async def _metrics_flush_loop():
    while True:
        await asyncio.sleep(60)
        try:
            MetricsPersistence().flush(
                MetricsCollector.get_instance(), CacheDB.get_default()
            )

Two-character change per call site. The singleton is already created, thread-safe, and cleared at shutdown (CacheDB.reset_default() is called in the shutdown path).

Environment

  • SLM version: 3.6.13 (also present in current main)
  • Python: 3.14
  • Platform: macOS Darwin 25.5.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions