| |
| """Refresh TTL on existing RAG keys to match new persistence policy.""" |
|
|
| import subprocess |
| import sys |
|
|
|
|
| def redis_cli(*args): |
| result = subprocess.run( |
| ["docker", "exec", "rmi-redis", "redis-cli", "-a", REDIS_PASS, *args], |
| capture_output=True, |
| text=True, |
| ) |
| return result.stdout.strip() |
|
|
|
|
| def redis_eval(lua_script, numkeys, *keys_and_args): |
| """Run a Lua script via EVAL. Returns stdout.""" |
| all_args = ["EVAL", lua_script, str(numkeys), *list(keys_and_args)] |
| return redis_cli(*all_args) |
|
|
|
|
| |
| env_out = subprocess.run(["docker", "exec", "rmi-backend", "env"], capture_output=True, text=True).stdout |
| REDIS_PASS = "" |
| for line in env_out.splitlines(): |
| if line.startswith("REDIS_PASSWORD="): |
| REDIS_PASS = line.split("=", 1)[1] |
| break |
|
|
| if not REDIS_PASS: |
| print("ERROR: Could not find REDIS_PASSWORD") |
| sys.exit(1) |
|
|
| |
| TTL_POLICY = { |
| "scam_patterns": 0, |
| "contract_audits": 0, |
| "transaction_patterns": 0, |
| "forensic_reports": 0, |
| "known_scams": 0, |
| "wallet_profiles": 86400 * 365, |
| "market_intel": 86400 * 365, |
| "token_analysis": 86400 * 90, |
| "news_articles": 86400 * 30, |
| "general": 86400 * 30, |
| } |
|
|
| total_scanned = 0 |
| total_persisted = 0 |
| total_refreshed = 0 |
| errors = 0 |
|
|
| |
| |
| |
| LUA_BATCH = """ |
| local collection = ARGV[1] |
| local desired_ttl = tonumber(ARGV[2]) |
| local count = 0 |
| local persist_count = 0 |
| local refresh_count = 0 |
| |
| -- Use SCAN to iterate keys |
| local cursor = '0' |
| repeat |
| local reply = redis.call('SCAN', cursor, 'MATCH', 'rag:' .. collection .. ':*', 'COUNT', 500) |
| cursor = reply[1] |
| local keys = reply[2] |
| for i, key in ipairs(keys) do |
| count = count + 1 |
| local current_ttl = redis.call('TTL', key) |
| if desired_ttl == 0 then |
| -- Make permanent: only if not already persistent (-1) |
| if current_ttl ~= -1 then |
| redis.call('PERSIST', key) |
| persist_count = persist_count + 1 |
| end |
| else |
| -- Set expiry if currently persistent (-1) or TTL is shorter than desired |
| if current_ttl == -1 then |
| -- Already persistent, leave it (don't downgrade) |
| elseif current_ttl < desired_ttl then |
| redis.call('EXPIRE', key, desired_ttl) |
| refresh_count = refresh_count + 1 |
| end |
| end |
| end |
| until cursor == '0' |
| |
| return {count, persist_count, refresh_count} |
| """ |
|
|
| print("Refreshing RAG TTLs according to persistence policy...") |
| print() |
|
|
| for collection, desired_ttl in TTL_POLICY.items(): |
| result = redis_eval(LUA_BATCH, 0, collection, str(desired_ttl)) |
| |
| lines = [l.strip() for l in result.splitlines() if l.strip()] |
| if len(lines) == 3: |
| try: |
| scanned = int(lines[0].lstrip("[").rstrip(",")) |
| persisted = int(lines[1].rstrip(",")) |
| refreshed = int(lines[2].rstrip("]")) |
| except (ValueError, IndexError): |
| print(f" {collection}: unexpected result: {result}") |
| errors += 1 |
| continue |
|
|
| action = f"TTL={0} (persistent)" if desired_ttl == 0 else f"TTL={desired_ttl}s ({desired_ttl // 86400}d)" |
| print(f" {collection}: {scanned} keys scanned, {persisted} persisted, {refreshed} refreshed [{action}]") |
| total_scanned += scanned |
| total_persisted += persisted |
| total_refreshed += refreshed |
| else: |
| print(f" {collection}: unexpected result: {result}") |
| errors += 1 |
|
|
| print() |
| print( |
| f"Done: {total_scanned} keys scanned, {total_persisted} persisted (TTL removed), {total_refreshed} TTLs refreshed" |
| ) |
| if errors: |
| print(f"Errors: {errors}") |
|
|