Ejemplo n.º 1
0
class Reader(object):
    def __init__(self,
                 host: str = 'localhost',
                 port: int = 1883,
                 timeout: int = 3,
                 **kwargs):

        self._kvs = KVStore()
        node_id = self.node_id = self._kvs.get('node:node_id', '')

        self._host = host
        self._port = port
        # Note that the timeout is the time to wait for data, not for establishing a connection
        # A timeout for connection is not supported by the Paho MQTT library
        self._timeout = timeout

        self._client = mqtt.Client(client_id=CLIENT_ID_PREFIX + node_id,
                                   clean_session=False,
                                   **kwargs)

        self._client.enable_logger(logger=logger)

        # We store payloads from all topics in a dict. We need this since it's possible that
        # a payload comes in for a topic that's different to the one we're currently looking
        # to read, but we need to retain it so it can be returned as the result of another reading
        self._current_payloads = {}

    def __enter__(self):
        try:
            self._client.connect(self._host, port=self._port)
        except Exception:
            logger.error(
                'Exception while attempting to connect to MQTT broker:')
            raise

        self._client.on_message = self.__on_message
        self._client.loop_start()

        sleep(self._timeout)

        return self

    def __exit__(self, type, value, traceback):
        try:
            self._client.disconnect()
        except Exception:
            logger.warning("Could not disconnect from MQTT broker",
                           exc_info=True)

    def __on_message(self, client, userdata, msg):
        self._current_payloads[msg.topic] = msg.payload

    def read(self, topic, **rdg):
        res, _ = self._client.subscribe(topic, qos=DEFAULT_QOS)
        if res != mqtt.MQTT_ERR_SUCCESS:
            logger.error(
                f"Could not subscribe to topic '{topic}'. Result: {res}")
            return None

        return self._current_payloads.get(topic)
Ejemplo n.º 2
0
class Server(object):

    'A server for the key-value store'

    def __init__(self, addr, addrs, avg_delays):
        self.addr = addr
        self.addrs = sorted(addrs)
        assert self.addr in addrs

        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.data = KVStore()
        self.avg_delays = avg_delays

    def start(self):
        thread = Thread(target=self.run)
        thread.daemon = True
        thread.start()

    def send_message(self, q, msg, peer):
        'Send `msg` to `peer` and wait for a response'
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect(peer)
        time.sleep(random.uniform(0, 2 * self.avg_delays[peer]))
        self.send(msg, sock)
        q.put(self.receive(sock))

    def run(self):
        '''Listening for incoming connections and process them in a
        seperate thread.'''
        self.sock.bind(self.addr)
        self.sock.listen(5)

        while True:
            conn, addr = self.sock.accept()
            handler = Thread(target=self.handle_connection, args=(conn, addr))
            handler.daemon = True
            handler.start()

    def replicas(self, key):
        h = hash(key) % len(self.addrs)
        return set(self.addrs[(h + i) % len(self.addrs)]
                   for i in range(NUM_REPLICAS))

    @classmethod
    def send(cls, obj, sock):
        'Send the object `obj` over the socket `sock`'
        sock.send(pickle.dumps(obj))

    @classmethod
    def receive(cls, sock):
        'Receive a pickle-serialized object from the socket `sock`'
        data = ''
        while True:
            data += sock.recv(1024)
            try:
                return pickle.loads(data)
            except EOFError:
                pass  # read more data

    def handle_connection(self, conn, addr):
        while True:
            msg = self.receive(conn)
            if isinstance(msg, GetRequest):
                value, timestamp = self.data.get(msg.key)
                self.send(GetResponse(msg.key, value, timestamp), conn)
            elif isinstance(msg, InsertRequest):
                result = self.data.insert(msg.key, msg.value)
                self.send(InsertResponse(msg.key, result), conn)
            elif isinstance(msg, UpdateRequest):
                result = self.data.update(msg.key, msg.value)
                self.send(UpdateResponse(msg.key, result), conn)
            elif isinstance(msg, DeleteRequest):
                result = self.data.delete(msg.key)
                self.send(DeleteResponse(msg.key, result), conn)
            elif isinstance(msg, RepairRequest):
                self.repair_val_timestamp(msg.key, msg.value, msg.timestamp)
            else:
                print "Unknown message type: {}".format(msg)

    def repair_val_timestamp(self, key, new_val, new_timestamp):
        local_pair = self.data.get(key)
        local_val = local_pair[0]
        local_timestamp = local_pair[1]
       
        #  ignore repairs if key has been deleted/doesn't exist
        if local_pair and local_timestamp < new_timestamp:
            self.data.update(key, new_val, new_timestamp)

    def executeRepair(self, key, resp=None):
        result = resp
        if not result:
            result = self.get(key, "all")
        q = Queue()
        # send out repair requests to all replicas for a key, no need to block
        for replica in self.replicas(key):
            t = Thread(target=self.send_message,
                       args=(q, RepairRequest(key, result.value, result.timestamp), replica))
            t.start()

    def get(self, key, level):
        q = Queue()
        for replica in self.replicas(key):
            t = Thread(target=self.send_message,
                       args=(q, GetRequest(key), replica))
            t.start()
        if level == 'one':
            wait_for = 1
        else:
            wait_for = NUM_REPLICAS
        responses = []
        while len(responses) < wait_for:
            resp = q.get()
            assert isinstance(resp, GetResponse)
            assert resp.key == key
            responses.append(resp)

        max_response = max(responses, key=lambda r: r.timestamp)
        resp = None if level == 'one' else max_response

        t = Thread(target=self.executeRepair, args=(key, resp))
        t.start()
        return max_response

    def insert(self, key, value, level):
        q = Queue()
        for replica in self.replicas(key):
            t = Thread(target=self.send_message,
                       args=(q, InsertRequest(key, value), replica))
            t.start()

        if level == 'one':
            wait_for = 1
        else:
            wait_for = NUM_REPLICAS
        responses = []
        while len(responses) < wait_for:
            resp = q.get()
            assert isinstance(resp, InsertResponse)
            assert resp.key == key
            responses.append(resp)
        return all(r.result for r in responses)

    def update(self, key, value, level):
        q = Queue()
        for replica in self.replicas(key):
            t = Thread(target=self.send_message,
                       args=(q, UpdateRequest(key, value), replica))
            t.start()

        if level == 'one':
            wait_for = 1
        else:
            wait_for = NUM_REPLICAS
        responses = []
        while len(responses) < wait_for:
            resp = q.get()
            assert isinstance(resp, UpdateResponse)
            assert resp.key == key
            responses.append(resp)
        return all(r.result for r in responses)

    def delete(self, key):
        q = Queue()
        for replica in self.replicas(key):
            t = Thread(target=self.send_message,
                       args=(q, DeleteRequest(key), replica))
            t.start()

        responses = []
        while len(responses) < NUM_REPLICAS:
            resp = q.get()
            assert isinstance(resp, DeleteResponse)
            assert resp.key == key
            responses.append(resp)
        return all(r.result for r in responses)

    def items(self):
        for key, (value, timestamp) in self.data.items():
            yield key, value

    def owners(self, key):
        'Return a list of servers responsible for `key`'
        return self.replicas(key)
Ejemplo n.º 3
0
def initialize(wifi_ap: WifiAPSnapCtl, kvs: KVStore) -> bool:
    wifi_ap_cfg = kvs.get(KVS_CONFIG_KEY)
    return wifi_ap.configure(wifi_ap_cfg)