aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/indexer/cache.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/indexer/cache.py')
-rw-r--r--packages/meshbay-node/src/meshbay_node/indexer/cache.py56
1 files changed, 33 insertions, 23 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/indexer/cache.py b/packages/meshbay-node/src/meshbay_node/indexer/cache.py
index 31b9b15..c167db9 100644
--- a/packages/meshbay-node/src/meshbay_node/indexer/cache.py
+++ b/packages/meshbay-node/src/meshbay_node/indexer/cache.py
@@ -32,21 +32,25 @@ log = logging.getLogger(__name__)
_SCHEMA = """
CREATE TABLE IF NOT EXISTS files (
- path TEXT PRIMARY KEY,
- mtime REAL NOT NULL,
- size INTEGER NOT NULL,
- hash TEXT NOT NULL,
- type TEXT NOT NULL,
- added_at INTEGER NOT NULL
+ path TEXT PRIMARY KEY,
+ mtime REAL NOT NULL,
+ size INTEGER NOT NULL,
+ hash TEXT NOT NULL,
+ type TEXT NOT NULL,
+ added_at INTEGER NOT NULL,
+ hash_version INTEGER NOT NULL DEFAULT 1
);
"""
+_MIGRATE_V2 = "ALTER TABLE files ADD COLUMN hash_version INTEGER NOT NULL DEFAULT 1"
+
@dataclass
class CachedEntry:
hash: str
type: str
added_at: int
+ hash_version: int = 1
class IndexCache:
@@ -60,6 +64,10 @@ class IndexCache:
self._db_path.parent.mkdir(parents=True, exist_ok=True)
self._db = await aiosqlite.connect(str(self._db_path))
await self._db.executescript(_SCHEMA)
+ try:
+ await self._db.execute(_MIGRATE_V2)
+ except Exception:
+ pass # column already exists
await self._db.commit()
async def close(self) -> None:
@@ -74,34 +82,36 @@ class IndexCache:
async def __aexit__(self, *_):
await self.close()
- async def lookup(self, path: str, size: int, mtime: float) -> CachedEntry | None:
+ async def lookup(self, path: str, size: int, mtime: float,
+ hash_version: int = 1) -> CachedEntry | None:
"""
- A cache hit requires an EXACT match on both size and mtime. A mtime
- touched without a content change is a false negative (an unnecessary
- rehash) — accepted, since the alternative (trusting a stale hash) is
- a silent wrong answer instead of an occasional wasted read.
+ A cache hit requires an EXACT match on size, mtime AND hash_version.
+ A v1 cached hash won't serve a v2 lookup for the same path — the file
+ is re-hashed with the new algorithm instead.
"""
async with self._db.execute(
- "SELECT hash, type, added_at FROM files "
- "WHERE path = ? AND size = ? AND mtime = ?",
- (path, size, mtime)) as cur:
+ "SELECT hash, type, added_at, hash_version FROM files "
+ "WHERE path = ? AND size = ? AND mtime = ? AND hash_version = ?",
+ (path, size, mtime, hash_version)) as cur:
row = await cur.fetchone()
- return CachedEntry(hash=row[0], type=row[1], added_at=row[2]) if row else None
+ return CachedEntry(hash=row[0], type=row[1], added_at=row[2],
+ hash_version=row[3]) if row else None
async def put(self, path: str, size: int, mtime: float, hash: str,
- type: str, added_at: int) -> None:
+ type: str, added_at: int, hash_version: int = 1) -> None:
"""
- Written only once a file has been hashed in full — never partway
- through — so a crash mid-hash leaves no stale/partial row behind: the
- next scan simply finds no cache entry and hashes the file again.
+ Written only once a file has been hashed — never partway through — so
+ a crash mid-hash leaves no stale/partial row behind: the next scan
+ simply finds no cache entry and hashes the file again.
"""
await self._db.execute(
- "INSERT INTO files (path, mtime, size, hash, type, added_at) "
- "VALUES (?, ?, ?, ?, ?, ?) "
+ "INSERT INTO files (path, mtime, size, hash, type, added_at, hash_version) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?) "
"ON CONFLICT(path) DO UPDATE SET "
"mtime = excluded.mtime, size = excluded.size, hash = excluded.hash, "
- "type = excluded.type, added_at = excluded.added_at",
- (path, mtime, size, hash, type, added_at))
+ "type = excluded.type, added_at = excluded.added_at, "
+ "hash_version = excluded.hash_version",
+ (path, mtime, size, hash, type, added_at, hash_version))
await self._db.commit()
# ── Maintenance (node admin UI "prune index cache") ──────────────────────