95 lines
3.0 KiB
Python
95 lines
3.0 KiB
Python
|
|
"""Shared Hazelcast push utility for ExF/EsoF live daemons.
|
||
|
|
ULTRA-PERFORMANT: Zero-allocation fast path, minimal logging.
|
||
|
|
"""
|
||
|
|
import json
|
||
|
|
import logging
|
||
|
|
import time
|
||
|
|
from datetime import datetime, timezone
|
||
|
|
from typing import Any, Dict, Optional, Tuple
|
||
|
|
|
||
|
|
# Module-level logger
|
||
|
|
logger = logging.getLogger(__name__)
|
||
|
|
|
||
|
|
# Pre-compute level check for speed
|
||
|
|
_LOG_DEBUG = logger.isEnabledFor(logging.DEBUG)
|
||
|
|
_LOG_INFO = logger.isEnabledFor(logging.INFO)
|
||
|
|
_LOG_WARNING = logger.isEnabledFor(logging.WARNING)
|
||
|
|
|
||
|
|
# Constants
|
||
|
|
HZ_CLUSTER = "dolphin"
|
||
|
|
HZ_MEMBER = "localhost:5701"
|
||
|
|
HZ_MAP_NAME = "DOLPHIN_FEATURES"
|
||
|
|
MAX_CONNECT_RETRIES = 3
|
||
|
|
CONNECT_RETRY_DELAY = 1.0
|
||
|
|
|
||
|
|
# Pre-allocate timestamp format string cache
|
||
|
|
_TS_FORMAT = "%Y-%m-%dT%H:%M:%S.%f"
|
||
|
|
|
||
|
|
|
||
|
|
def make_hz_client(max_retries: int = MAX_CONNECT_RETRIES, retry_delay: float = CONNECT_RETRY_DELAY):
|
||
|
|
"""Create and return a connected HazelcastClient with retry logic."""
|
||
|
|
import hazelcast
|
||
|
|
|
||
|
|
last_error = None
|
||
|
|
for attempt in range(1, max_retries + 1):
|
||
|
|
try:
|
||
|
|
client = hazelcast.HazelcastClient(
|
||
|
|
cluster_name=HZ_CLUSTER,
|
||
|
|
cluster_members=[HZ_MEMBER],
|
||
|
|
connection_timeout=5.0,
|
||
|
|
)
|
||
|
|
if _LOG_INFO:
|
||
|
|
logger.info("HZ connected (attempt %d)", attempt)
|
||
|
|
return client
|
||
|
|
except Exception as e:
|
||
|
|
last_error = e
|
||
|
|
if _LOG_WARNING:
|
||
|
|
logger.warning("HZ connect attempt %d/%d failed", attempt, max_retries)
|
||
|
|
if attempt < max_retries:
|
||
|
|
time.sleep(retry_delay * attempt)
|
||
|
|
|
||
|
|
raise last_error
|
||
|
|
|
||
|
|
|
||
|
|
def hz_push(imap_key: str, data: Dict[str, Any], client) -> bool:
|
||
|
|
"""
|
||
|
|
Serialise `data` to JSON and put it on DOLPHIN_FEATURES[imap_key].
|
||
|
|
ZERO-ALLOCATION FAST PATH: No logging on success, minimal work.
|
||
|
|
Returns True on success, False on error.
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
# Inline dict copy and timestamp to avoid function calls
|
||
|
|
payload = {
|
||
|
|
**data,
|
||
|
|
"_pushed_at": datetime.now(timezone.utc).isoformat(),
|
||
|
|
"_push_seq": int(time.time() * 1000)
|
||
|
|
}
|
||
|
|
|
||
|
|
# Single call chain - no intermediates
|
||
|
|
client.get_map(HZ_MAP_NAME).blocking().put(imap_key, json.dumps(payload))
|
||
|
|
|
||
|
|
if _LOG_DEBUG:
|
||
|
|
logger.debug("HZ push ok [%s]", imap_key)
|
||
|
|
return True
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
# Only log errors - success path is silent
|
||
|
|
if _LOG_WARNING:
|
||
|
|
logger.warning("HZ push fail [%s]: %s", imap_key, type(e).__name__)
|
||
|
|
return False
|
||
|
|
|
||
|
|
|
||
|
|
def hz_push_fast(imap_key: str, data: Dict[str, Any], client, _ts: datetime = None) -> bool:
|
||
|
|
"""
|
||
|
|
ULTRA-FAST VARIANT: Pre-computed timestamp for batch operations.
|
||
|
|
Caller provides timestamp to avoid repeated datetime.now() calls.
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
payload = data.copy()
|
||
|
|
payload["_pushed_at"] = _ts.isoformat() if _ts else datetime.now(timezone.utc).isoformat()
|
||
|
|
|
||
|
|
client.get_map(HZ_MAP_NAME).blocking().put(imap_key, json.dumps(payload))
|
||
|
|
return True
|
||
|
|
except Exception:
|
||
|
|
return False
|