def heartbeat_rpc(view: Dict[int, int], rep_n: int, request: HeartbeatRequest) -> HeartbeatRequest:
    """
    Send a RPC call to a process telling it to read the data corresponding to a key
    """
    port = view[rep_n]
    with grpc.insecure_channel(f"localhost:{port}") as channel:
        stub = DynamoInterfaceStub(channel)
        response = stub.Heartbeat(request)
    return response
def transfer_data_rpc(view: Dict[int, int], rep_n: int, request: DataBunchRequest) -> DataBunchResponse:
    """
    Send a RPC call to a process telling it to add the following data to it's memory buffer
    """
    port = view[rep_n]
    with grpc.insecure_channel(f"localhost:{port}") as channel:
        stub = DynamoInterfaceStub(channel)
        response = stub.TransferData(request)
    return response
def replicate_rpc(view, rep_n, request) -> ReplicateResponse:
    """
    Send a RPC call to a process telling it to replicate the request in it's memory
    """
    port = view[rep_n]
    with grpc.insecure_channel(f"localhost:{port}") as channel:
        stub = DynamoInterfaceStub(channel)
        response = stub.Replicate(request)
    return response
def read_rpc(view, rep_n, request) -> ReadResponse:
    """
    Send a RPC call to a process telling it to read the data corresponding to a key
    """
    port = view[rep_n]
    with grpc.insecure_channel(f"localhost:{port}") as channel:
        stub = DynamoInterfaceStub(channel)
        response = stub.Read(request)
    return response
def client_gossip(port):
    """
    Turn gossip protocol on for this node
    """
    with grpc.insecure_channel(f"localhost:{port}") as channel:
        stub = DynamoInterfaceStub(channel)
        request = NoParams()
        response = stub.Gossip(request)
    return response
def client_get(port, client_id, key=1):
    logging.info("-------------Sending GET request !!!--------------")
    with grpc.insecure_channel(f"localhost:{port}") as channel:
        stub = DynamoInterfaceStub(channel)
        response = get(stub, client_id, key)

    if response.reroute == True:
        # sending to actual coordinator node
        with grpc.insecure_channel(f"localhost:{response.reroute_server_id}") as channel:
            stub = DynamoInterfaceStub(channel)
            response = get(stub, client_id, key)

    return response
def client_put(port, client_id, key=1, val="1", context=None):
    # item = VectorClockItem(server_id=1, count=1)
    if context is None:
        context = VectorClock(clock=[]) # An existing context only needs to be passed when updating an existing key's value
    request = PutRequest(client_id=client_id, key=key, val=val, context=context, hinted_handoff=-1)
    with grpc.insecure_channel(f"localhost:{port}") as channel:
        stub = DynamoInterfaceStub(channel)
        response = put(stub, request)
    
    if response.reroute == True:
        # sending it to actual coordinator node
        with grpc.insecure_channel(f"localhost:{response.reroute_server_id}") as channel:
            stub = DynamoInterfaceStub(channel)
            response = put(stub, request)
    
    return response
def client_fail(port, fail=True):
    with grpc.insecure_channel(f"localhost:{port}") as channel:
        stub = DynamoInterfaceStub(channel)
        request = FailRequest(fail=fail)
        response = stub.Fail(request)
def client_get_memory(port):
    with grpc.insecure_channel(f"localhost:{port}") as channel:
        stub = DynamoInterfaceStub(channel)
        request = NoParams()
        response = stub.PrintMemory(request)
    return response.mem, response.mem_replicated