Skip to content

Commit

Permalink
feat(roles): seed role
Browse files Browse the repository at this point in the history
  • Loading branch information
BrianLusina committed Mar 14, 2023
1 parent 4c2b966 commit 12c8a0f
Showing 1 changed file with 52 additions and 0 deletions.
52 changes: 52 additions & 0 deletions konsensus/models/roles/seed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from typing import List, Callable
from . import Role
from ..node import Node
from .bootstrap import Bootstrap
from konsensus.entities.messages_types import Join, Welcome
from konsensus.constants import JOIN_RETRANSMIT


class Seed(Role):
"""
Network partitions are the most challenging failure case for clustered applications. In a network partition,
all cluster members remain alive, but communication fails between some members.
For example, if the network link joining a cluster with nodes in Berlin and Taipei fails, the network is partitioned.
If both parts of a cluster continue to operate during a partition, then re-joining the parts after the network link is restored can be challenging.
In the Multi-Paxos case, the healed network would be hosting two clusters with different decisions for the same slot numbers.
To avoid this outcome, creating a new cluster is a user-specified operation. Exactly one node in the cluster runs the seed role, with the others running bootstrap as usual.
The seed waits until it has received Join messages from a majority of its peers, then sends a Welcome with an initial state for the state machine and an empty set of decisions.
The seed role then stops itself and starts a bootstrap role to join the newly-seeded cluster.
Seed emulates the Join/Welcome part of the bootstrap/replica interaction
"""

def __init__(self, node: Node, initial_state, execute_fn: Callable, peers: List, bootstrap_cls=Bootstrap) -> None:
super().__init__(node)
self.initial_state = initial_state
self.execute_fn = execute_fn
self.peers = peers
self.bootstrap_cls = bootstrap_cls
self.seen_peers = set([])
self.exit_timer = None

def do_join(self, sender):
self.seen_peers.add(sender)
if len(self.seen_peers) <= len(self.peers) / 2:
return

# cluster is ready - welcome everyone
self.node.send(list(self.seen_peers), Welcome(
state=self.initial_state, slot=1, decisions={}
))

# stick around for long enough that we don't hear any new JOINs from newly formed cluster
if self.exit_timer:
self.exit_timer.cancel()
self.exit_timer = self.set_timer(JOIN_RETRANSMIT * 2, self.finish)

def finish(self):
# bootstrap this node int the cluster we just seeded
bs = self.bootstrap_cls(self.node, peers=self.peers, execute_fn=self.execute_fn)
bs.start()
self.stop()

0 comments on commit 12c8a0f

Please sign in to comment.