distribute/discovery
High-level discovery API for the distribute library.
This module provides a unified facade for the discovery layer, managing the default BEAM discovery adapter as a singleton. Other modules can use these functions without explicitly passing adapter handles.
Architecture
The discovery layer is designed as a pluggable system:
discovery/types- Shared type definitionsdiscovery/behaviour- Adapter contract (behaviour)discovery/adapter- Adapter utilities and defaultsdiscovery/beam_adapter- BEAM node monitoring implementationdiscovery(this module) - High-level singleton facade
Usage
Start the discovery as part of your supervision tree:
import distribute/discovery
import gleam/otp/static_supervisor as supervisor
pub fn start_app() {
supervisor.new(supervisor.OneForOne)
|> supervisor.add(discovery.child_spec())
|> supervisor.start()
}
Then use the discovery functions:
// Subscribe to peer events
let callback = fn(event) {
case event {
discovery.PeerUp(peer, _meta) -> io.println("Peer joined: " <> peer)
discovery.PeerDown(peer, _reason) -> io.println("Peer left: " <> peer)
_ -> Nil
}
}
let assert Ok(sub_id) = discovery.subscribe(callback)
// Get current peers
let assert Ok(peers) = discovery.snapshot()
// Check health
case discovery.health() {
discovery.Up -> io.println("Discovery is healthy")
discovery.Degraded(reason) -> io.println("Degraded: " <> reason)
discovery.Down(reason) -> io.println("Down: " <> reason)
}
Values
pub fn child_spec() -> supervision.ChildSpecification(
types.AdapterHandle,
)
Create a child specification for starting the discovery adapter under a supervisor.
This is the recommended way to start the discovery layer. The adapter will be automatically restarted if it crashes.
Example
import distribute/discovery
import gleam/otp/static_supervisor as supervisor
supervisor.new(supervisor.OneForOne)
|> supervisor.add(discovery.child_spec())
|> supervisor.start()
pub const default_adapter_name: String
The registered name for the default discovery adapter process.
This name is used to look up the singleton adapter instance. You typically don’t need to use this directly - the functions in this module handle the lookup automatically.
pub fn health() -> types.HealthStatus
Get the health status of the discovery adapter.
Returns:
Up- Discovery is fully operationalDegraded(reason)- Discovery is working but with issuesDown(reason)- Discovery is not operational
Example
case discovery.health() {
types.Up -> io.println("All good!")
types.Degraded(r) -> io.println("Warning: " <> r)
types.Down(r) -> io.println("Error: " <> r)
}
pub fn lookup(
peer_id: String,
) -> Result(types.PeerInfo, types.DiscoveryError)
Lookup a specific peer by ID.
Returns the peer’s info if known, or PeerNotFound error otherwise.
Example
case discovery.lookup("node2@host") {
Ok(peer) -> io.println("Found peer with metadata")
Error(types.PeerNotFound(_)) -> io.println("Peer not found")
Error(_) -> io.println("Discovery error")
}
pub fn snapshot() -> Result(
List(types.PeerInfo),
types.DiscoveryError,
)
Get a snapshot of all currently known peers.
Returns a list of peers with their metadata at the current point in time. This is a consistent snapshot - peers won’t change during iteration.
Example
let assert Ok(peers) = discovery.snapshot()
list.each(peers, fn(peer) {
io.println("Peer: " <> peer.id)
})
pub fn start_link() -> Result(
types.AdapterHandle,
types.DiscoveryError,
)
Start the discovery adapter directly without supervision.
Note: For production use, prefer child_spec() with a supervisor.
This function is primarily useful for testing or simple scripts.
Returns the adapter handle on success, or an error if startup fails.
pub fn subscribe(
callback: fn(types.DiscoveryEvent) -> Nil,
) -> Result(types.SubscriptionId, types.DiscoveryError)
Subscribe to peer membership events.
The callback will be invoked for each peer event (up, down, update). Returns a subscription ID that can be used to unsubscribe later.
Example
let callback = fn(event) {
case event {
types.PeerUp(peer, meta) ->
io.println("New peer: " <> peer)
types.PeerDown(peer, reason) ->
io.println("Peer left: " <> peer)
types.PeerUpdate(peer, meta) ->
io.println("Peer updated: " <> peer)
}
}
let assert Ok(sub_id) = discovery.subscribe(callback)
pub fn unsubscribe(
id: types.SubscriptionId,
) -> Result(Nil, types.DiscoveryError)
Unsubscribe from peer events.
After calling this function, the callback associated with the given subscription ID will no longer receive events.