orchestrata_ultimate.postman.delivery
Reference › orchestrata_ultimate › postman › delivery
The delivery manager of the broker, SPEC-002 story US-001 (REQ-002-US-001-002 to -005) with queue transport from FR-004. DeliveryManager implements IDeliveryManager from interfaces and takes a validated MessageEnvelope and a MessagePayload through to one delivery receipt per recipient. It composes three collaborators, each replaceable by passing one in: the RoutingEngine to resolve recipients, the EnvelopeHandler to validate, and the QueueManager to provide queues. No other module imports it; the Postman facade has its own simpler path.
deliver(envelope, payload, mode) follows these steps. It extracts or generates a message id. _validate_envelope returns a failed DeliveryResult if the envelope handler rejects the envelope or raises. _queue_message records a queued status and keeps the envelope and payload in a pending table. _resolve_recipients asks the routing engine and turns routing errors into a failed result with _record_failed. The result is set to delivering, and _deliver_to_all calls _deliver_to_recipient for each recipient. That method gets, or lazily creates, an in-memory queue named for the recipient (_get_or_create_recipient_queue, tolerating a creation race) and enqueues a QueueEntry with the envelope's priority. The receipt is DELIVERED only after the queue accepts the entry; any exception gives a FAILED receipt carrying the error, so a message that never arrived is not reported as delivered. The aggregate result is delivered only if every recipient succeeded, and successful messages leave the pending table.
get_status returns the stored result for a message id. cancel works only on a message still queued and marks it failed with the reason "Cancelled by sender". retry re-delivers a failed message from the stored envelope and payload and raises DeliveryError if the message is unknown, not failed, or no longer stored. A lock guards the shared tables.
At a glance
| Source | src/orchestrata_ultimate/postman/delivery.py |
| Lines of code | 506 |
| Classes | 1 |
| Functions | 0 |
| Imports (internal) | orchestrata_ultimate.core.base.traces, orchestrata_ultimate.postman.envelope, orchestrata_ultimate.postman.exceptions, orchestrata_ultimate.postman.interfaces, orchestrata_ultimate.postman.queue_manager, orchestrata_ultimate.postman.router, orchestrata_ultimate.postman.types |
| Imported by | none |
| Tests that import it | tests/postman/test_cov100_postman.py, tests/postman/test_delivery.py, tests/postman/test_delivery_routing_engine_wiring.py, tests/postman/test_postman_interface_compliance.py, tests/postman/test_postman_signatures.py |
Class diagram
Dependency diagram
API
Classes
DeliveryManager
Bases: IDeliveryManager · Decorators: traces(spec='002', story='US-001', reqs=['REQ-002-US-001-002', 'REQ-002-US-001-003', 'REQ-002-US-001-004', 'REQ-002-US-… · Line 58
Manages message delivery lifecycle from sender to recipient(s).
| Method | Signature | Summary |
|---|---|---|
__init__ |
__init__(routing_engine: IRoutingEngine | None = None, envelope_handler: IMessageEnvelope | None = None, queue_manager: IQueueManager | None = None) -> None |
Initialize delivery manager with required dependencies. |
deliver |
async deliver(envelope: MessageEnvelope, payload: MessagePayload, mode: DeliveryMode = DeliveryMode.AT_LEAST_ONCE) -> DeliveryResult |
Deliver message to recipients according to envelope routing. |
_extract_message_id |
_extract_message_id(envelope: MessageEnvelope) -> str |
Extract or generate message ID from envelope. |
_validate_envelope |
_validate_envelope(envelope: MessageEnvelope, message_id: str, timestamp: datetime) -> DeliveryResult | None |
Validate envelope structure. |
_queue_message |
async _queue_message(message_id: str, envelope: MessageEnvelope, payload: MessagePayload, timestamp: datetime) -> None |
Queue message and record initial QUEUED status. |
_resolve_recipients |
async _resolve_recipients(message_id: str, envelope: MessageEnvelope) -> list[str] | DeliveryResult |
Resolve recipients via routing engine. |
_record_failed |
async _record_failed(message_id: str, error_msg: str) -> DeliveryResult |
Record a FAILED result and remove from pending. |
_deliver_to_all |
async _deliver_to_all(message_id: str, recipients: list[str], envelope: MessageEnvelope, payload: MessagePayload) -> DeliveryResult |
Deliver to all recipients and return final aggregated result. |
_get_or_create_recipient_queue |
async _get_or_create_recipient_queue(recipient: str) -> IQueue |
Get (or lazily create) the recipient's real delivery queue. |
_deliver_to_recipient |
async _deliver_to_recipient(message_id: str, recipient: str, envelope: MessageEnvelope, payload: MessagePayload) -> DeliveryReceipt |
Deliver message to a single recipient. |
get_status |
async get_status(message_id: str) -> DeliveryResult | None |
Get current delivery status for a message. |
cancel |
async cancel(message_id: str) -> bool |
Cancel pending message delivery. |
retry |
async retry(message_id: str) -> DeliveryResult |
Retry failed message delivery. |
Source
# mypy: ignore-errors
"""
Delivery Manager - Core delivery orchestration for Postman message broker.
This module implements the IDeliveryManager protocol, coordinating message
delivery from sender to recipient(s) using the routing engine and envelope
validation.
Author: Mohamed
"""
import asyncio
import logging
import uuid
from datetime import UTC, datetime
from orchestrata_ultimate.core.base.traces import traces
from orchestrata_ultimate.postman.envelope import EnvelopeHandler
from orchestrata_ultimate.postman.exceptions import (
DeliveryError,
QueueError,
RecipientNotFoundError,
)
from orchestrata_ultimate.postman.interfaces import (
IDeliveryManager,
IMessageEnvelope,
IQueue,
IQueueManager,
IRoutingEngine,
)
from orchestrata_ultimate.postman.queue_manager import QueueManager
from orchestrata_ultimate.postman.router import RoutingEngine
from orchestrata_ultimate.postman.types import (
DeliveryMode,
DeliveryReceipt,
DeliveryResult,
DeliveryStatus,
MessageEnvelope,
MessagePayload,
QueueConfig,
QueueEntry,
ReceiptType,
)
logger = logging.getLogger(__name__)
@traces(
spec="002",
story="US-001",
reqs=[
"REQ-002-US-001-002",
"REQ-002-US-001-003",
"REQ-002-US-001-004",
"REQ-002-US-001-005",
],
)
class DeliveryManager(IDeliveryManager):
"""
Manages message delivery lifecycle from sender to recipient(s).
Orchestrates the complete delivery process:
1. Validates envelope structure and headers
2. Resolves recipients via routing engine
3. Delivers message to each recipient
4. Tracks delivery status and generates receipts
5. Handles retries and cancellations
Thread-safe for concurrent delivery operations.
"""
def __init__(
self,
routing_engine: IRoutingEngine | None = None,
envelope_handler: IMessageEnvelope | None = None,
queue_manager: IQueueManager | None = None,
) -> None:
"""
Initialize delivery manager with required dependencies.
Args:
routing_engine: Engine for resolving message recipients. Defaults
to a fresh :class:`~orchestrata_ultimate.postman.router.RoutingEngine`
(SPEC-002 FR-003) when not supplied.
envelope_handler: Handler for envelope validation. Defaults to a
fresh :class:`~orchestrata_ultimate.postman.envelope.EnvelopeHandler`
(SPEC-002 FR-001) when not supplied.
queue_manager: Manager providing each recipient's real delivery
queue (SPEC-002 FR-004) -- the actual transport
``_deliver_to_recipient`` routes messages through. Defaults to
a fresh :class:`~orchestrata_ultimate.postman.queue_manager.QueueManager`
when not supplied.
"""
self._routing_engine: IRoutingEngine = routing_engine or RoutingEngine()
self._envelope_handler: IMessageEnvelope = envelope_handler or EnvelopeHandler()
self._queue_manager: IQueueManager = queue_manager or QueueManager()
# Delivery tracking state. Pending holds the original (envelope, payload)
# so a failed delivery can be retried with the real payload (not a
# placeholder). Cleared only on successful delivery.
self._deliveries: dict[str, DeliveryResult] = {}
self._pending: dict[str, tuple[MessageEnvelope, MessagePayload]] = {}
# Thread safety
self._lock = asyncio.Lock()
@traces(
spec="002",
story="US-001",
reqs=["REQ-002-US-001-002", "REQ-002-US-001-003", "REQ-002-US-001-005"],
)
async def deliver(
self,
envelope: MessageEnvelope,
payload: MessagePayload,
mode: DeliveryMode = DeliveryMode.AT_LEAST_ONCE,
) -> DeliveryResult:
"""
Deliver message to recipients according to envelope routing.
Process:
1. Validate envelope structure
2. Resolve recipients from TO/CC/BCC headers
3. Attempt delivery to each recipient
4. Generate delivery receipts
5. Track delivery status
Args:
envelope: Message envelope with routing headers
payload: Message payload to deliver
mode: Delivery guarantee mode (default: AT_LEAST_ONCE)
Returns:
DeliveryResult with success status, receipts, and any errors
Raises:
DeliveryError: If critical delivery error occurs
"""
message_id = self._extract_message_id(envelope)
timestamp = datetime.now(UTC)
# Step 1: Validate envelope
validation_result = self._validate_envelope(envelope, message_id, timestamp)
if validation_result is not None:
return validation_result
# Step 2: Queue message and update status
await self._queue_message(message_id, envelope, payload, timestamp)
# Step 3: Resolve recipients
recipients_result = await self._resolve_recipients(message_id, envelope)
if isinstance(recipients_result, DeliveryResult):
return recipients_result
recipients = recipients_result
# Step 4: Update status to DELIVERING
async with self._lock:
delivering_result = DeliveryResult(
message_id=message_id,
timestamp=datetime.now(UTC),
success=False,
status=DeliveryStatus.DELIVERING,
receipts=[],
)
self._deliveries[message_id] = delivering_result
# Step 5: Deliver to each recipient and generate final result
final_result = await self._deliver_to_all(
message_id, recipients, envelope, payload
)
# Step 6: Update tracking. Keep the pending (envelope, payload) when the
# delivery failed so retry() can re-deliver the real payload; clear it
# only on success.
async with self._lock:
self._deliveries[message_id] = final_result
if final_result.success:
self._pending.pop(message_id, None)
return final_result
@traces(spec="002", story="US-001", reqs=["REQ-002-US-001-005"])
def _extract_message_id(self, envelope: MessageEnvelope) -> str:
"""Extract or generate message ID from envelope.
Uses ``getattr`` rather than direct attribute access: real
``MessageEnvelope`` instances never carry a ``headers`` dict (it is
not a declared field), but some duck-typed callers still supply one.
"""
headers = getattr(envelope, "headers", None)
if headers:
return headers.get("MESSAGE_ID", str(uuid.uuid4()))
elif hasattr(envelope, "message_id") and envelope.message_id:
return envelope.message_id
return str(uuid.uuid4())
@traces(spec="002", story="FR-001", reqs=["REQ-002-FR1-014"])
def _validate_envelope(
self,
envelope: MessageEnvelope,
message_id: str,
timestamp: datetime,
) -> DeliveryResult | None:
"""Validate envelope structure. Returns DeliveryResult on failure, None on success."""
try:
is_valid, validation_error = self._envelope_handler.validate(envelope)
if not is_valid:
return DeliveryResult(
message_id=message_id,
timestamp=timestamp,
success=False,
status=DeliveryStatus.FAILED,
receipts=[],
error=f"Envelope validation failed: {validation_error}",
)
except Exception as e:
return DeliveryResult(
message_id=message_id,
timestamp=timestamp,
success=False,
status=DeliveryStatus.FAILED,
receipts=[],
error=f"Validation error: {str(e)}",
)
return None
@traces(spec="002", story="US-001", reqs=["REQ-002-US-001-005"])
async def _queue_message(
self,
message_id: str,
envelope: MessageEnvelope,
payload: MessagePayload,
timestamp: datetime,
) -> None:
"""Queue message and record initial QUEUED status."""
async with self._lock:
self._pending[message_id] = (envelope, payload)
self._deliveries[message_id] = DeliveryResult(
message_id=message_id,
timestamp=timestamp,
success=False,
status=DeliveryStatus.QUEUED,
receipts=[],
)
@traces(
spec="002",
story="US-001/FR-003",
reqs=["REQ-002-US-001-002", "REQ-002-FR3-010"],
)
async def _resolve_recipients(
self,
message_id: str,
envelope: MessageEnvelope,
) -> list[str] | DeliveryResult:
"""Resolve recipients via routing engine.
Returns list of recipients on success, or DeliveryResult on failure.
"""
try:
recipients = await self._routing_engine.resolve(envelope)
if not recipients:
return await self._record_failed(message_id, "No recipients found")
return recipients
except RecipientNotFoundError as e:
return await self._record_failed(
message_id, f"Recipient resolution failed: {str(e)}"
)
except Exception as e:
return await self._record_failed(message_id, f"Routing error: {str(e)}")
@traces(spec="002", story="US-001", reqs=["REQ-002-US-001-005"])
async def _record_failed(self, message_id: str, error_msg: str) -> DeliveryResult:
"""Record a FAILED result and remove from pending."""
async with self._lock:
failed_result = DeliveryResult(
message_id=message_id,
timestamp=datetime.now(UTC),
success=False,
status=DeliveryStatus.FAILED,
receipts=[],
error=error_msg,
)
self._deliveries[message_id] = failed_result
# Keep the pending (envelope, payload) so retry() can re-deliver.
return failed_result
@traces(spec="002", story="US-001", reqs=["REQ-002-US-001-002"])
async def _deliver_to_all(
self,
message_id: str,
recipients: list[str],
envelope: MessageEnvelope,
payload: MessagePayload,
) -> DeliveryResult:
"""Deliver to all recipients and return final aggregated result."""
receipts: list[DeliveryReceipt] = []
all_success = True
for recipient in recipients:
receipt = await self._deliver_to_recipient(
message_id=message_id,
recipient=recipient,
envelope=envelope,
payload=payload,
)
receipts.append(receipt)
if receipt.status != DeliveryStatus.DELIVERED:
all_success = False
final_status = (
DeliveryStatus.DELIVERED if all_success else DeliveryStatus.FAILED
)
return DeliveryResult(
message_id=message_id,
timestamp=datetime.now(UTC),
success=all_success,
status=final_status,
receipts=receipts,
error=None if all_success else "Some deliveries failed",
)
@traces(
spec="002",
story="FR-004",
reqs=["REQ-002-FR4-001"],
)
async def _get_or_create_recipient_queue(self, recipient: str) -> IQueue:
"""Get (or lazily create) the recipient's real delivery queue.
This is the actual transport ``_deliver_to_recipient`` routes messages
through (SPEC-002 FR-004): an in-memory queue keyed by recipient,
created on first use via the composed ``IQueueManager``. A concurrent
first delivery to the same recipient can race with queue creation;
losing that race just falls back to the queue the winner created.
Args:
recipient: Recipient agent ID the queue belongs to.
Returns:
The recipient's real ``IQueue`` instance.
"""
queue = await self._queue_manager.get_queue(recipient)
if queue is not None:
return queue
try:
return await self._queue_manager.create_queue(
recipient, QueueConfig(name=recipient, persistence_enabled=False)
)
except QueueError:
# Lost a race with a concurrent delivery to the same recipient.
existing = await self._queue_manager.get_queue(recipient)
if existing is None:
raise
return existing
@traces(
spec="002",
story="FR-004/FR-007",
reqs=["REQ-002-FR4-006", "REQ-002-FR7-002", "REQ-002-FR7-003"],
)
async def _deliver_to_recipient(
self,
message_id: str,
recipient: str,
envelope: MessageEnvelope,
payload: MessagePayload,
) -> DeliveryReceipt:
"""
Deliver message to a single recipient.
Actually enqueues the message onto the recipient's real delivery
queue (SPEC-002 FR-004, via the composed ``IQueueManager``) -- the
transport a real subscriber dequeues from. A receipt is only ever
DELIVERED once the queue confirms the entry was accepted; any
transport failure (queue full, queue unavailable, ...) is reported
FAILED so callers never see a false DELIVERED for a message that
never arrived.
Args:
message_id: Unique message identifier
recipient: Recipient address (e.g., "U:Cd", "U:PM")
envelope: Message envelope
payload: Message payload
Returns:
DeliveryReceipt with delivery status and metadata
"""
try:
queue = await self._get_or_create_recipient_queue(recipient)
entry = QueueEntry(message_id=message_id, priority=envelope.priority)
await queue.enqueue(entry)
# Generate successful receipt -- only once the entry actually
# landed in the recipient's real queue.
return DeliveryReceipt(
receipt_id=str(uuid.uuid4()),
message_id=message_id,
recipient=recipient,
timestamp=datetime.now(UTC),
status=DeliveryStatus.DELIVERED,
receipt_type=ReceiptType.DELIVERY,
)
except Exception as e:
# Real transport failure (queue full, queue unreachable, ...) --
# report FAILED, never a false DELIVERED.
logger.warning(f"Delivery to recipient {recipient} failed: {e}")
return DeliveryReceipt(
receipt_id=str(uuid.uuid4()),
message_id=message_id,
recipient=recipient,
timestamp=datetime.now(UTC),
status=DeliveryStatus.FAILED,
receipt_type=ReceiptType.DELIVERY,
error=str(e),
)
@traces(spec="002", story="US-001", reqs=["REQ-002-US-001-005"])
async def get_status(self, message_id: str) -> DeliveryResult | None:
"""
Get current delivery status for a message.
Args:
message_id: Unique message identifier
Returns:
DeliveryResult if message found, None otherwise
"""
async with self._lock:
return self._deliveries.get(message_id)
async def cancel(self, message_id: str) -> bool:
"""
Cancel pending message delivery.
Only cancels messages in QUEUED status. Messages already
in DELIVERING or DELIVERED status cannot be cancelled.
Args:
message_id: Unique message identifier
Returns:
True if cancelled successfully, False otherwise
"""
async with self._lock:
delivery = self._deliveries.get(message_id)
if not delivery:
return False
# Can only cancel queued messages
if delivery.status != DeliveryStatus.QUEUED:
return False
# Update status to FAILED with cancellation note
cancelled_result = DeliveryResult(
message_id=message_id,
timestamp=datetime.now(UTC),
success=False,
status=DeliveryStatus.FAILED,
receipts=[],
error="Cancelled by sender",
)
self._deliveries[message_id] = cancelled_result
self._pending.pop(message_id, None)
return True
@traces(spec="002", story="US-001", reqs=["REQ-002-US-001-004"])
async def retry(self, message_id: str) -> DeliveryResult:
"""
Retry failed message delivery.
Re-attempts delivery for failed messages using the original
envelope and payload (if still available).
Args:
message_id: Unique message identifier
Returns:
DeliveryResult with retry outcome
Raises:
DeliveryError: If message not found or not retryable
"""
async with self._lock:
delivery = self._deliveries.get(message_id)
pending = self._pending.get(message_id)
if not delivery:
raise DeliveryError(f"Message {message_id} not found")
if delivery.status != DeliveryStatus.FAILED:
raise DeliveryError(
f"Message {message_id} is not in failed status (current: {delivery.status})"
)
if not pending:
raise DeliveryError(
f"Original envelope/payload for message {message_id} no longer available"
)
# Retry delivery with the original, persisted envelope AND payload.
envelope, payload = pending
return await self.deliver(envelope, payload)