fix(maria): lacat intre reindexari, scriere atomica a indexului, XML invalid raportat
Descoperit la prima sincronizare reala din Drive: `maria-sync.timer` a pornit peste rularea manuala si doua procese faceau embeddings in paralel pe acelasi Ollama, ambele urmand sa scrie acelasi rag_index.json. Embedding-ul a incetinit de la ~7s la ~20s din concurenta, iar ultimul care termina ar fi suprascris munca celuilalt. - config.exclusive(): lacat `flock` intre procese, luat la intrarea in sync.py si indexer.py. Nu asteapta — a doua rulare iese curat cu "o reindexare e deja in curs", fiindca ar reface exact acelasi lucru. Verificat pe procese reale. - indexer scrie indexul atomic (tmp + os.replace): consumer-ul reciteste fisierul la 30s si putea prinde un JSON pe jumatate scris. - build() intoarce `warnings` pentru XML-urile care nu se pot parsa, iar rularea din linia de comanda le scrie in stderr. Pana acum, un XML invalid se indexa tacut ca text simplu, cu o singura linie pierduta in log. Context de performanta, masurat pe LXC 171 fara alta incarcare: un embedding `nomic-embed-text` ia ~7,3s, deci o reindexare completa a celor 173 de chunk-uri dureaza ~21 de minute — mai mult decat intervalul timer-ului. Nu e o problema practica (amprenta reindexeaza doar la schimbare, iar lacatul opreste suprapunerea), dar explica de ce prima rulare pare blocata. docs/rclone-google-drive-headless.md: procedura de conectare a unui container headless la Drive prin `rclone authorize`, cu transcriptul rularii reale de pe Windows, capcanele (sync e distructiv pe destinatie, connection string in loc de cale pe nume, unde stau secretele) si de ce nu contul de serviciu. Indexata in CLAUDE.md. 6 teste noi (lacat, eliberare la exceptie, scriere atomica, avertismente). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
This commit is contained in:
@@ -7,6 +7,8 @@ model de citit pentru cine intretine ambele punti pe acest container.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import fcntl
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
@@ -16,6 +18,7 @@ _DEFAULT_DIR = pathlib.Path.home() / ".maria-bridge"
|
||||
STATE_DIR: pathlib.Path = pathlib.Path(os.environ.get("MARIA_BRIDGE_DIR") or _DEFAULT_DIR)
|
||||
DOCS_DIR: pathlib.Path = STATE_DIR / "documents"
|
||||
INDEX_FILE: pathlib.Path = STATE_DIR / "rag_index.json"
|
||||
LOCK_FILE: pathlib.Path = STATE_DIR / ".rag.lock"
|
||||
LOG_DIR: pathlib.Path = STATE_DIR / "logs"
|
||||
ENV_FILE: pathlib.Path = STATE_DIR / "env"
|
||||
AUTH_DIR: pathlib.Path = STATE_DIR / "whatsapp-auth"
|
||||
@@ -70,12 +73,13 @@ def parse_env(text: str) -> dict[str, str]:
|
||||
|
||||
def reload(base_dir: str | os.PathLike | None = None) -> dict[str, str]:
|
||||
"""Recalculeaza caile si reciteste env-ul. Returneaza dictionarul incarcat."""
|
||||
global STATE_DIR, DOCS_DIR, INDEX_FILE, LOG_DIR, ENV_FILE, AUTH_DIR, _env
|
||||
global STATE_DIR, DOCS_DIR, INDEX_FILE, LOCK_FILE, LOG_DIR, ENV_FILE, AUTH_DIR, _env
|
||||
if base_dir is None:
|
||||
base_dir = os.environ.get("MARIA_BRIDGE_DIR") or _DEFAULT_DIR
|
||||
STATE_DIR = pathlib.Path(base_dir)
|
||||
DOCS_DIR = STATE_DIR / "documents"
|
||||
INDEX_FILE = STATE_DIR / "rag_index.json"
|
||||
LOCK_FILE = STATE_DIR / ".rag.lock"
|
||||
LOG_DIR = STATE_DIR / "logs"
|
||||
ENV_FILE = STATE_DIR / "env"
|
||||
AUTH_DIR = STATE_DIR / "whatsapp-auth"
|
||||
@@ -102,3 +106,30 @@ def get_int(key: str, default: int) -> int:
|
||||
|
||||
|
||||
reload()
|
||||
|
||||
|
||||
class Busy(RuntimeError):
|
||||
"""Alta reindexare e deja in curs."""
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def exclusive():
|
||||
"""Lacat intre PROCESE pentru reindexare (timer vs dashboard vs rulare manuala).
|
||||
|
||||
Reindexarea dureaza minute (embeddings pe CPU). Fara lacat, `maria-sync.timer`
|
||||
poate porni peste o sincronizare manuala si ambele scriu acelasi rag_index.json.
|
||||
Nu asteptam: a doua rulare se anuleaza curat, fiindca oricum ar reface acelasi
|
||||
lucru imediat dupa.
|
||||
"""
|
||||
STATE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
fh = open(LOCK_FILE, "w", encoding="utf-8")
|
||||
try:
|
||||
try:
|
||||
fcntl.flock(fh, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
except OSError:
|
||||
raise Busy("o reindexare e deja in curs")
|
||||
fh.write(str(os.getpid()))
|
||||
fh.flush()
|
||||
yield
|
||||
finally:
|
||||
fh.close()
|
||||
|
||||
Reference in New Issue
Block a user