Beispiel #1
0
 def broadcast_view(self, view, repl_factor):
     addresses = set(sorted(view.split(',')) + self.view)
     for address in addresses:
         Request.send_node_change(address, view, repl_factor)
     for address in addresses:
         Request.send_key_migration(address, view)
     app.logger.info('Broadcast complete')
Beispiel #2
0
def get(key):
    if state.address not in state.view:
        return json.dumps({
            "error": "Unable to satisfy request",
            "message": "Error in GET"
        }), 503
    if privlege(request) < USER_PRIVLEGE:
        return json.dumps({
            "error": "Unauthorized access",
            "message": "Not on authenticated user or server list"
        }), 401
    address = state.maps_to(key)
    shard_id = state.shard_map[address]
    causal_context = get_causal_context(request)
    if shard_id == state.shard_id:
        response = Request.send_get(state.address, key, causal_context)
        payload = response.json()
        if 'address' in payload: del payload['address']
        return payload, response.status_code
    else:
        # Attempt to send to every address in a shard, first one that doesn't tine
        shard_id -= 1
        for i in range(state.repl_factor):
            address = state.view[shard_id * state.repl_factor + i]
            response = Request.send_get(address, key, causal_context)
            if response.status_code != 500:
                return response.json(), response.status_code
        #unreachable due to TA guarentee
        app.logger.error(
            f'No requests were successfully forwarded to shard.{shard_id}')
        return json.dumps({
            "error": "Unable to satisfy request",
            "message": "Error in GET"
        }), 503
Beispiel #3
0
def delete(key):
    if privlege(request) < USER_PRIVLEGE:
        return json.dumps({
            "error": "Unauthorized access",
            "message": "Not on authenticated user or server list"
        }), 401
    address = state.maps_to(key)
    shard_id = state.shard_map[address]
    # get causal context, if empty, initalize
    causal_context = get_causal_context(request)
    # if its in our shard, foward
    if shard_id == state.shard_id:
        entry = state.update_delete_entry(
            state.storage[key]
        ) if key in state.storage else state.build_delete_entry()
        # forward to replicas
        successful_broadcast = state.delete_from_replicas(
            key, entry, causal_context)
        if not successful_broadcast:  # if a node didn't recieve, save in client
            causal_context['queue'][key] = entry
        elif key in causal_context[
                'queue']:  # if every node recieved, delete previous context from client
            del causal_context['queue'][key]
        # send to self
        response = Request.send_delete_endpoint(state.address, key, entry,
                                                causal_context)
        payload = response.json()
        payload['causal-context'] = causal_context
        return payload, response.status_code
    else:
        # key belongs to different shard, foward deletion
        return state.delete_from_shard(shard_id, key, causal_context)
Beispiel #4
0
def view_change():
    view_str = request.get_json()['view']
    replica_factor = request.get_json().get('repl-factor',
                                            kvs.state.repl_factor)
    app.logger.info("Start broadcast view change: " + str(kvs.state.view))
    kvs.state.broadcast_view(view_str, replica_factor)

    shards = {}
    for address in kvs.state.view:
        response = Request.send_get(address, 'key-count', {})
        if response.status_code == 500: continue
        shard_id = response.json()["shard-id"]
        key_count = response.json()['key-count']
        if shard_id in shards:
            key_count = min(key_count, shards[shard_id]['key-count'])
            shards[shard_id]['key-count'] = key_count
        else:
            replicas = [
                address for address in kvs.state.view
                if shard_id == str(kvs.state.shard_map[address])
            ]
            shards[shard_id] = {
                "shard-id": shard_id,
                "key-count": key_count,
                "replicas": replicas
            }
    return json.dumps({
        "message": "View change successful",
        "shards": list(shards.values())
    }), 200
Beispiel #5
0
def put_store():
    data = request.get_json()
    if data['type'] == 'shard':
        for key, value in data['store'].items():
            data['store'][key] = kvs.state.build_put_entry(value)
        # forward update to every replica
        for address in kvs.state.local_view:
            Request.put_store(address, data['store'], 'replica')
        return json.dumps({'message': 'success'}), 200
    elif data['type'] == 'replica':
        for key, entry in data['store'].items():
            if not kvs.state.storage_contains(key):
                kvs.state.key_count += 1
            kvs.state.storage[key] = entry
        return json.dumps({'message': 'success'}), 200
    else:
        return json.dumps({'message': 'unreachable'}), 500
Beispiel #6
0
def anti_entropy():
    for address in kvs.state.queue:
        if len(kvs.state.queue[address]) > 0:
            response = Request.send_gossip(address, {
                'address': kvs.state.address,
                'queue': kvs.state.queue[address]
            })
            if response.status_code != 500:
                kvs.state.queue[address].clear()
