Swift implementation of the Rapid Membership Protocol. It is inspired by the Java implementation.
This is a proof-of-concept / playground project and is not meant for production use as yet.
A few notes about this implementation:
- it uses GRPC as a communication layer. There are likely more efficient ways to go about it than full-blown GRPC. The communication layer is pluggable, see MessagingClient and MessagingServer
- the edge failure detector is pluggable as well, see EdgeFailureDetectorProvider
- the default failure detector implementation is the the New Adaptive Accrual Failure Detector which was chosen here simply because it is easier to implement than the Phi Accrual Failure Detector. Read more about it here
- at the time of writing this there aren't many established concurrency primitives in Swift so this project uses a few of them creatively. It makes use of:
- swift-nio and its
EventLoopFuture
- the good old DispatchQueue
- swift-nio concurreny helpers, mainly
NIOAtomic
andLock
- a home-grown, stupid, horrible actor implementation (but hey, it works ¯_(ツ)_/¯ )
- swift-nio and its
- at the core of it all is the RapidStateMachine which follows the pattern outlined in this talk. The most interesting states are
active
andviewChanging
. A lot of care is put into stashing/keeping alerts for "future" configurations in order to honor the virtual synchrony requirements of Rapid and yet at the same time to allow for progress in settings where some nodes are faster than others (see this article on a very large cluster where this happens)
- run
./generateProtos.sh
to generate the message protocol with swift-protobuf. This script expectsprotoc
to be available on the path. - run
swift test
to verify that the tests are passing. You may need to increase the maximum number of allowed open files on your system (using e.g.ulimit -n 20000
on linux). - run
swift build
to build the binaries
To assemble a cluster for testing puropses, you can use the SwiftRapidCLI
interface:
- run
./SwiftRapidCLI start
(under.build/<yourPlatform>/debug
) to start a seed node onlocalhost:8000
- run
./SwiftRapidCLI join localhost 8000 --port 8001
to join the cluster and listen onlocalhost:8001
- note: for Paxos to be able to reach consensus you need at least 3 nodes, so make sure that you e.g. run at least a 5 port cluster
The entry point for this project is the RapidCluster class.
In order to start a seed node, use the start
method:
try RapidCluster.Builder.with {
$0.host = "localhost"
$0.port = 8000
$0.registerSubscription(callback: { event in
// print out any event on stdout
print(event)
})
}.start()
In order to join a seed node, use the join
method:
let cluster = try RapidCluster.Builder.with {
$0.host = "localhost"
$0.port = 8001
$0.registerSubscription(callback: { event in
print(event)
})
}.join(host: "localhost", port: 8000)
// print all members
print(try cluster.getMemberList())