-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4c2b966
commit 12c8a0f
Showing
1 changed file
with
52 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |