|  | @@ -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);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 |