Files
rune/shared/tier_manager.py
T

179 lines
5.8 KiB
Python

#!/usr/bin/env python3
"""tier_manager.py — Tiered storage management for unbounded memory.
Hot → warm → cold based on access patterns.
Entries NEVER get deleted — they get demoted to colder tiers.
Cold storage is compressed, not indexed, not searched — but still there.
Accessed again → promoted back to warm/hot.
Run under ~/.venvs/memory/bin/python3.
"""
import json
import os
import sys
from datetime import datetime, timezone, timedelta
from typing import Dict, Optional
# Tier thresholds (days since last access)
HOT_THRESHOLD = 7 # accessed within 7 days = hot
WARM_THRESHOLD = 30 # accessed within 30 days = warm
# > 30 days = cold
# Tier storage characteristics
TIER_CONFIG = {
"hot": {
"indexed": True,
"cached": True,
"compressed": False,
"searched": True,
"description": "Instant access, indexed, cached",
},
"warm": {
"indexed": True,
"cached": False,
"compressed": False,
"searched": True,
"description": "Fast access, indexed, not cached",
},
"cold": {
"indexed": False,
"cached": False,
"compressed": True,
"searched": False,
"description": "Slow access, compressed, not searched",
},
}
class TierManager:
"""Manages tiered storage for entries based on access patterns."""
def __init__(self):
pass
def get_tier(self, entry: dict) -> str:
"""Determine the tier for an entry based on its access patterns.
Returns 'hot', 'warm', or 'cold'.
"""
wash_count = entry.get("wash_count", 0) or 0
last_accessed = entry.get("last_accessed")
rock_status = entry.get("rock_status", "washable")
# Rock/locked entries are always hot
if rock_status == "rock":
return "hot"
# High proven_uses = hot
if wash_count >= 20:
return "hot"
# Check recency
if last_accessed:
try:
last = datetime.fromisoformat(last_accessed)
hours_since = (datetime.now(timezone.utc) - last).total_seconds() / 3600
if hours_since < HOT_THRESHOLD * 24:
return "hot"
elif hours_since < WARM_THRESHOLD * 24:
return "warm"
else:
return "cold"
except (ValueError, TypeError):
pass
# Default: warm (new entries start here)
return "warm"
def promote(self, entry: dict) -> str:
"""Promote an entry to a higher tier. Returns new tier."""
current = self.get_tier(entry)
if current == "cold":
return "warm"
elif current == "warm":
return "hot"
return "hot" # already hot
def demote(self, entry: dict) -> str:
"""Demote an entry to a lower tier. Returns new tier."""
current = self.get_tier(entry)
rock_status = entry.get("rock_status", "washable")
# Never demote rock/locked entries
if rock_status == "rock":
return "hot"
if current == "hot":
return "warm"
elif current == "warm":
return "cold"
return "cold" # already cold
def demotion_sweep(self, entry: dict) -> str:
"""Sweep an entry for demotion. Returns the tier it should be in.
Same as get_tier but explicitly named for the sweep operation.
"""
return self.get_tier(entry)
def get_tier_config(self, tier: str) -> dict:
"""Get the configuration for a tier."""
return TIER_CONFIG.get(tier, TIER_CONFIG["warm"])
def should_search(self, tier: str) -> bool:
"""Whether entries in this tier should be searched."""
return TIER_CONFIG.get(tier, {}).get("searched", False)
def should_compress(self, tier: str) -> bool:
"""Whether entries in this tier should be compressed."""
return TIER_CONFIG.get(tier, {}).get("compressed", False)
# ═══════════════════════════════════════════════════════════════════════════════
# CLI
# ═══════════════════════════════════════════════════════════════════════════════
def main():
import argparse
parser = argparse.ArgumentParser(description="Tier manager for unbounded memory")
sub = parser.add_subparsers(dest="cmd")
sub.add_parser("config", help="Show tier configuration")
p_tier = sub.add_parser("tier", help="Get tier for an entry")
p_tier.add_argument("--wash-count", type=int, default=0)
p_tier.add_argument("--last-accessed", default=None)
p_tier.add_argument("--rock-status", default="washable")
args = parser.parse_args()
if not args.cmd:
parser.print_help()
sys.exit(1)
tm = TierManager()
if args.cmd == "config":
print("Tier Configuration:")
for tier, config in TIER_CONFIG.items():
print(f" {tier}: {config['description']}")
print(f" indexed={config['indexed']}, cached={config['cached']}, compressed={config['compressed']}, searched={config['searched']}")
elif args.cmd == "tier":
entry = {
"wash_count": args.wash_count,
"last_accessed": args.last_accessed,
"rock_status": args.rock_status,
}
tier = tm.get_tier(entry)
config = tm.get_tier_config(tier)
print(f"Entry: wash_count={args.wash_count}, rock_status={args.rock_status}")
print(f"Tier: {tier}")
print(f"Config: {config['description']}")
if __name__ == "__main__":
main()