Spaces:
Paused
Paused
Download sensorflow/evaluation/queue.py from whassan/SensorflowStudio: direct link, hf CLI and curl.
- Browser
- Download file 6.01 kB
-
https://huggingface.co/spaces/whassan/SensorflowStudio/resolve/main/sensorflow/evaluation/queue.py
- Command line
-
hf download hf://spaces/whassan/SensorflowStudio/sensorflow/evaluation/queue.py
-
curl -L -o queue.py https://huggingface.co/spaces/whassan/SensorflowStudio/resolve/main/sensorflow/evaluation/queue.py
6.01 kB
| """Queue abstraction between ingestion and evaluation workers. | |
| The platform is queue-first but transport-agnostic: EventQueue is the interface, | |
| InMemoryEventQueue is the fully-working default, RedisEventQueue is used when a | |
| redis client is importable/reachable, and KafkaEventQueue is an | |
| interface-compatible stub kept for deployment parity (Kafka is not bundled). | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import threading | |
| import time | |
| from collections import defaultdict, deque | |
| from typing import Any, Dict, List, Optional | |
| class EventQueue: | |
| """Abstract message/job queue keyed by topic.""" | |
| backend_name = "abstract" | |
| def publish(self, topic: str, message: Dict[str, Any]) -> None: | |
| raise NotImplementedError | |
| def consume(self, topic: str, max_messages: int = 100) -> List[Dict[str, Any]]: | |
| raise NotImplementedError | |
| def ack(self, topic: str, count: int, failed: int = 0) -> None: | |
| raise NotImplementedError | |
| def stats(self) -> Dict[str, Any]: | |
| raise NotImplementedError | |
| def reset(self) -> None: | |
| raise NotImplementedError | |
| class InMemoryEventQueue(EventQueue): | |
| """Default queue: thread-safe deque per topic with throughput accounting.""" | |
| backend_name = "in_memory" | |
| def __init__(self) -> None: | |
| self.lock = threading.RLock() | |
| self.topics: Dict[str, deque] = defaultdict(deque) | |
| self.processing = 0 | |
| self.completed = 0 | |
| self.failed = 0 | |
| self._window: deque = deque(maxlen=500) # (ts, count) for throughput | |
| def publish(self, topic: str, message: Dict[str, Any]) -> None: | |
| with self.lock: | |
| self.topics[topic].append(message) | |
| def consume(self, topic: str, max_messages: int = 100) -> List[Dict[str, Any]]: | |
| out = [] | |
| with self.lock: | |
| q = self.topics[topic] | |
| while q and len(out) < max_messages: | |
| out.append(q.popleft()) | |
| self.processing += len(out) | |
| return out | |
| def ack(self, topic: str, count: int, failed: int = 0) -> None: | |
| with self.lock: | |
| self.processing = max(0, self.processing - count - failed) | |
| self.completed += count | |
| self.failed += failed | |
| self._window.append((time.time(), count)) | |
| def stats(self) -> Dict[str, Any]: | |
| with self.lock: | |
| depth = {t: len(q) for t, q in self.topics.items() if len(q) > 0} | |
| pending = sum(len(q) for q in self.topics.values()) | |
| now = time.time() | |
| recent = [(ts, c) for ts, c in self._window if now - ts <= 5.0] | |
| throughput = sum(c for _, c in recent) / 5.0 if recent else 0.0 | |
| return { | |
| "backend": self.backend_name, | |
| "pending": pending, | |
| "processing": self.processing, | |
| "completed": self.completed, | |
| "failed": self.failed, | |
| "throughput_per_s": round(throughput, 1), | |
| "depth_by_topic": depth, | |
| } | |
| def reset(self) -> None: | |
| with self.lock: | |
| self.topics.clear() | |
| self.processing = 0 | |
| self.completed = 0 | |
| self.failed = 0 | |
| self._window.clear() | |
| class RedisEventQueue(InMemoryEventQueue): | |
| """Redis-backed queue. Falls back cleanly if redis is unavailable. | |
| Uses Redis lists per topic; counters are kept locally (single-process dev | |
| deployment). Only constructed when the redis package imports and pings. | |
| """ | |
| backend_name = "redis" | |
| def __init__(self, url: str = "redis://localhost:6379/0") -> None: | |
| super().__init__() | |
| import redis # noqa: F401 (raises ImportError when unavailable) | |
| self.client = redis.Redis.from_url(url, socket_connect_timeout=0.5) | |
| self.client.ping() | |
| self.prefix = "labeleval:queue:" | |
| def publish(self, topic: str, message: Dict[str, Any]) -> None: | |
| self.client.rpush(self.prefix + topic, json.dumps(message)) | |
| def consume(self, topic: str, max_messages: int = 100) -> List[Dict[str, Any]]: | |
| out = [] | |
| for _ in range(max_messages): | |
| raw = self.client.lpop(self.prefix + topic) | |
| if raw is None: | |
| break | |
| out.append(json.loads(raw)) | |
| with self.lock: | |
| self.processing += len(out) | |
| return out | |
| def stats(self) -> Dict[str, Any]: | |
| base = super().stats() | |
| depth = {} | |
| for key in self.client.scan_iter(self.prefix + "*"): | |
| name = key.decode().replace(self.prefix, "") | |
| depth[name] = self.client.llen(key) | |
| base["pending"] = sum(depth.values()) | |
| base["depth_by_topic"] = depth | |
| base["backend"] = self.backend_name | |
| return base | |
| def reset(self) -> None: | |
| for key in self.client.scan_iter(self.prefix + "*"): | |
| self.client.delete(key) | |
| super().reset() | |
| class KafkaEventQueue(EventQueue): | |
| """Interface-compatible Kafka stub. | |
| Kafka is not bundled with this repo; this class documents the production | |
| transport and raises a clear error if selected without a broker client. | |
| """ | |
| backend_name = "kafka" | |
| def __init__(self, bootstrap_servers: str = "localhost:9092") -> None: | |
| self.bootstrap_servers = bootstrap_servers | |
| raise RuntimeError( | |
| "KafkaEventQueue is a deployment stub: install/configure a Kafka client " | |
| "and broker, or use InMemoryEventQueue (default) / RedisEventQueue." | |
| ) | |
| def make_queue(preferred: str = "auto") -> EventQueue: | |
| """Build the best available queue. Never hard-codes to Kafka.""" | |
| if preferred in ("auto", "redis"): | |
| try: | |
| return RedisEventQueue() | |
| except Exception: | |
| if preferred == "redis": | |
| pass # fall through to in-memory with a working queue | |
| if preferred == "kafka": | |
| try: | |
| return KafkaEventQueue() | |
| except Exception: | |
| pass | |
| return InMemoryEventQueue() | |