Adding the Initial WIP

This commit is contained in:
Ketan Patel committed 2025-10-14 20:09:28 -04:00
commit 326479c338
25 files changed
+6629

No files matched your search

+76
View File
@@ -0,0 +1,76 @@
from __future__ import annotations
import asyncio
import time
from typing import Dict, Any, List
import redis
import json
import os
REDIS_URL = os.environ.get("REDIS_URL", "redis://redis:6379/0")
RETENTION_SECONDS = 7 * 24 * 3600
MAX_SAMPLES_PER_UPS = 7 * 24 * 60 * 2 # assume worst-case 30s interval -> ~20160 entries
_redis: redis.Redis | None = None
def get_redis() -> redis.Redis:
global _redis
if _redis:
return _redis
_redis = redis.Redis.from_url(REDIS_URL, decode_responses=True)
return _redis
SNAP_KEY_PREFIX = "ups:snap:" # latest hash per ups
HIST_KEY_PREFIX = "ups:hist:" # time-series list per ups (append JSON)
async def store_snapshot(ups_name: str, data: Dict[str, Any]):
r = get_redis()
ts = int(time.time())
pipe = r.pipeline()
# store latest snapshot (hash)
pipe.hset(f"{SNAP_KEY_PREFIX}{ups_name}", mapping={**data, "_ts": ts})
# append to history list
hist_key = f"{HIST_KEY_PREFIX}{ups_name}"
pipe.rpush(hist_key, json.dumps({"ts": ts, "data": data}))
pipe.ltrim(hist_key, -MAX_SAMPLES_PER_UPS, -1)
# add pruning via async task (length-based + time-based)
pipe.execute()
async def get_latest(ups_name: str) -> Dict[str, Any] | None:
r = get_redis()
h = r.hgetall(f"{SNAP_KEY_PREFIX}{ups_name}")
return h or None
async def get_history(ups_name: str, since_seconds: int = RETENTION_SECONDS) -> List[Dict[str, Any]]:
r = get_redis()
key = f"{HIST_KEY_PREFIX}{ups_name}"
raw = r.lrange(key, 0, -1)
now = int(time.time())
out: List[Dict[str, Any]] = []
for item in raw:
try:
obj = json.loads(item)
except json.JSONDecodeError:
continue
if now - obj.get("ts", 0) <= since_seconds:
out.append(obj)
return out
async def prune_old():
r = get_redis()
now = int(time.time())
cutoff = now - RETENTION_SECONDS
for key in r.scan_iter(f"{HIST_KEY_PREFIX}*"):
# prune from left while older than cutoff
while True:
item = r.lindex(key, 0)
if not item:
break
try:
obj = json.loads(item)
except json.JSONDecodeError:
r.lpop(key)
continue
if obj.get("ts", 0) < cutoff:
r.lpop(key)
else:
break