"""Hazelcast Warm-Up from ClickHouse. Reconstructs critical HZ map state from ClickHouse after an HZ restart. Called on startup by MHS or as a standalone script. HZ is RAM-only volatile — every restart wipes all state. This module reads the last-known values from CH (the persistent audit trail) and pre-populates HZ maps so services don't start from zero. Maps reconstructed: DOLPHIN_FEATURES["exf_latest"] — from dolphin.exf_data DOLPHIN_FEATURES["acb_boost"] — from dolphin.acb_state DOLPHIN_FEATURES["mc_forewarner_latest"] — from dolphin.posture_events (mc_status) DOLPHIN_SAFETY["latest"] — from dolphin.posture_events DOLPHIN_HEARTBEAT["nautilus_flow_heartbeat"] — synthetic (marks warm-up) DOLPHIN_LIFECYCLE["state"] — lifecycle sentinel Usage: from hz_warmup import hz_warmup hz_warmup(hz_client) # call once after HZ client connects """ import json import logging import time import urllib.request from datetime import datetime, timezone log = logging.getLogger("hz_warmup") CH_URL = "http://localhost:8123" CH_USER = "dolphin" CH_PASS = "dolphin_ch_2026" CH_DB = "dolphin" def _ch_query(sql: str) -> list[dict]: """Run a CH query, return list of row dicts. Returns [] on failure.""" url = f"{CH_URL}/?database={CH_DB}&query={urllib.parse.quote(sql + ' FORMAT JSONEachRow')}" req = urllib.request.Request(url) req.add_header("X-ClickHouse-User", CH_USER) req.add_header("X-ClickHouse-Key", CH_PASS) try: with urllib.request.urlopen(req, timeout=5) as resp: body = resp.read().decode() return [json.loads(line) for line in body.strip().split("\n") if line.strip()] except Exception as e: log.warning("CH query failed: %s", e) return [] def _write_lifecycle(hz_client, state: str): """Write lifecycle sentinel to DOLPHIN_LIFECYCLE.""" m = hz_client.get_map("DOLPHIN_LIFECYCLE").blocking() m.put("state", json.dumps({ "state": state, "ts": datetime.now(timezone.utc).isoformat(), "pid": __import__("os").getpid(), })) log.info("LIFECYCLE: %s", state) def read_lifecycle_state(hz_client) -> str: """Read current lifecycle state. Returns 'UNKNOWN' if not set.""" try: m = hz_client.get_map("DOLPHIN_LIFECYCLE").blocking() raw = m.get("state") if raw: return json.loads(raw).get("state", "UNKNOWN") except Exception: pass return "UNKNOWN" import urllib.parse def hz_warmup(hz_client) -> dict: """Reconstruct HZ state from ClickHouse. Returns summary of what was restored.""" summary = {} _write_lifecycle(hz_client, "STARTING") features = hz_client.get_map("DOLPHIN_FEATURES").blocking() # 1. ExF latest rows = _ch_query("SELECT * FROM exf_data ORDER BY ts DESC LIMIT 1") if rows: r = rows[0] exf_payload = { "funding_rate_btc": r.get("funding_rate", 0), "funding_rate_eth": r.get("funding_rate", 0), "dvol": r.get("dvol", 0), "fear_greed": r.get("fear_greed", 0), "taker_buy_sell_ratio": r.get("taker_ratio", 0), "_ok_count": 5, "_acb_ready": True, "_ts": r.get("ts", ""), "_source": "ch_warmup", } features.put("exf_latest", json.dumps(exf_payload)) summary["exf_latest"] = True log.info("Restored exf_latest from CH (ts=%s)", r.get("ts")) # 2. ACB boost rows = _ch_query("SELECT * FROM acb_state ORDER BY ts DESC LIMIT 1") if rows: r = rows[0] acb_payload = { "boost": r.get("boost", 1.0), "beta": r.get("beta", 0.8), "signals": r.get("signals", 0), "_ts": r.get("ts", ""), "_source": "ch_warmup", } features.put("acb_boost", json.dumps(acb_payload)) summary["acb_boost"] = True log.info("Restored acb_boost from CH (boost=%.2f)", r.get("boost", 0)) # 3. Posture (DOLPHIN_SAFETY) rows = _ch_query("SELECT * FROM posture_events ORDER BY ts DESC LIMIT 1") if rows: r = rows[0] safety_payload = { "posture": r.get("posture", "APEX"), "Rm": r.get("rm", 0.97), "timestamp": r.get("ts", ""), "breakdown": {}, "_source": "ch_warmup", } safety_map = hz_client.get_map("DOLPHIN_SAFETY").blocking() safety_map.put("latest", json.dumps(safety_payload)) summary["posture"] = r.get("posture") log.info("Restored posture from CH: %s (Rm=%.3f)", r.get("posture"), r.get("rm", 0)) # 4. Heartbeat (synthetic — marks that warm-up populated state) hb_map = hz_client.get_map("DOLPHIN_HEARTBEAT").blocking() hb_payload = { "ts": time.time(), "iso": datetime.now(timezone.utc).isoformat(), "phase": "warmup", "flow": "hz_warmup", } hb_map.put("nautilus_flow_heartbeat", json.dumps(hb_payload)) summary["heartbeat"] = True # 5. MC-Forewarner (if available) rows = _ch_query(""" SELECT ts, posture, rm, JSONExtractString(trigger, 'Cat2') as cat2 FROM posture_events ORDER BY ts DESC LIMIT 1 """) # MC status isn't directly in CH — use a safe default mc_payload = { "status": "GREEN", "catastrophic_prob": 0.05, "envelope_score": 0.95, "timestamp": datetime.now(timezone.utc).isoformat(), "_source": "ch_warmup_default", } features.put("mc_forewarner_latest", json.dumps(mc_payload)) summary["mc_forewarner"] = True _write_lifecycle(hz_client, "READY") log.info("HZ warm-up complete: %s", summary) return summary if __name__ == "__main__": """Standalone warm-up — run after HZ container starts.""" import hazelcast logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s") client = hazelcast.HazelcastClient(cluster_name="dolphin", cluster_members=["localhost:5701"]) result = hz_warmup(client) print(json.dumps(result, indent=2)) client.shutdown()