Arifa
Arifa is a lightweight, Redis-based realtime pub/sub engine for Rust applications.
It provides a simple abstraction over Redis Pub/Sub and WebSocket sessions, making it easy to build scalable realtime systems that work across multiple application nodes.
Features
- Redis Pub/Sub
- Shared Redis Pub/Sub connection per node
- Automatic reconnect with exponential backoff
- Automatic channel resubscription
- Tokio async runtime
- WebSocket session abstraction
- Channel-based subscriptions
- Multi-node message routing
- Online user tracking
- Lock-free metrics
- Framework agnostic (
WsSessiontrait)
Benchmarks
| Benchmark | Result |
|---|---|
| Subscribe → unsubscribe (round trip) | ~1.1 ms |
| Message serialization | ~0.18 µs |
| Cross-node publish → receive | ~375 µs |
| Delivery throughput @ 100 subscribers | ~185,000 msg/sec |
| Delivery throughput @ 1,000 subscribers | ~260,000 msg/sec |
| Delivery throughput @ 10,000 subscribers | ~270,000 msg/sec |
| Fan-out: 1 channel × 2,000 subs (1 publish) | ~17 ms |
| Fan-out: 2,000 channels × 1 sub each (2,000 publishes) | ~490 ms |
| Messages dropped / Redis reconnects | 0 / 0 |
Delivery throughput holds steady from 100 to 10,000 concurrent subscribers on a single channel — total time to fan out scales with sessions × messages, not with a drop in per-message throughput.
Benchmarks live in their own crate at benchmarks/ and use Criterion against a local Redis instance. See benchmarks/README.md for how to run them yourself and what each one measures.
Installation
Creating an Arifa Instance
use *;
async
Defining a WebSocket Session
Arifa is transport agnostic. Implement the WsSession trait for your WebSocket framework.
use *;
use async_trait;
;
Subscribing
subscribe() registers the session, marks it online, and returns a generated session id.
let session = MySession;
let channels = vec!;
let session_id = arifa.subscribe.await;
The returned session id is used when removing the connection from the online users set and unsubscribing.
Publishing Messages
use *;
use json;
let message = WsMessage ;
arifa
.publish
.await?;
Unsubscribing
When a WebSocket disconnects, remove the session from the online users set before unsubscribing.
let _ = arifa.remove_online_user.await;
arifa.unsubscribe;
Message Model
MessageScope
MessageKind
Online Users
A connection is automatically marked online inside subscribe().
Query the current number of active sessions:
let users = arifa.online_users.await?;
println!;
Online User Server Node
A user's node_id is automatically recorded when subscribe() is called.
To retrieve the user's current node_id:
let node_id = arifa.get_user_node_id.await?;
println!;
If the user is not online, get_user_node_id() returns None.
Remove a session when its WebSocket closes:
let _ = arifa.remove_online_user.await;
Check whether a user currently has any active sessions:
let online = arifa.is_user_online.await?;
Note: The online count reflects active sessions, not unique users. Multiple browser tabs or devices for the same user count as multiple sessions.
Multi-node Routing
To target only a specific application node, set the node_id field.
let message = WsMessage ;
arifa.publish.await?;
If node_id is None, every subscribed node receives the message.
Metrics
Arifa exposes lightweight runtime metrics.
let metrics = arifa.metrics.snapshot;
println!;
println!;
println!;
println!;
Graceful Shutdown
Before shutting down your application, stop Arifa's background router and forwarding tasks.
arifa.shutdown.await;
Logging
Arifa emits logs using the tracing crate.
To see these logs, initialize a tracing subscriber in your application:
use ;
fmt
.with_env_filter
.init;
Example:
RUST_LOG=info
RUST_LOG=arifa=debug
Example: Actix Web
pub async
Architecture
WebSocket Client
│
▼
WsSession
│
▼
Per-session forwarder
▲
│
Routing Table
│
▼
Shared Redis Pub/Sub Router
(one per application node)
│
▼
Redis Pub/Sub
│
▼
Other Nodes
Each Arifa instance maintains a single Redis Pub/Sub connection that is shared by all subscriptions on that node. Incoming messages are routed to subscribed sessions through an in-memory routing table. If the Redis connection drops, Arifa automatically reconnects and resubscribes to all active channels.
Use Cases
- Chat applications
- Notifications
- Live location updates
- Multiplayer games
- Live dashboards
- Social feeds
- Collaborative applications
Notes
- One shared Redis Pub/Sub connection is used per application node.
- Each subscribed session runs in its own forwarding task.
- Session message queues are bounded to prevent slow clients from consuming unbounded memory.
- Redis reconnects and channel resubscriptions happen automatically.
- Call
remove_online_user()beforeunsubscribe()when a client disconnects.
License
Apache-2.0