Skip to content

webermarci/roar

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

roar

Package Version Hex Docs Status: Experimental Stability: Unstable

⚠️ Warning: This project is experimental and unstable. APIs may change significantly between versions.

Lightweight distributed pub/sub for Gleam on Erlang with automatic cluster synchronization.

Roar enables process-local and cluster-wide message broadcasting with automatic scope isolation, supporting both streaming subscriptions and callback-based handlers.

Features

  • 🌍 Distributed by default - Messages automatically sync across all nodes in your cluster
  • 🎯 Scope isolation - Multiple independent pub/sub systems in the same cluster
  • 📦 Simple API - Subscribe with callbacks or buffered streams
  • 🛡️ Memory safe - Configurable buffer capacity prevents runaway memory growth
  • BEAM native - Built on Erlang's distribution primitives

Installation

Add roar to your Gleam project:

gleam add roar

Quick Start

import roar

pub fn main() {
  // Create a router with buffer capacity and scope
  let router = roar.new(capacity: 100, scope: "my_app")
  
  // Subscribe to a topic
  let sub = roar.subscribe(router, "events")
  
  // Publish a message (reaches ALL nodes in "my_app" scope)
  roar.publish(router, "events", "Hello, cluster!")
  
  // Receive messages
  case sub.receive() {
    Ok(message) -> io.println(message)
    Error(Nil) -> io.println("No messages")
  }
  
  sub.cancel()
}

Usage

Callback-based subscriptions

let cancel = roar.on(router, "notifications", fn(msg) {
  io.println("Received: " <> msg)
})

roar.publish(router, "notifications", "New user signed up!")

cancel()

Buffered subscriptions

let sub = roar.subscribe(router, "events")

roar.publish(router, "events", "Event 1")
roar.publish(router, "events", "Event 2")

// Messages are buffered, read at your own pace
let assert Ok("Event 1") = sub.receive()
let assert Ok("Event 2") = sub.receive()

sub.cancel()

Multiple topics

let router = roar.new(capacity: 50, scope: "chat")

let messages = roar.subscribe(router, "messages")
let alerts = roar.subscribe(router, "alerts")

roar.publish(router, "messages", "Hello!")
roar.publish(router, "alerts", "Server restarting")

// Each subscription only receives its topic's messages

Scope isolation

// Different scopes don't interfere with each other
let app_router = roar.new(capacity: 100, scope: "app")
let admin_router = roar.new(capacity: 100, scope: "admin")

roar.subscribe(app_router, "events")    // Only sees "app" scope
roar.subscribe(admin_router, "events")  // Only sees "admin" scope

How It Works

Roar uses Whisper for local pub/sub and Erlang's pg (process groups) for cluster-wide distribution. When you publish a message:

  1. Local delivery - Delivered immediately to subscribers on the same node
  2. Remote delivery - Forwarded to all nodes in the same scope via process groups
  3. No duplicates - Each subscriber receives exactly one copy

The capacity parameter protects against memory issues by limiting buffered messages per subscription. If a subscriber is too slow, old messages are dropped to prevent memory exhaustion.

About

Distributed pub/sub for Gleam with automatic cluster synchronization across BEAM nodes

Topics

Resources

License

Stars

Watchers

Forks

Contributors