Source code for litestar_queues.execution.base

import logging
from dataclasses import dataclass
from typing import TYPE_CHECKING, Literal

from typing_extensions import Self

from litestar_queues.namespace import QueueNamespace

if TYPE_CHECKING:
    from datetime import timedelta
    from types import TracebackType

    from litestar_queues.config import QueueConfig
    from litestar_queues.models import QueuedTaskRecord
    from litestar_queues.service import QueueService

__all__ = (
    "BaseConsumerExecutionBackend",
    "BaseExecutionBackend",
    "DispatchRepairResult",
    "ExecutionCancelResult",
    "ExecutionCancelStatus",
)

_MESSAGING_SYSTEM = "litestar_queues"


def _queue_observability_attributes(operation: "str", record: "QueuedTaskRecord") -> "dict[str, object]":
    """Build the span attributes for one execution-backend operation.

    Returns:
        Span attributes describing the record and the operation.
    """
    attributes: "dict[str, object]" = {
        "messaging.system": _MESSAGING_SYSTEM,
        "messaging.operation.name": operation,
        "messaging.destination.name": record.queue,
        "queue.task.name": record.task_name,
        "queue.execution.backend": record.execution_backend,
    }
    if record.execution_profile:
        attributes["queue.execution.profile"] = record.execution_profile
    return attributes


def _queue_metric_attributes(record: "QueuedTaskRecord") -> "dict[str, str]":
    """Build the label set every execution-backend metric family carries.

    Shared rather than per-backend because a Prometheus collector is registered
    once per metric name and registry, label names included: a backend that adds
    or drops one key raises on the first recording instead of opening a second
    series. Every value here is bounded by the deployment's own configuration --
    queue names, task names, backend and profile selectors -- and the operation's
    outcome is the only key a caller adds.

    Returns:
        The shared metric labels for this record.
    """
    return {
        "messaging.destination.name": record.queue,
        "queue.task.name": record.task_name,
        "queue.execution.backend": record.execution_backend,
        "queue.execution.profile": record.execution_profile or "",
    }


