Push Delivery Store

Durable, at-least-once push delivery that survives gateway restarts

Push delivery persists pending, un-acknowledged events to disk so a gateway restart never silently drops a message.

Diagram

Push Delivery

ACK

Restart replay

🤖 Agent

💾 SQLite Store

📱 Client

Quick Start

  1. Default (durable, zero config)

    Enable push and delivery is durable out of the box — no flags, no external services.

    python
    from praisonaiagents import Agent
    from praisonaiagents.gateway import GatewayConfig, PushConfig
    
    agent = Agent(name="Notifier", instructions="Push updates to subscribed clients")
    
    gateway = GatewayConfig(
        push=PushConfig(enabled=True)   # durable-by-default SQLite backend
    )

    Pending events are written to ~/.praisonai/state/push_delivery.sqlite and replayed on restart.

  2. Explicit ephemeral (opt-out for tests)

    Switch to memory when durability is unwanted, such as throwaway test runs.

    python
    from praisonaiagents.gateway import GatewayConfig, PushConfig, DeliveryConfig
    
    gateway = GatewayConfig(
        push=PushConfig(
            enabled=True,
            delivery=DeliveryConfig(store_backend="memory"),
        )
    )
  3. Redis for horizontal scaling

    Point delivery at Redis to fan out across multiple gateway processes.

    python
    from praisonaiagents.gateway import GatewayConfig, PushConfig, DeliveryConfig, RedisConfig
    
    gateway = GatewayConfig(
        push=PushConfig(
            enabled=True,
            redis=RedisConfig(url="redis://localhost:6379/0"),
            delivery=DeliveryConfig(store_backend="redis"),
        )
    )

The same settings load from gateway.yaml:

yaml
gateway:
  push:
    enabled: true
    delivery:
      store_backend: sqlite   # default; also: redis | memory
      ack_timeout: 30
      max_retries: 3
      message_ttl: 86400

How It Works

The store persists each pending event, replays it after a restart, and evicts the row once the client acknowledges.

Diagram
ClientSqlitePushStoreAgentClientSqlitePushStoreAgentgateway restartspersist(event, client_id)deliver(event)reload _pending_acks from diskreconnectreplay pending eventsACKevict acked row
StageWhat happens
PersistEvent and its recipient client_id are written to the SQLite WAL journal
DeliverThe gateway sends the event and waits for an ACK
Restart replayPending rows are reloaded and scheduled for immediate redelivery
EvictThe row is deleted on ACK or once older than message_ttl

Choosing a Backend

Pick the backend that matches your durability and scaling needs.

Diagram

No, tests only

Yes

No

Yes

Need durability?

memory

Multi-process fan-out?

sqlite ✓ default

redis


Configuration Options

DeliveryConfig controls the at-least-once guarantee.

OptionTypeDefaultDescription
enabledboolTrueToggle delivery guarantees
ack_timeoutint30Seconds to wait for ACK before retrying
max_retriesint3Maximum retry attempts
retry_backofffloat2.0Exponential backoff multiplier
message_ttlint86400Retention seconds for un-acked messages
store_backendstr"sqlite""sqlite" \"redis" \"memory"

An invalid store_backend raises ValueError at construction time — the closed set is validated in __post_init__.

Where State Lives

BackendLocationSurvives restart?Multi-process?
sqlite (default)~/.praisonai/state/push_delivery.sqlite (WAL)✅❌ single process
redisRedis (uses RedisConfig)✅✅
memoryRAM❌❌

Common Patterns

Three setups cover most deployments.

Survives a redeploy

Keep the default backend so a mid-notification restart replays on reconnect.

python
from praisonaiagents.gateway import GatewayConfig, PushConfig

gateway = GatewayConfig(push=PushConfig(enabled=True))

Horizontal scale-out

Switch to redis when running more than one gateway replica behind a load balancer.

python
from praisonaiagents.gateway import GatewayConfig, PushConfig, DeliveryConfig, RedisConfig

gateway = GatewayConfig(
    push=PushConfig(
        enabled=True,
        redis=RedisConfig(url="redis://localhost:6379/0"),
        delivery=DeliveryConfig(store_backend="redis"),
    )
)

Throwaway CI

Use memory so no SQLite artefacts leak into the CI workspace.

python
from praisonaiagents.gateway import GatewayConfig, PushConfig, DeliveryConfig

gateway = GatewayConfig(
    push=PushConfig(
        enabled=True,
        delivery=DeliveryConfig(store_backend="memory"),
    )
)

Best Practices

Keep the default

SQLite is durable, zero-dependency, and appropriate for the vast majority of deployments.

Choose redis only for multi-process gateways

For a single gateway, SQLite is faster and simpler. Reach for redis when fanning out across replicas.

Use memory only for tests

Any restart loses in-flight events silently, so reserve memory for throwaway runs.

Back up the state directory

Include ~/.praisonai/state/ in your agent-state backups. The file is small — rows evict on ACK and TTL — but it holds the durability guarantee.


Related