Foca: Cluster membership discovery on your terms
Foca is a building block for your gossip-based cluster discovery. It's
a small no_std
+ alloc
crate that implements the SWIM protocol along
with its useful extensions (SWIM+Inf.+Susp.
).
Project:
Introduction
The most notable thing about Foca is the fact that it does almost
nothing. Out of the box, all it gives is a reliable and efficient
implementation of the SWIM protocol that's transport and
identity agnostic.
Knowledge of how SWIM works is helpful but not necessary to make use
of this library. Reading the documentation for the Message
enum
should give you an idea of how the protocol works, but the paper is
a very accessible read.
Foca is designed to fit into any sort of transport: If your network
allows peers to talk to each other you can deploy Foca on it.
Not only the general bandwidth requirements are low, but you also
have full control of how members identify each other (see
./examples/identity_golf.rs
) and how messages are encoded.
Usage
Please take a look at ./examples/foca_insecure_udp_agent.rs
. It
showcases how a simple tokio-based agent could look like and lets
you actually run and see Foca swimming.
$ cargo run --features std,tracing,bincode-codec --example foca_insecure_udp_agent -- --help
foca_insecure_udp_agent
USAGE:
foca_insecure_udp_agent [OPTIONS] <BIND_ADDR>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-a, --announce <announce> Address to another Foca instance to join with
-f, --filename <filename> Name of the file that will contain all active members
-i, --identity <identity> The address cluster members will use to talk to you.
Defaults to BIND_ADDR
ARGS:
<BIND_ADDR> Socket address to bind to. Example: 127.0.0.1:8080
So you can start the agent in one terminal with
./foca_insecure_udp_agent 127.0.0.1:8000
and join it with as many others
as you want with using a different BIND_ADDR
and --announce
to a
running instance. Example:
./foca_insecure_udp_agent 127.0.0.1:8001 -a 127.0.0.1:8000
.
The agent outputs some information to the console via tracing's
subscriber. It defaults to the INFO
log level and can be customized
via the RUST_LOG
environment variable using tracing_subscriber's
EnvFilter directives.
Cargo Features
Every feature is optional. The default
set will always be empty.
-
std
: Adds std::error::Error
support, implements foca::Identity
for std::net::SocketAddr*
and exposes Config::new_lan
and
Config::new_wan
-
tracing
: Instruments Foca using the tracing crate. High-level
protocol interactions are emited as DEBUG
traces, more details can
be exposed with the TRACE
level. No other levels are emitted.
-
serde
: Derives Serialize
and Deserialize
for Foca's public
types.
-
bincode-codec
: Provides BincodeCodec
, a serde-based codec type
that uses bincode under the hood.
-
postcard-codec
: Provides PostcardCodec
a serde-based, no_std
friendly codec that uses postcard under the hood.
Notes
When writing this library, the main goal was having a simple and small
core that's easy to test, simulate and reason about; It was mostly
about getting a better understanding of the protocol after reading
the paper.
Sticking to these goals naturally led to an implementation that doesn't
rely on many operating system features like a hardware clock, atomics
and threads, so becoming a no_std
crate (albeit still requiring heap
allocations) was kind of a nice accidental feature that I decided to
commit to.
Comparison to memberlist
I avoided looking at memberlist until I was satisfied with my
own implementation. Since then I did take a non-thorough look at it:
-
memberlist supports custom broadcasts, which is a very cool feature
for complex service discovery scenarios, so now Foca has support
for disseminating user data too (see BroadcastHandler
documentation) :-)
-
It has a stream-based synchronization mechanism (push-pull) that's
used for joining and periodic merging state between members: It's
way beyond Foca's responsibilities, but it's a very interesting idea,
so I've exposed the Foca::apply_many
method which enables code
using Foca to do a similar thing if desired.
-
Its configuration parameters change based on (current) cluster
size. Users can make use of Config::new_{lan,wan}
along with
Foca::set_config
to achieve the same
References
License
Unless explicitly stated otherwise, all work is subject to the terms
of the Mozilla Public License, version 2.0.
Files inside the examples/
directory are dedicated to the Public
Domain.