Source code for edri.switch.forwarder

from dataclasses import dataclass
from enum import IntEnum
from logging import getLogger
from queue import Queue
from threading import Thread
from time import monotonic
from uuid import UUID

from edri.protocol.frame import ZERO_TARGET, FrameType
from edri.switch.connection import Connection


[docs] class ForwardType(IntEnum): SPECIFIC = 1 OTHERS = 2
[docs] @dataclass(frozen=True) class ForwardMessage: router_id: UUID kind: ForwardType payload: bytes source: UUID | None = None delivery_id: UUID | None = None source_connection: Connection | None = None
[docs] class Forwarder(Thread): def __init__(self, forwarder_queue: Queue[ForwardMessage], connections: dict[UUID, Connection]) -> None: super().__init__(daemon=True) self.forwarder_queue = forwarder_queue self.connections = connections self.logger = getLogger(__name__) # Bounded by the Router cache horizon: retries with the same transport # identifier must not enqueue a second copy after a lost source ACK. self._seen: dict[tuple[UUID, UUID], float] = {} self._last_cleanup = monotonic() def _accepted_before(self, source: UUID, delivery_id: UUID) -> bool: from edri.config.setting import CACHE_TIMEOUT now = monotonic() if now - self._last_cleanup > CACHE_TIMEOUT: self._seen = {k: t for k, t in self._seen.items() if now - t < CACHE_TIMEOUT * 2} self._last_cleanup = now key = (source, delivery_id) if key in self._seen: return True self._seen[key] = now return False
[docs] def run(self) -> None: while True: item = self.forwarder_queue.get() if item.source is not None and item.delivery_id is not None: key = (item.source, item.delivery_id) if key in self._seen: self._ack_source(item) continue if item.kind == ForwardType.SPECIFIC: connection = self.connections.get(item.router_id) if connection is None: self.logger.error("Unknown router id: %s; source delivery is unacknowledged", item.router_id) continue connection.mailbox.put(item.payload) else: for router_id, connection in list(self.connections.items()): if router_id != item.router_id: connection.mailbox.put(item.payload) if item.source is not None and item.delivery_id is not None: self._accepted_before(item.source, item.delivery_id) self._ack_source(item)
def _ack_source(self, item: ForwardMessage) -> None: assert item.source is not None and item.delivery_id is not None connection = item.source_connection if connection is None or self.connections.get(item.source) is not connection or connection.channel is None: return try: connection.channel.send(FrameType.ACK, item.delivery_id.bytes, b"") except Exception: self.logger.debug("Source ACK failed for %s; sender will retry", item.delivery_id, exc_info=True)