[docs] @dataclass(frozen=True, slots=True) class DispatchRepairResult: """Outcome of one bounded delivery-repair pass. ``examined`` is how much of the caller's budget the pass consumed, whether or not a candidate needed anything done, so the caller can spend what is left on its other work. """ examined: "int" = 0 changed: "int" = 0
ExecutionCancelStatus = Literal["accepted", "already_cancelled", "retryable", "unsupported"]
[docs] @dataclass(frozen=True, slots=True) class ExecutionCancelResult: """Outcome of one provider-level cancellation attempt. ``detail`` carries the provider's own words for the log line and the lifecycle event; it is never parsed. """ status: "ExecutionCancelStatus" detail: "str | None" = None @property def permits_durable_cancel(self) -> "bool": """Whether this outcome may win the durable transition to cancelled. Only a control plane that exists and refused blocks the write. A transport with no control plane cannot strand anything, because the dispatch-by-id claim fence makes an in-flight delivery a no-op. Returns: True unless the provider refused a cancellation it could have made. """ return self.status != "retryable"
[docs] @classmethod def accepted(cls, detail: "str | None" = None) -> "Self": """Return an accepted cancellation result. Returns: A result whose status is ``accepted``. """ return cls(status="accepted", detail=detail)
[docs] @classmethod def already_cancelled(cls, detail: "str | None" = None) -> "Self": """Return an idempotent already-cancelled result. Returns: A result whose status is ``already_cancelled``. """ return cls(status="already_cancelled", detail=detail)
[docs] @classmethod def retryable(cls, detail: "str | None" = None) -> "Self": """Return a transient or rejected cancellation result. Returns: A result whose status is ``retryable``. """ return cls(status="retryable", detail=detail)
[docs] @classmethod def unsupported(cls, detail: "str | None" = None) -> "Self": """Return a result for a transport with no cancellation control plane. Returns: A result whose status is ``unsupported``. """ return cls(status="unsupported", detail=detail)
[docs] class BaseExecutionBackend: """Base class for queue execution backends.""" __slots__ = ("_logger", "config")
[docs] def __init__(self, config: "QueueConfig | None" = None) -> "None": """Initialize the execution backend.""" self.config = config names = config.names if config is not None else QueueNamespace() self._logger = logging.getLogger(names.logger("execution", type(self).__name__))
@property def is_external(self) -> "bool": """Whether this backend dispatches records to another process.""" return False @property def schedules_on_enqueue(self) -> "bool": """Whether the backend schedules a persisted record without a Worker. Deliberately separate from :attr:`is_external`. Cloud Run Jobs and the broker backends are external but still rely on a worker loop noticing a pending record; only a managed transport that accepts the record itself answers true here. """ return False @property def max_schedule_horizon(self) -> "timedelta | None": """How far ahead this backend will hold a scheduled delivery. ``None`` means unbounded, which is every backend that keeps due records in the queue store. A managed transport that takes ownership of the record has its own ceiling, and a schedule past it is not a call that fails once -- it is a recurrence that can never run. """ return None
[docs] async def schedule(self, service: "QueueService", record: "QueuedTaskRecord") -> "str | None": """Schedule one already-persisted record for external delivery. Returns: The external delivery reference, if one was created. """ return await self.dispatch(service, record)
[docs] async def repair(self, service: "QueueService", *, limit: "int") -> "DispatchRepairResult": """Recreate deliveries this backend owns that its transport no longer holds. A no-op for every backend whose records are found by polling: nothing can go missing from a store the worker reads directly. Only a managed transport that took ownership of the record can lose it silently. Args: service: The queue service whose records to repair. limit: Ceiling on how many records one pass may examine. Bounded maintenance is the only caller, and it always passes a positive budget it needs back. Returns: An empty result. """ return DispatchRepairResult()
[docs] async def open(self) -> "bool": """Open execution resources. Returns: True when resources are ready. """ return True
[docs] async def close(self) -> "None": """Close execution resources."""
[docs] async def execute( self, service: "QueueService", record: "QueuedTaskRecord", *, worker_id: "str | None" = None ) -> "QueuedTaskRecord": """Execute a queue record.""" raise NotImplementedError
[docs] async def dispatch(self, service: "QueueService", record: "QueuedTaskRecord") -> "str | None": """Dispatch a queue record to an external executor. Returns: The external execution reference, if one was created. """ await self.execute(service, record) return record.execution_ref
[docs] async def reconcile(self, service: "QueueService", record: "QueuedTaskRecord") -> "QueuedTaskRecord | None": """Reconcile an externally running queue record. Returns: The updated record when reconciliation changes state. """ return None
[docs] async def cancel_execution(self, service: "QueueService", record: "QueuedTaskRecord") -> "ExecutionCancelResult": """Cancel the provider resource backing one external attempt. Called before any durable transition to ``cancelled`` for a record whose ``execution_ref`` names a live provider resource. Implementations must contain their client's exceptions and answer with a result: a not-found resource is ``already_cancelled``, anything transient or refused is ``retryable``. Raising is reserved for programming errors. Returns: ``unsupported`` for a transport with no cancellation control plane. """ del service, record return ExecutionCancelResult.unsupported()
async def __aenter__(self) -> "Self": await self.open() return self async def __aexit__( self, exc_type: "type[BaseException] | None", # noqa: PYI036 exc_val: "BaseException | None", # noqa: PYI036 exc_tb: "TracebackType | None", # noqa: PYI036 ) -> "None": await self.close()
[docs] class BaseConsumerExecutionBackend(BaseExecutionBackend): """Base for external backends that continuously receive broker deliveries.""" __slots__ = ()
[docs] async def run_consumer(self, service: "QueueService", *, max_concurrency: "int", drain_timeout: "float") -> "None": """Receive and execute deliveries until cancelled.""" raise NotImplementedError