LogCabin
|
00001 /* Copyright (c) 2012 Stanford University 00002 * Copyright (c) 2014-2015 Diego Ongaro 00003 * 00004 * Permission to use, copy, modify, and distribute this software for any 00005 * purpose with or without fee is hereby granted, provided that the above 00006 * copyright notice and this permission notice appear in all copies. 00007 * 00008 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES 00009 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 00010 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR 00011 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 00012 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 00013 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 00014 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 00015 */ 00016 00017 #include "Core/Debug.h" 00018 #include "Core/ProtoBuf.h" 00019 #include "Core/STLUtil.h" 00020 #include "Client/MockClientImpl.h" 00021 #include "Tree/ProtoBuf.h" 00022 00023 namespace LogCabin { 00024 namespace Client { 00025 00026 namespace PC = LogCabin::Protocol::Client; 00027 00028 namespace { 00029 00030 template<typename T> 00031 struct RAIISwap { 00032 RAIISwap(T& a, T& b) 00033 : a(a) 00034 , b(b) 00035 { 00036 std::swap(a, b); 00037 } 00038 ~RAIISwap() 00039 { 00040 std::swap(a, b); 00041 } 00042 T& a; 00043 T& b; 00044 }; 00045 00046 typedef RAIISwap<std::shared_ptr<TestingCallbacks>> CallbackSwap; 00047 00048 /** 00049 * This class intercepts LeaderRPC calls from ClientImpl. 00050 * It's used to mock out the Tree RPCs by processing them directly. 00051 */ 00052 class TreeLeaderRPC : public LeaderRPCBase { 00053 public: 00054 explicit TreeLeaderRPC(std::shared_ptr<TestingCallbacks> callbacks) 00055 : mutex() 00056 , callbacks(callbacks) 00057 , tree() 00058 { 00059 } 00060 Status call(OpCode opCode, 00061 const google::protobuf::Message& request, 00062 google::protobuf::Message& response, 00063 TimePoint timeout) { 00064 std::lock_guard<std::recursive_mutex> lockGuard(mutex); 00065 if (opCode == OpCode::STATE_MACHINE_QUERY) { 00066 PC::StateMachineQuery::Request qrequest; 00067 qrequest.CopyFrom(request); 00068 auto& qresponse = 00069 static_cast<PC::StateMachineQuery::Response&>(response); 00070 auto localCallbacks = std::make_shared<TestingCallbacks>(); 00071 // set to noop for recursive calls, then restore 00072 CallbackSwap s(callbacks, localCallbacks); 00073 if (localCallbacks->stateMachineQuery(qrequest, qresponse)) 00074 return Status::OK; 00075 qresponse.Clear(); 00076 if (timeout < Clock::now()) 00077 return Status::TIMEOUT; 00078 if (qrequest.has_tree()) { 00079 LogCabin::Tree::ProtoBuf::readOnlyTreeRPC( 00080 tree, qrequest.tree(), *qresponse.mutable_tree()); 00081 return Status::OK; 00082 } 00083 } else if (opCode == OpCode::STATE_MACHINE_COMMAND) { 00084 PC::StateMachineCommand::Request crequest; 00085 crequest.CopyFrom(request); 00086 auto& cresponse = 00087 static_cast<PC::StateMachineCommand::Response&>(response); 00088 auto localCallbacks = std::make_shared<TestingCallbacks>(); 00089 // set to noop for recursive calls, then restore 00090 CallbackSwap s(callbacks, localCallbacks); 00091 if (localCallbacks->stateMachineCommand(crequest, cresponse)) 00092 return Status::OK; 00093 cresponse.Clear(); 00094 if (timeout < Clock::now()) 00095 return Status::TIMEOUT; 00096 if (crequest.has_tree()) { 00097 LogCabin::Tree::ProtoBuf::readWriteTreeRPC( 00098 tree, crequest.tree(), *cresponse.mutable_tree()); 00099 return Status::OK; 00100 } else if (crequest.has_open_session()) { 00101 cresponse.mutable_open_session()-> 00102 set_client_id(1); 00103 return Status::OK; 00104 } else if (crequest.has_close_session()) { 00105 return Status::OK; 00106 } 00107 } 00108 PANIC("Unexpected request: %d %s", 00109 opCode, 00110 Core::ProtoBuf::dumpString(request).c_str()); 00111 } 00112 00113 class Call : public LeaderRPCBase::Call { 00114 public: 00115 explicit Call(TreeLeaderRPC& leaderRPC) 00116 : leaderRPC(leaderRPC) 00117 , opCode() 00118 , request() 00119 { 00120 } 00121 void start(OpCode _opCode, 00122 const google::protobuf::Message& _request, 00123 TimePoint _timeout) { 00124 opCode = _opCode; 00125 request.reset(_request.New()); 00126 request->CopyFrom(_request); 00127 } 00128 void cancel() { 00129 // no op 00130 } 00131 Status wait(google::protobuf::Message& response, 00132 TimePoint timeout) { 00133 leaderRPC.call(opCode, *request, response, timeout); 00134 return Status::OK; 00135 } 00136 TreeLeaderRPC& leaderRPC; 00137 OpCode opCode; 00138 std::unique_ptr<google::protobuf::Message> request; 00139 00140 // Call is not copyable. 00141 Call(const Call&) = delete; 00142 Call& operator=(const Call&) = delete; 00143 }; 00144 00145 std::unique_ptr<LeaderRPCBase::Call> makeCall() { 00146 return std::unique_ptr<LeaderRPCBase::Call>(new Call(*this)); 00147 } 00148 00149 /** 00150 * Prevents concurrent access to callbacks and tree. It's recursive so that 00151 * you can call the client library from within MockCallbacks, if you're 00152 * that insane. 00153 */ 00154 std::recursive_mutex mutex; 00155 std::shared_ptr<TestingCallbacks> callbacks; 00156 LogCabin::Tree::Tree tree; 00157 }; 00158 } // anonymous namespace 00159 00160 MockClientImpl::MockClientImpl(std::shared_ptr<TestingCallbacks> callbacks) 00161 { 00162 leaderRPC.reset(new TreeLeaderRPC(callbacks)); 00163 } 00164 00165 MockClientImpl::~MockClientImpl() 00166 { 00167 } 00168 00169 void 00170 MockClientImpl::initDerived() 00171 { 00172 } 00173 00174 std::pair<uint64_t, Configuration> 00175 MockClientImpl::getConfiguration() 00176 { 00177 return {0, {}}; 00178 } 00179 00180 ConfigurationResult 00181 MockClientImpl::setConfiguration(uint64_t oldId, 00182 const Configuration& newConfiguration) 00183 { 00184 ConfigurationResult result; 00185 result.status = ConfigurationResult::BAD; 00186 result.badServers = newConfiguration; 00187 return result; 00188 } 00189 00190 } // namespace LogCabin::Client 00191 } // namespace LogCabin