Beispiel #1
0
 def __init__(self, clock, storage, client=client):
     self.election = _LeaderElectionProtocol(clock, self)
     self.keystore = KeyStoreMixin(clock, storage,
             [self.election.LEADER_KEY, self.election.VOTE_KEY,
              self.election.PRIO_KEY])
     self.client = client
     self.storage = storage
 def setUp(self):
     self.clock = task.Clock()
     self.storage = {}
     self.keystore = KeyStoreMixin(self.clock, self.storage)
     self.gossiper = mock()
     self.gossiper.name = 'self'
     self.keystore.make_connection(self.gossiper)
Beispiel #3
0
class ClusterNode(service.Service, KeyStoreMixin, LeaderElectionMixin):
    """Gossip participant that both implements our replicated
    key-value store and a leader-election mechanism.
    """

    def __init__(self, clock, storage, client=client):
        self.election = _LeaderElectionProtocol(clock, self)
        self.keystore = KeyStoreMixin(clock, storage,
                [self.election.LEADER_KEY, self.election.VOTE_KEY,
                 self.election.PRIO_KEY])
        self.client = client
        self.storage = storage

    def startService(self):
        self.keystore.load_from(self.storage)
        service.Service.startService(self)

    def value_changed(self, peer, key, value):
        """A peer changed one of its values."""
        if key == '__heartbeat__':
            return

        if self.election.value_changed(peer, key, value):
            # This value change was handled by the leader election
            # protocol.
            return
        self.keystore.value_changed(peer, key, value)

        if self.election.is_leader and peer.name == self.gossiper.name:
            # This peer is the leader of the cluster, which means that
            # we're responsible for firing notifications.
            if not key.startswith('watcher:'):
                self._check_notify(key)

    def make_connection(self, gossiper):
        """Make connection to gossip instance."""
        self.gossiper = gossiper
        self.election.make_connection(gossiper)
        self.keystore.make_connection(gossiper)
        self.gossiper.set(self.election.PRIO_KEY, 0)

    def peer_alive(self, peer):
        """The gossiper reports that C{peer} is alive."""
        self.election.peer_alive(peer)

    def peer_dead(self, peer):
        """The gossiper reports that C{peer} is dead."""
        self.election.peer_alive(peer)

    def leader_elected(self, is_leader):
        """Leader elected."""
        print "is leader?", is_leader
        if is_leader:
            # Go through and possible trigger all notifications.
            for key in self.keystore.keys('app:*'):
                self._check_notify(key)
            for key in self.keystore.keys('srv:*'):
                self._check_notify(key)

    def _notify(self, wkey, watcher):
        """Notification watcher about change."""
        def done(result):
            watcher['last-hit'] = self.clock.seconds()
            # Verify that the watcher has not been deleted.
            if wkey in self and self.keystore[wkey] is not None:
                self.keystore.set(wkey, watcher)
        d = self.client.getPage(str(watcher['endpoint']), method='POST',
                postdata=json.dumps({'name': watcher['name'],
                                     'uri': watcher['uri']}),
                timeout=3)
        return d.addCallback(done).addErrback(log.err)

    def _check_notify(self, key):
        """Possible notify listener that something has changed."""
        for wkey in self.keystore.keys('watcher:*'):
            watcher = self.keystore.get(wkey)
            if watcher is None:
                continue
            timestamp = self.keystore.timestamp_for_key(key)
            if (key.startswith(watcher['pattern'])
                    and watcher['last-hit'] < timestamp):
                self._notify(wkey, watcher)
class KeyStoreTestCase(unittest.TestCase):
    """Test cases for the key-value store mixin."""

    def setUp(self):
        self.clock = task.Clock()
        self.storage = {}
        self.keystore = KeyStoreMixin(self.clock, self.storage)
        self.gossiper = mock()
        self.gossiper.name = 'self'
        self.keystore.make_connection(self.gossiper)

    def test_replicate_when_remote_peer_changed_value(self):
        self.keystore.value_changed('a', 'k', (0, 'value'))
        verify(self.gossiper).set('k', (0, 'value'))
        self.assertNotIn('k', self.storage)

    def test_ignore_replication_when_remote_peer_has_old_value(self):
        self.storage['k'] = (1, 'value')
        self.keystore.value_changed('a', 'k', (0, 'value'))
        verify(self.gossiper, times=0).set('k', (0, 'value'))

    def test_persist_value_when_set_on_local_peer(self):
        self.keystore.value_changed('self', 'k', (0, 'value'))
        self.assertIn('k', self.storage)
        self.assertEquals(self.storage['k'], (0, 'value'))

    def test_keys_returns_all_keys_in_gossiper(self):
        when(self.gossiper).keys().thenReturn(['a', 'b'])
        self.assertEquals(self.keystore.keys(), ['a', 'b'])

    def test_keys_can_be_filtered(self):
        when(self.gossiper).keys().thenReturn(['ab', 'ba'])
        self.assertEquals(self.keystore.keys('b*'), ['ba'])

    def test_contains_use_keys_from_gossiper(self):
        when(self.gossiper).keys().thenReturn(['a', 'b'])
        self.assertIn('a', self.keystore)

    def test_get_results_default_value_if_value_not_present(self):
        when(self.gossiper).keys().thenReturn([])
        self.assertEquals(self.keystore.get('a', '!'), '!')

    def test_get_returns_value_from_gossip_state(self):
        when(self.gossiper).keys().thenReturn(['a'])
        when(self.gossiper).get('a').thenReturn((0, '!'))
        self.assertEquals(self.keystore.get('a'), '!')