Skip to content
jmeiracorbalPublic

About

Lightweight event bus over Redis Streams.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

13 Commits

Folders and files

Repository files navigation

streambus

PyPI version PyPI downloads Python License Release Tests

Lightweight event bus over Redis Streams. Define typed events, publish from one service, consume in others without boilerplate or framework dependencies.

Install

pip install streambus
uv add streambus

Quick start

Define an event:

from dataclasses import dataclass
from streambus import StreamBusEvent

@dataclass(kw_only=True)
class OrderCreated(StreamBusEvent):
    order_id: str
    customer_id: str
    total: str

Publish it:

from streambus import EventPublisher, RedisConfig, RedisStreamsTransport

transport = RedisStreamsTransport(RedisConfig(url="redis://localhost:6379"))
publisher = EventPublisher(stream="orders:events", transport=transport)

publisher.publish(
    OrderCreated(
        event_type="order.created",
        order_id="ord_123",
        customer_id="cust_456",
        total="99.90",
    )
)

Consume it:

from streambus import EventListener, RedisConfig, RedisStreamsTransport

def handle_order(event: OrderCreated) -> None:
    print(f"New order {event.order_id}: total {event.total}")

EventListener(
    stream="orders:events",
    group="notifications",
    handler=handle_order,
    event_class=OrderCreated,
    transport=RedisStreamsTransport(RedisConfig(url="redis://localhost:6379")),
).run()

How it works

streambus uses Redis Streams for transport: persistent storage, consumer groups, and at-least-once delivery. The library manages group creation, the blocking read loop, message acknowledgement, and automatic reconnection on connection loss.

Each consumer group processes events independently. Delivery is at least once: unacknowledged messages can be redelivered, so handlers must be idempotent. The listener reclaims idle pending entries using Redis XAUTOCLAIM before reading new entries (Redis 6.2+ required).

Events

All events extend StreamBusEvent:

Field Type Required Notes
event_type str yes Identifies the event. Convention: noun.verb
occurred_at str no ISO 8601 timestamp. Auto-set on creation.

Subclasses declare only the fields they need. Unknown fields arriving from the stream are silently discarded — each consumer maps what it uses, nothing more.

Both StreamBusEvent and its subclasses must use @dataclass(kw_only=True). This is required: the base class has a field with a default (occurred_at), and kw_only=True prevents the ordering conflict that would otherwise block subclasses from adding required fields.

Configuration

RedisConfig

RedisConfig(
    url="redis://localhost:6379",  # required
    socket_timeout=None,           # None = no timeout (correct for blocking reads)
    socket_connect_timeout=5.0,    # seconds to wait on initial connect
)

EventListener

EventListener(
    stream="stream:name",     # required
    group="consumer-group",   # required
    handler=my_handler,       # required — callable(event) -> None
    event_class=MyEvent,      # required — must subclass StreamBusEvent
    transport=RedisStreamsTransport(RedisConfig(...)),  # required
    consumer=None,            # defaults to "{group}-1"
    batch_size=10,
    block_ms=5000,
    retry_delay=3.0,
    pending_idle_ms=60000,  # minimum idle time before retrying pending entries
)

EventPublisher

EventPublisher(
    stream="stream:name",    # required
    transport=RedisStreamsTransport(RedisConfig(...)), # required
)

Exceptions

Exception When
ConfigurationError Invalid or missing parameters at construction time
EventValidationError publish() receives a non-StreamBusEvent or empty event_type
StreambusError Base class for all streambus exceptions

Development

git clone https://github.com/jmeiracorbal/streambus
cd streambus
uv sync --group dev
uv run pytest tests/ -v

Run the example (requires Docker):

cd example
docker compose up -d        # start Redis
uv sync
uv run python consume.py    # terminal 1
uv run python publish.py    # terminal 2

License

MIT

About

Lightweight event bus over Redis Streams.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages