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')
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
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)
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
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
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()
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
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
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)
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
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
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}')