Coverage for src / lexigram / ai / relay / gateway / job_registry.py: 100%
29 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-08 23:08 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-08 23:08 +0800
1"""In-memory relay job registry for job-based media generation.
3Maps a gateway-issued job handle (a UUID generated at insertion) to a
4``RelayJobRecord`` describing which channel owns the upstream job and
5what the upstream's own job id is. Records are evicted lazily on
6lookup once older than the configured ``job_ttl_seconds``; an expired
7or unknown handle returns ``None`` so callers can treat both as the
8same not-found outcome.
10Note:
11 The registry is in-memory only: a gateway process restart loses
12 all in-flight job mappings, and a caller polling a lost job
13 receives the same not-found outcome as an unknown id. A durable
14 job store is an explicit v1 non-goal (see the async job-relay
15 plan).
16"""
18from __future__ import annotations
20from collections.abc import Callable
21from dataclasses import dataclass
22import time
24from lexigram.identity.ambient import new_uuid
26__all__ = ["RelayJobRecord", "RelayJobRegistry"]
29@dataclass(frozen=True, slots=True)
30class RelayJobRecord:
31 """One in-flight relayed job's channel-affinity mapping.
33 Attributes:
34 channel_name: Name of the channel that owns the upstream job.
35 Every poll for this job routes to this same channel.
36 upstream_job_id: The job id issued by the upstream provider;
37 never exposed to the caller.
38 endpoint_kind: The endpoint kind the job was submitted through
39 (e.g. ``"video_generation"``).
40 created_at: Monotonic timestamp of the record; the registry
41 evicts records older than the configured TTL.
42 """
44 channel_name: str
45 upstream_job_id: str
46 endpoint_kind: str
47 created_at: float
50class RelayJobRegistry:
51 """Maps gateway job ids to relay job records with TTL eviction.
53 The gateway job id is generated by this registry at ``put`` time —
54 callers never choose it — so a caller-visible handle can never
55 collide with an upstream-issued job id.
57 Attributes:
58 _job_ttl_seconds: Age after which a record is evicted on the
59 next lookup.
60 _clock: Monotonic clock used for eviction decisions; tests
61 inject a fake.
62 _records: Gateway job id to record mapping.
63 """
65 def __init__(
66 self,
67 job_ttl_seconds: int,
68 *,
69 clock: Callable[[], float] = time.monotonic,
70 ) -> None:
71 """Bind the registry to a TTL and clock.
73 Args:
74 job_ttl_seconds: Age in seconds after which a record is
75 evicted on the next ``get``. The configuration layer
76 validates this is positive.
77 clock: Callable returning the current monotonic time.
78 Defaults to ``time.monotonic``; tests inject a fake.
79 """
80 self._job_ttl_seconds = job_ttl_seconds
81 self._clock = clock
82 self._records: dict[str, RelayJobRecord] = {}
84 def put(self, record: RelayJobRecord) -> str:
85 """Store *record* under a fresh gateway-issued job id.
87 Args:
88 record: The channel-affinity record to store.
90 Returns:
91 The gateway job id the caller can hand back to consumers
92 for status polling.
93 """
94 gateway_job_id = new_uuid()
95 self._records[gateway_job_id] = record
96 return gateway_job_id
98 def get(self, gateway_job_id: str) -> RelayJobRecord | None:
99 """Return the record for *gateway_job_id*, evicting when stale.
101 An expired record is removed from storage (not merely hidden)
102 so memory does not grow unbounded across many expired lookups.
104 Args:
105 gateway_job_id: The gateway-issued job id from ``put``.
107 Returns:
108 The stored record when it exists and is not older than
109 ``job_ttl_seconds``, otherwise ``None`` (both unknown and
110 expired ids behave identically).
111 """
112 record = self._records.get(gateway_job_id)
113 if record is None:
114 return None
115 if self._clock() - record.created_at > self._job_ttl_seconds:
116 del self._records[gateway_job_id]
117 return None
118 return record