Beispiel #7
0
def delete_store():
    data = request.get_json()
    if data['type'] == 'shard':
        for key, _value in data['store'].items():
            # build entry
            data['store'][key] = kvs.state.build_delete_entry()
        # forward update to every replica
        for address in kvs.state.local_view:
            Request.delete_store(address, data['store'], 'replica')
        return json.dumps({'message': 'success'}), 200
    elif data['type'] == 'replica':
        for key, _value in data['store'].items():
            if kvs.state.storage_contains(key):
                kvs.state.key_count -= 1
                kvs.state.storage[key] = kvs.state.update_delete_entry(
                    kvs.state.storage[key])
            else:
                kvs.state.storage[key] = kvs.state.build_delete_entry()
        return json.dumps({'message': 'success'}), 200
    else:
        return json.dumps({'message': 'unreachable'}), 500
Beispiel #8
0
 def delete_from_replicas(self, key, entry, causal_context):
     successful_broadcast = True
     for address in self.replicas:
         response = Request.send_delete_endpoint(address, key, entry)
         if response.status_code == 500:
             self.queue[address][key] = entry
             successful_broadcast = False
         else:
             self.vector_clock[address] += 1
             causal_context['logical'][str(
                 self.shard_id
             )] = self.logical + 1 if causal_context['logical'][str(
                 self.shard_id
             )] <= self.logical else causal_context['logical'][str(
                 self.shard_id)]
     return successful_broadcast
Beispiel #9
0
def put(key):
    if privlege(request) < USER_PRIVLEGE:
        return json.dumps({
            "error": "Unauthorized access",
            "message": "Not on authenticated user or server list",
            "replaced": False
        }), 401
    data = request.get_json()
    causal_context = get_causal_context(request)
    address = state.maps_to(key)
    shard_id = state.shard_map[address]
    if shard_id == state.shard_id:
        if 'value' not in data:
            return json.dumps({
                "error": "Value is missing",
                "message": "Error in PUT",
                "causal-context": causal_context
            }), 400
        if len(key) > 50:
            return json.dumps({
                "error": "Key is too long",
                "message": "Error in PUT",
                "causal-context": causal_context
            }), 400
        # if in storage update, else create
        entry = state.update_put_entry(
            data['value'], state.storage[key]
        ) if key in state.storage else state.build_put_entry(data['value'])
        causal_context['queue'][key] = entry
        successful_broadcast = state.put_to_replicas(key, entry,
                                                     causal_context)
        if not successful_broadcast:
            causal_context['queue'][key] = entry
        elif key in causal_context['queue']:
            del causal_context['queue'][key]
        # # save on local, return causal context to client
        response = Request.send_put_endpoint(state.address, key, entry,
                                             causal_context)
        payload = response.json()
        payload['causal-context'] = causal_context
        return payload, response.status_code
    else:
        # try sending to every node inside of expected shard, first successful quit
        return state.put_to_shard(shard_id, key, data['value'], causal_context)
Beispiel #10
0
 def delete_from_shard(self,
                       shard_id,
                       key,
                       causal_context={
                           'queue': {},
                           'logical': 0
                       }):
     for i in range(self.repl_factor):
         address = self.view[(shard_id - 1) * self.repl_factor + i]
         response = Request.send_delete(address, key, causal_context)
         if response.status_code != 500:
             payload = response.json()
             payload['address'] = address
             return payload, response.status_code
     # unreachable
     return json.dumps({
         "error": "Unable to satisfy request",
         "message": "Error in DELETE",
         "causal-context": causal_context
     }), 503
Beispiel #11
0
 def put_to_shard(self,
                  shard_id,
                  key,
                  value,
                  causal_context={
                      'queue': {},
                      'logical': 0
                  }):
     for i in range(self.repl_factor):
         address = self.view[(shard_id - 1) * self.repl_factor + i]
         response = Request.send_put(address, key, value, causal_context)
         if response.status_code != 500:
             payload = response.json()
             payload['address'] = address
             return payload, response.status_code
     # unreachable by TA guarentee at least one node will be available in every shard
     return json.dumps({
         "error": "Unable to satisfy request",
         "message": "Error in PUT"
     }), 503
Beispiel #12
0
    def key_migration(self, view):
        app.logger.info("Key migration starts")
        deleting = {address: {} for address in self.view}
        putting = {address: {} for address in self.view}
        # rehash every key, if it belongs in different shard, put else. If in our shard, restart vector clock
        for key in list(self.storage.keys()):
            address = self.maps_to(key)
            shard_id = self.shard_map[address]

            if self.shard_id != shard_id:
                if self.storage[key]['method'] != 'DELETE':
                    putting[address][key] = self.storage[key]['value']
                    self.key_count -= 1
                del self.storage[key]
            else:
                self.storage[key]['vector_clock'] = self.new_vector_clock()
                for address in self.replicas:
                    if self.storage[key]['method'] != 'DELETE':
                        putting[address][key] = self.storage[key]
                    else:
                        deleting[address][key] = self.storage[key]
        # send mass storage
        for address, store in putting.items():
            if len(store) > 0:
                if self.shard_map[address] == self.shard_id:
                    Request.put_store(address, store, 'replica')
                else:
                    Request.put_store(address, store, 'shard')
        # send mass deletion
        for address, store in deleting.items():
            if len(store) > 0:
                if self.shard_map[address] == self.shard_id:
                    Request.delete_store(address, store, 'replica')
                else:
                    Request.delete_store(address, store, 'shard')
        app.logger.info(f'Key migration complete, key_count:{self.key_count}')