parent
2ef35fe9db
commit
6e94552a30
3 changed files with 146 additions and 4 deletions
@ -0,0 +1,128 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2018 gRPC authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <map> |
||||||
|
|
||||||
|
#include <grpcpp/support/client_interceptor.h> |
||||||
|
|
||||||
|
#ifdef BAZEL_BUILD |
||||||
|
#include "examples/protos/keyvaluestore.grpc.pb.h" |
||||||
|
#else |
||||||
|
#include "keyvaluestore.grpc.pb.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
// This is a naive implementation of a cache. A new cache is for each call. For
|
||||||
|
// each new key request, the key is first searched in the map and if found. Only
|
||||||
|
// if the key is not found in the cache do we make a request.
|
||||||
|
class CachingInterceptor : public grpc::experimental::Interceptor { |
||||||
|
public: |
||||||
|
CachingInterceptor(grpc::experimental::ClientRpcInfo* info) {} |
||||||
|
|
||||||
|
void Intercept( |
||||||
|
::grpc::experimental::InterceptorBatchMethods* methods) override { |
||||||
|
bool hijack = false; |
||||||
|
if (methods->QueryInterceptionHookPoint( |
||||||
|
grpc::experimental::InterceptionHookPoints:: |
||||||
|
PRE_SEND_INITIAL_METADATA)) { |
||||||
|
// Hijack all calls
|
||||||
|
hijack = true; |
||||||
|
// Create a stream on which this interceptor can make requests
|
||||||
|
stub_ = keyvaluestore::KeyValueStore::NewStub( |
||||||
|
methods->GetInterceptedChannel()); |
||||||
|
stream_ = stub_->GetValues(&context_); |
||||||
|
} |
||||||
|
if (methods->QueryInterceptionHookPoint( |
||||||
|
grpc::experimental::InterceptionHookPoints::PRE_SEND_MESSAGE)) { |
||||||
|
// We know that clients perform a Read and a Write in a loop, so we don't
|
||||||
|
// need to maintain a list of the responses.
|
||||||
|
std::string requested_key; |
||||||
|
const keyvaluestore::Request* req_msg = |
||||||
|
static_cast<const keyvaluestore::Request*>(methods->GetSendMessage()); |
||||||
|
if (req_msg != nullptr) { |
||||||
|
requested_key = req_msg->key(); |
||||||
|
} else { |
||||||
|
// The non-serialized form would not be available in certain scenarios,
|
||||||
|
// so add a fallback
|
||||||
|
keyvaluestore::Request req_msg; |
||||||
|
auto* buffer = methods->GetSerializedSendMessage(); |
||||||
|
auto copied_buffer = *buffer; |
||||||
|
GPR_ASSERT( |
||||||
|
grpc::SerializationTraits<keyvaluestore::Request>::Deserialize( |
||||||
|
&copied_buffer, &req_msg) |
||||||
|
.ok()); |
||||||
|
requested_key = req_msg.key(); |
||||||
|
} |
||||||
|
|
||||||
|
// Check if the key is present in the map
|
||||||
|
auto search = cached_map_.find(requested_key); |
||||||
|
if (search != cached_map_.end()) { |
||||||
|
std::cout << "Key " << requested_key << "found in map"; |
||||||
|
response_ = search->second; |
||||||
|
} else { |
||||||
|
std::cout << "Key " << requested_key << "not found in cache"; |
||||||
|
// Key was not found in the cache, so make a request
|
||||||
|
keyvaluestore::Request req; |
||||||
|
req.set_key(requested_key); |
||||||
|
stream_->Write(req); |
||||||
|
keyvaluestore::Response resp; |
||||||
|
stream_->Read(&resp); |
||||||
|
response_ = resp.value(); |
||||||
|
// Insert the pair in the cache for future requests
|
||||||
|
cached_map_.insert({requested_key, response_}); |
||||||
|
} |
||||||
|
} |
||||||
|
if (methods->QueryInterceptionHookPoint( |
||||||
|
grpc::experimental::InterceptionHookPoints::PRE_SEND_CLOSE)) { |
||||||
|
stream_->WritesDone(); |
||||||
|
} |
||||||
|
if (methods->QueryInterceptionHookPoint( |
||||||
|
grpc::experimental::InterceptionHookPoints::PRE_RECV_MESSAGE)) { |
||||||
|
keyvaluestore::Response* resp = |
||||||
|
static_cast<keyvaluestore::Response*>(methods->GetRecvMessage()); |
||||||
|
resp->set_value(response_); |
||||||
|
} |
||||||
|
if (methods->QueryInterceptionHookPoint( |
||||||
|
grpc::experimental::InterceptionHookPoints::PRE_RECV_STATUS)) { |
||||||
|
auto* status = methods->GetRecvStatus(); |
||||||
|
*status = grpc::Status::OK; |
||||||
|
} |
||||||
|
if (hijack) { |
||||||
|
methods->Hijack(); |
||||||
|
} else { |
||||||
|
methods->Proceed(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
grpc::ClientContext context_; |
||||||
|
std::unique_ptr<keyvaluestore::KeyValueStore::Stub> stub_; |
||||||
|
std::unique_ptr< |
||||||
|
grpc::ClientReaderWriter<keyvaluestore::Request, keyvaluestore::Response>> |
||||||
|
stream_; |
||||||
|
std::map<std::string, std::string> cached_map_; |
||||||
|
std::string response_; |
||||||
|
}; |
||||||
|
|
||||||
|
class CachingInterceptorFactory |
||||||
|
: public grpc::experimental::ClientInterceptorFactoryInterface { |
||||||
|
public: |
||||||
|
grpc::experimental::Interceptor* CreateClientInterceptor( |
||||||
|
grpc::experimental::ClientRpcInfo* info) override { |
||||||
|
return new CachingInterceptor(info); |
||||||
|
} |
||||||
|
}; |
Loading…
Reference in new issue