Skip to content

fix(memory): don't take a write lock to backfill nothing on every open (#368) - #980

Open
ziomik wants to merge 1 commit into
bradbrok:mainfrom
ziomik:fix/368-backfill-review-once
Open

fix(memory): don't take a write lock to backfill nothing on every open (#368)#980
ziomik wants to merge 1 commit into
bradbrok:mainfrom
ziomik:fix/368-backfill-review-once

Conversation

@ziomik

@ziomik ziomik commented Aug 2, 2026

Copy link
Copy Markdown

Fixes #368.

Il problema

_migrate_backfill_review_schedule eseguiva la sua UPDATE in modo incondizionato a ogni apertura di ReflectionStore. Diversi componenti ne costruiscono uno all'avvio del daemon, quindi ognuno prendeva una transazione di scrittura anche quando nessuna riga aveva bisogno di essere schedulata — che e' lo stato normale a regime.

Perche' NON e' stata resa una migrazione one-shot

Il nome del metodo e il vecchio docstring ("on first run") dicono migrazione una-tantum, ed e' la lettura che porterebbe naturalmente a un marker di versione dello schema. Sarebbe stato un errore silenzioso.

insert() non imposta mai next_review_date — le nuove righe restano NULL. Questa UPDATE e' quindi anche cio' che rende recensibile una memoria appena creata. Eseguirla una volta sola avrebbe fatto smettere al sistema di review di raccogliere qualunque cosa inserita in seguito, senza alcun errore visibile.

Quindi il comportamento resta identico: gira a ogni apertura. Cambia solo che non prende piu' il lock quando non c'e' niente da fare.

Il fix

Una sonda read-only (SELECT 1 ... LIMIT 1) precede la UPDATE; si scrive solo se esiste davvero almeno una riga da schedulare.

Il predicato delle righe e' estratto in _NEEDS_REVIEW_DATE, condiviso da guardia e UPDATE cosi' i due non possono divergere: una guardia piu' stretta salterebbe lavoro reale, una piu' larga rimetterebbe la scrittura a vuoto che questa PR elimina.

Il docstring e' stato riscritto per dire cosa fa davvero il metodo, visto che quello vecchio era esattamente la premessa fuorviante.

Test

Nuovo file tests/test_memory_backfill_review_writes.py (4 test):

  • riaprire un DB gia' interamente schedulato non esegue alcuna scrittura di backfill
  • lo stesso su un DB vuoto
  • una memoria inserita dopo viene comunque schedulata alla successiva apertura (fissa il comportamento load-bearing descritto sopra)
  • le memorie protette (salience >= 4) restano NULL e non entrano nel ciclo di review

Verifica eseguita:

  • pre-fix: 2 failed (la UPDATE parte anche su un DB vuoto), 2 passed — i 2 verdi sono le guardie di non-regressione, il loro compito e' passare da subito
  • post-fix: 4 passed
  • non-regressione: 464 passed su tutte e 10 le suite che toccano pinky_memory (test_memory_store, test_memory_server, test_kg_*, test_memory_cross_agent, test_memory_heal_unembedded, test_shared_mcp)
  • ruff check pulito su entrambi i file

Note

Branch basato su origin/main, come #979. Le due PR toccano hunk diversi dello stesso file e non hanno dipendenze d'ordine: si possono mergiare in qualunque sequenza.


🤖 Opened by Engineer

bradbrok#368)

_migrate_backfill_review_schedule ran its UPDATE unconditionally on every
ReflectionStore open, so every component that constructs a store took a write
transaction at daemon start even when no row needed scheduling — which is the
steady state. Guard it with a read-only EXISTS probe and write only when there
is something to schedule.

Note this is deliberately NOT turned into a one-shot migration despite its
name and old docstring: insert() never sets next_review_date, so this UPDATE
is also what makes newly created memories reviewable. Running it once would
silently stop the review system from picking up anything inserted later. The
docstring now says so, and a test pins the behaviour.

The row predicate moves into _NEEDS_REVIEW_DATE, shared by the guard and the
UPDATE so they cannot drift: a narrower guard would skip real work, a wider
one would write when there is nothing to do.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant