This page documents the public surface of omni-box — everything re-exported from the top-level package and the most useful submodules. For an auto-generated, source-of-truth listing run:
import omni_box
print(sorted(omni_box.__all__))
print(omni_box.__version__)Imported from omni_box (defined in omni_box.core.models).
BaseEvent— common fields:id,event_type,payload,headers,status,attempts_made,max_attempts,last_error, timing (created_at,scheduled_at,completed_at), locking (locked_at,locked_by), tracing (trace_id,correlation_id,causation_id,idempotency_key,schema_version). There is noupdated_aton the entity — that column lives on the ORM mixin (EventMixin) only.OutboxEvent— addsaggregate_type,aggregate_id,topic,partition_key.InboxEvent— addsmessage_id,consumer_group,source.BaseEventSchema— Pydantic schema base used by ad-hoc payload models.EventStatus(StrEnum) —PENDING = "pending",COMPLETED = "completed",FAILED = "failed".
Factory for validated event entities and the source of truth for lock/transition rules.
create_outbox_event(aggregate_type, aggregate_id, event_type, topic, partition_key, payload, *, headers=None, idempotency_key=None, trace_id=None, correlation_id=None, causation_id=None, schema_version=None, max_attempts=None, scheduled_at=None) -> OutboxEventcreate_inbox_event(message_id, consumer_group, source, event_type, payload, *, headers=None, trace_id=None, correlation_id=None, causation_id=None, schema_version=None) -> InboxEventlock_event(event, worker_id, locked_at),refresh_event_lock,unlock_event,force_unlock_eventmark_event_completed,mark_event_failed(supportscount_as_attempt,next_retry_at)is_lock_stale(event, now, stale_timeout_seconds=None) -> bool
Operational helpers. Requires a repository that implements SupportsRetentionPolicies.
release_stale_locks(stale_timeout_seconds) -> intcleanup_old_events(retention_days, batch_size=..., max_iterations=...) -> int
From omni_box.core.protocols.
- Repositories:
EventRepository[T],OutboxEventRepository,InboxEventRepository,FetchFilters,RepositoryCapabilities. - Capabilities:
SupportsBulkOperations[T],SupportsDistributedLocking[T],SupportsRetentionPolicies. - Broker:
EventPublisher,EventConsumer,ConsumedMessage,AckHandle/NullAckHandle,EnvelopeParser,EnvelopeData,InboxHandler. - Transaction providers:
InboxTransactionProviderProtocol,OutboxTransactionProviderProtocol(underomni_box.core.protocols.transaction). - Metrics:
InboxMetrics,OutboxMetrics,ProcessingMetrics(underomni_box.core.protocols.metrics).
From omni_box.core.pipeline.
Fluent builder. Picks DistributedLockingFetchStrategy + BulkCommitStrategy automatically when the repository advertises the matching capabilities; falls back to OptimisticLockingFetchStrategy + SingleCommitStrategy.
| Method | Description |
|---|---|
add_step(step) |
Append a ProcessingStep[T]. |
with_fetch_strategy(strategy) |
Override the auto-picked fetch strategy. |
with_commit_strategy(strategy) |
Override the auto-picked commit strategy. |
with_metrics(metrics) |
Wire a ProcessingMetrics collector. Steps reach it as ProcessingContext.metrics. |
with_lease_ttl(seconds) |
Lock TTL used by DistributedLockingFetchStrategy. |
with_job_name(name) |
Logging/metrics label. |
build() |
Returns an EventBatchProcessor[T]. |
process_batch(worker_id, batch_size, shutdown_requested_func=None, **fetch_filters) -> BatchProcessingResultBatchProcessingResultcarriesprocessed_event_ids,failed_counted,failed_noncounted,remaining_event_ids,commit_failed.shutdown_requested_funcis polled before the fetch and before every event. Once it returnsTruethe batch stops: what was already processed is committed, and the events left untouched come back inremaining_event_ids. Asked before the fetch, it locks nothing at all.
| Step | Purpose | Notes |
|---|---|---|
HandlerExecutionStep |
Runs the user handler inside the pipeline with a timeout. | Required terminal step in every processor. A TransientError out of the handler is recorded with count_as_attempt=False and rescheduled; a timeout counts. |
PublisherExecutionStep |
The outbox's handler step; installed by create_outbox_processor. |
Subclasses HandlerExecutionStep. The publish timeout is transient too, and the first transient failure ends the batch's publishing — the remaining events are recorded the same way, unpublished, with their schedule untouched. |
SiblingDeduplicationStep |
Skips an InboxEvent if a sibling row with the same (message_id, consumer_group) is already completed. |
Calls InboxEventRepository.has_completed_sibling_for_inbox_key. A no-op on a non-partitioned table — see storage adapters. |
MetricsStep |
Pushes batch lifecycle counters into an InboxMetrics / OutboxMetrics sink. |
|
OpenTelemetryStep(service_name=...) |
Creates spans for each batch/event. | Requires opentelemetry extra. |
CircuitBreakerStep(failure_threshold, recovery_timeout_seconds) |
Stops batch processing after consecutive failures. | State is process-local; not distributed. Add Redis-backed coordination if you need cross-replica behaviour. |
DLQStep(dlq_storage) |
Routes failed events to a DLQStorage[T] after exhausting retries. |
Best-effort, non-transactional. Transient (count_as_attempt=False) failures are never routed to DLQ. |
- Fetch:
DistributedLockingFetchStrategy,OptimisticLockingFetchStrategy,FilteredFetchStrategy. - Commit:
BulkCommitStrategy,SingleCommitStrategy.
OutboxPublisher(
repo: OutboxEventRepository,
broker: EventPublisher,
metrics: OutboxMetrics | None = None,
publish_timeout: float = DEFAULT_PUBLISH_TIMEOUT_SECONDS,
concurrency_limit: int | None = None,
)publish_batch(worker_id, batch_size, shutdown_requested_func=None, **fetch_filters)— one fetch/lock/publish/write-back cycle through the session behindrepo. The library opens no transaction and commits nothing: wrap the call in one of your own.shutdown_requested_funcbehaves as it does onprocess_batchabove.
InboxConsumerRunner(
consumer: EventConsumer,
transaction_provider: InboxTransactionProviderProtocol,
handler: InboxHandler | None = None,
*,
worker_id: str,
consumer_group: str,
domain_service: OmniBoxDomainService | None = None,
ack_strategy: AckStrategy = AckStrategy.EXACTLY_ONCE_INBOX,
commit_offset_policy: CommitOffsetPolicy = CommitOffsetPolicy.ON_PERSIST,
exactly_once_commit_on_failed: bool = False,
process_timeout: float = DEFAULT_PROCESS_TIMEOUT_SECONDS,
concurrency_limit: int | None = None,
metrics: InboxMetrics | None = None,
)start(),stop()run_forever()— loops callingprocess_one, applies exponential backoff on errors.process_one() -> InboxConsumeResult(message_id,event_id,committed,processed,duplicate).
Enums:
AckStrategy—AT_MOST_ONCE,AT_LEAST_ONCE,EXACTLY_ONCE_INBOX.CommitOffsetPolicy—ON_PERSIST,ON_SUCCESS(only used byAT_LEAST_ONCE).
All three return EventBatchProcessor[T].
create_outbox_processor(repo, publisher, *, publish_timeout, metrics=None, dlq_storage=None, enable_otel=False, enable_circuit_breaker=False, ...)create_inbox_processor(repo, handler, *, skip_duplicate_siblings=True, filter_sources=None, process_timeout=..., ...)create_dispatching_processor(repo, router, *, dependencies=None, ...)— uses anEventRouter.
EventRouter(normalize_topic=None)— registry of handlers keyed by(topic, event_type, schema_version).register_handler(event_type, topic, handler, schema_version=None, handler_name=None)registers a callable;register_class(cls, topic=None)/register_instance(obj, topic=None)sweep aBaseEventHandlerfor decorated methods.dispatch(event, topic, repo, **dependencies)tries the exactschema_version, then a registered migration, then the version-agnostic entry, and returns a failedEventHandlerResultwhen nothing matches.BaseEventHandler— base class for class-based handlers; settopicon the subclass.event_handler(event_type, topic=None, schema_version=None)— decorator. There is nosourceparameter, and the decorator only marks the method: a router has to register it.create_dispatching_processordispatches onevent.sourceas the topic.create_dispatching_handler(router, **dependencies)— the router-backed handler thatcreate_dispatching_processorinstalls; use it when you assemble the pipeline yourself.DispatchName(str | StrEnum) andas_dispatch_str(name)— the topic / event-type spellings accepted by everything above.
From omni_box.core.services.results, re-exported at the top level.
EventHandlerStatus,EventHandlerResult,BatchProcessingResult.- Helpers:
handler_completed(status=EventHandlerStatus.COMPLETED),handler_retry(message, *, count_as_attempt=True, next_retry_at=None, status=EventHandlerStatus.RETRY),handler_skipped(status=EventHandlerStatus.SKIPPED). All three take astatus, not a free-text reason; the message on a retry is the one written tolast_error.
EventConverter(protocol)RawEventConverter— body is the rawpayload.SchemaVersionedConverter— body is{"schema_version": ..., "payload": ...}.EnvelopeEventConverter— full envelope with tracing identifiers (re-exported at the top level).
All inherit from OmniBoxError.
- Storage:
StorageError,StorageConnectionError,StorageTimeoutError,StorageTransactionError,StorageIntegrityError. - Domain / locking:
EventNotLockedError,EventLockedByAnotherWorkerError,EventAlreadyLockedError,InvalidEventStateError,EventConcurrentUpdateError. - Transient:
TransientError— raised by a publisher or a handler to say the failure belongs to the environment and not to the event. The pipeline records it without spending an attempt and retries the row on the next cycle. - Misc:
UnsupportedCapabilityError,InboxPersistError.
omni_box.infra.storage.postgres:
- ORM bases:
OutboxEventDBBase,InboxEventDBBase,OutboxEventPartitionedDBBase,InboxEventPartitionedDBBase, plus the underlyingEventMixin,OutboxColumnsMixin,InboxColumnsMixin. - Repositories:
PostgresOutboxRepository,PostgresInboxRepository,PostgresEventRepository(shared base). Both exposesession— theAsyncSessionthey were built on, read-only — which is how a handler passed toInboxConsumerRunnerwrites its side effects in the transaction that inserts the inbox row. - Helpers:
UnConstrainedEnum,get_event_constraints(table_name, include_created_at_in_unique=False).
omni_box.infra.brokers.kafka:
KafkaEventPublisher(producer, converter, *, max_infra_retries=3)— built on top ofaiokafka.AIOKafkaProducer. The caller owns the producer lifecycle (start/stop). A broker that does not answer — connection and node errors, request and client timeouts, and an unknown topic the broker will not confirm with a metadata refresh either — is retriedmax_infra_retriestimes and then raised asTransientError, which costs the row no attempt. Anything else, including a record the broker rejects, is raised as it is and counts.KafkaEventConsumer— wrapsaiokafka.AIOKafkaConsumerand exposes per-recordAckHandles. UseDefaultEnvelopeParseror provide your ownEnvelopeParser.
Neither adapter depends on any external "kit" package; only aiokafka is required.
omni_box.infra.metrics provides PrometheusInboxMetrics(prefix=None) and PrometheusOutboxMetrics(prefix=None) — implementations of InboxMetrics and OutboxMetrics. ProcessingMetrics is the shared base of those two protocols and has no implementation of its own. Wire them into the factories or pass to MetricsStep directly.
from omni_box import __version__