mirror of https://github.com/jackwener/wx-cli.git
fix: MCP DBCache 使用固定文件名 + 持久化缓存,重启秒恢复
- 缓存文件改用 md5(rel_key) 固定命名,放在 Temp/wechat_mcp_cache/ - mtime 映射持久化到 _mtimes.json,重启后验证一致直接复用 - 避免 mkstemp 随机文件名导致崩溃后临时文件堆积 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>feat/daemon-cli
parent
121fa9f7bd
commit
73598751a0
|
|
@ -120,10 +120,59 @@ def decrypt_wal(wal_path, out_path, enc_key):
|
||||||
# ============ DB 缓存 ============
|
# ============ DB 缓存 ============
|
||||||
|
|
||||||
class DBCache:
|
class DBCache:
|
||||||
"""缓存解密后的 DB,通过 mtime 检测变化"""
|
"""缓存解密后的 DB,通过 mtime 检测变化。使用固定文件名,重启后可复用。"""
|
||||||
|
|
||||||
|
CACHE_DIR = os.path.join(tempfile.gettempdir(), "wechat_mcp_cache")
|
||||||
|
MTIME_FILE = os.path.join(tempfile.gettempdir(), "wechat_mcp_cache", "_mtimes.json")
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._cache = {} # rel_key -> (db_mtime, wal_mtime, tmp_path)
|
self._cache = {} # rel_key -> (db_mtime, wal_mtime, tmp_path)
|
||||||
|
os.makedirs(self.CACHE_DIR, exist_ok=True)
|
||||||
|
self._load_persistent_cache()
|
||||||
|
|
||||||
|
def _cache_path(self, rel_key):
|
||||||
|
"""rel_key -> 固定的缓存文件路径"""
|
||||||
|
h = hashlib.md5(rel_key.encode()).hexdigest()[:12]
|
||||||
|
return os.path.join(self.CACHE_DIR, f"{h}.db")
|
||||||
|
|
||||||
|
def _load_persistent_cache(self):
|
||||||
|
"""启动时从磁盘恢复缓存映射,验证 mtime 后复用"""
|
||||||
|
if not os.path.exists(self.MTIME_FILE):
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
with open(self.MTIME_FILE) as f:
|
||||||
|
saved = json.load(f)
|
||||||
|
except (json.JSONDecodeError, OSError):
|
||||||
|
return
|
||||||
|
reused = 0
|
||||||
|
for rel_key, info in saved.items():
|
||||||
|
tmp_path = info["path"]
|
||||||
|
if not os.path.exists(tmp_path):
|
||||||
|
continue
|
||||||
|
rel_path = rel_key.replace('\\', os.sep)
|
||||||
|
db_path = os.path.join(DB_DIR, rel_path)
|
||||||
|
wal_path = db_path + "-wal"
|
||||||
|
try:
|
||||||
|
db_mtime = os.path.getmtime(db_path)
|
||||||
|
wal_mtime = os.path.getmtime(wal_path) if os.path.exists(wal_path) else 0
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
if db_mtime == info["db_mt"] and wal_mtime == info["wal_mt"]:
|
||||||
|
self._cache[rel_key] = (db_mtime, wal_mtime, tmp_path)
|
||||||
|
reused += 1
|
||||||
|
if reused:
|
||||||
|
print(f"[DBCache] reused {reused} cached decrypted DBs from previous run", flush=True)
|
||||||
|
|
||||||
|
def _save_persistent_cache(self):
|
||||||
|
"""持久化缓存映射到磁盘"""
|
||||||
|
data = {}
|
||||||
|
for rel_key, (db_mt, wal_mt, path) in self._cache.items():
|
||||||
|
data[rel_key] = {"db_mt": db_mt, "wal_mt": wal_mt, "path": path}
|
||||||
|
try:
|
||||||
|
with open(self.MTIME_FILE, 'w') as f:
|
||||||
|
json.dump(data, f)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
def get(self, rel_key):
|
def get(self, rel_key):
|
||||||
if rel_key not in ALL_KEYS:
|
if rel_key not in ALL_KEYS:
|
||||||
|
|
@ -144,27 +193,19 @@ class DBCache:
|
||||||
c_db_mt, c_wal_mt, c_path = self._cache[rel_key]
|
c_db_mt, c_wal_mt, c_path = self._cache[rel_key]
|
||||||
if c_db_mt == db_mtime and c_wal_mt == wal_mtime and os.path.exists(c_path):
|
if c_db_mt == db_mtime and c_wal_mt == wal_mtime and os.path.exists(c_path):
|
||||||
return c_path
|
return c_path
|
||||||
try:
|
|
||||||
os.unlink(c_path)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
tmp_path = self._cache_path(rel_key)
|
||||||
enc_key = bytes.fromhex(ALL_KEYS[rel_key]["enc_key"])
|
enc_key = bytes.fromhex(ALL_KEYS[rel_key]["enc_key"])
|
||||||
fd, tmp_path = tempfile.mkstemp(suffix='.db')
|
|
||||||
os.close(fd)
|
|
||||||
full_decrypt(db_path, tmp_path, enc_key)
|
full_decrypt(db_path, tmp_path, enc_key)
|
||||||
if os.path.exists(wal_path):
|
if os.path.exists(wal_path):
|
||||||
decrypt_wal(wal_path, tmp_path, enc_key)
|
decrypt_wal(wal_path, tmp_path, enc_key)
|
||||||
self._cache[rel_key] = (db_mtime, wal_mtime, tmp_path)
|
self._cache[rel_key] = (db_mtime, wal_mtime, tmp_path)
|
||||||
|
self._save_persistent_cache()
|
||||||
return tmp_path
|
return tmp_path
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
for _, _, path in self._cache.values():
|
"""正常退出时保存缓存映射(不删文件,下次启动可复用)"""
|
||||||
try:
|
self._save_persistent_cache()
|
||||||
os.unlink(path)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
self._cache.clear()
|
|
||||||
|
|
||||||
|
|
||||||
_cache = DBCache()
|
_cache = DBCache()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue