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 <string.h> 00018 00019 #include "include/LogCabin/Client.h" 00020 #include "Client/ClientImpl.h" 00021 #include "Client/MockClientImpl.h" 00022 #include "Core/StringUtil.h" 00023 00024 namespace LogCabin { 00025 namespace Client { 00026 00027 namespace { 00028 void throwException(const Result& result, uint64_t timeoutNanos) 00029 { 00030 switch (result.status) { 00031 case Status::OK: 00032 return; 00033 case Status::INVALID_ARGUMENT: 00034 throw InvalidArgumentException(result.error); 00035 case Status::LOOKUP_ERROR: 00036 throw LookupException(result.error); 00037 case Status::TYPE_ERROR: 00038 throw TypeException(result.error); 00039 case Status::CONDITION_NOT_MET: 00040 throw ConditionNotMetException(result.error); 00041 case Status::TIMEOUT: 00042 { 00043 throw TimeoutException(result.error + ": " + 00044 Core::StringUtil::toString(timeoutNanos) + 00045 "ns"); 00046 } 00047 } 00048 } 00049 } // anonymous namespace 00050 00051 00052 ////////// Server ////////// 00053 00054 Server::Server(uint64_t serverId, const std::string& addresses) 00055 : serverId(serverId) 00056 , addresses(addresses) 00057 { 00058 } 00059 00060 Server::Server() 00061 : serverId(~0UL) 00062 , addresses("") 00063 { 00064 } 00065 00066 Server::Server(const Server& other) 00067 : serverId(other.serverId) 00068 , addresses(other.addresses) 00069 { 00070 } 00071 00072 Server::~Server() 00073 { 00074 } 00075 00076 Server& 00077 Server::operator=(const Server& other) 00078 { 00079 serverId = other.serverId; 00080 addresses = other.addresses; 00081 return *this; 00082 } 00083 00084 ////////// GetConfigurationResult ////////// 00085 00086 GetConfigurationResult::GetConfigurationResult() 00087 : status(OK) 00088 , configuration(0) 00089 , servers() 00090 , error() 00091 { 00092 } 00093 00094 GetConfigurationResult::~GetConfigurationResult() 00095 { 00096 } 00097 00098 ////////// ConfigurationResult ////////// 00099 00100 ConfigurationResult::ConfigurationResult() 00101 : status(OK) 00102 , badServers() 00103 , error() 00104 { 00105 } 00106 00107 ConfigurationResult::~ConfigurationResult() 00108 { 00109 } 00110 00111 ////////// enum Status ////////// 00112 00113 std::ostream& 00114 operator<<(std::ostream& os, Status status) 00115 { 00116 switch (status) { 00117 case Status::OK: 00118 os << "Status::OK"; 00119 break; 00120 case Status::INVALID_ARGUMENT: 00121 os << "Status::INVALID_ARGUMENT"; 00122 break; 00123 case Status::LOOKUP_ERROR: 00124 os << "Status::LOOKUP_ERROR"; 00125 break; 00126 case Status::TYPE_ERROR: 00127 os << "Status::TYPE_ERROR"; 00128 break; 00129 case Status::CONDITION_NOT_MET: 00130 os << "Status::CONDITION_NOT_MET"; 00131 break; 00132 case Status::TIMEOUT: 00133 os << "Status::TIMEOUT"; 00134 break; 00135 } 00136 return os; 00137 } 00138 00139 ////////// struct Result ////////// 00140 00141 Result::Result() 00142 : status(Status::OK) 00143 , error() 00144 { 00145 } 00146 00147 ////////// class Exception ////////// 00148 00149 Exception::Exception(const std::string& error) 00150 : std::runtime_error(error) 00151 { 00152 } 00153 00154 InvalidArgumentException::InvalidArgumentException(const std::string& error) 00155 : Exception(error) 00156 { 00157 } 00158 00159 LookupException::LookupException(const std::string& error) 00160 : Exception(error) 00161 { 00162 } 00163 00164 TypeException::TypeException(const std::string& error) 00165 : Exception(error) 00166 { 00167 } 00168 00169 ConditionNotMetException::ConditionNotMetException(const std::string& error) 00170 : Exception(error) 00171 { 00172 } 00173 00174 TimeoutException::TimeoutException(const std::string& error) 00175 : Exception(error) 00176 { 00177 } 00178 00179 ConfigurationExceptionBad::ConfigurationExceptionBad(const std::string& error) 00180 : Exception(error) 00181 { 00182 } 00183 00184 ConfigurationExceptionChanged::ConfigurationExceptionChanged( 00185 const std::string& error) 00186 : Exception(error) 00187 { 00188 } 00189 00190 ////////// TreeDetails ////////// 00191 00192 /** 00193 * Implementation-specific members of Client::Tree. 00194 */ 00195 class TreeDetails { 00196 public: 00197 TreeDetails(std::shared_ptr<ClientImpl> clientImpl, 00198 const std::string& workingDirectory) 00199 : clientImpl(clientImpl) 00200 , workingDirectory(workingDirectory) 00201 , condition() 00202 , timeoutNanos(0) 00203 { 00204 } 00205 /** 00206 * Client implementation. 00207 */ 00208 std::shared_ptr<ClientImpl> clientImpl; 00209 /** 00210 * The current working directory for the Tree (an absolute path). 00211 */ 00212 std::string workingDirectory; 00213 /** 00214 * If set, specifies a predicate that must hold for operations to take 00215 * effect. 00216 */ 00217 Condition condition; 00218 /** 00219 * If nonzero, a relative timeout in nanoseconds for all Tree operations. 00220 */ 00221 uint64_t timeoutNanos; 00222 }; 00223 00224 00225 ////////// Tree ////////// 00226 00227 Tree::Tree(std::shared_ptr<ClientImpl> clientImpl, 00228 const std::string& workingDirectory) 00229 : mutex() 00230 , treeDetails(new TreeDetails(clientImpl, workingDirectory)) 00231 { 00232 } 00233 00234 Tree::Tree(const Tree& other) 00235 : mutex() 00236 , treeDetails(other.getTreeDetails()) 00237 { 00238 } 00239 00240 Tree& 00241 Tree::operator=(const Tree& other) 00242 { 00243 // Hold one lock at a time to avoid deadlock and handle self-assignment. 00244 std::shared_ptr<const TreeDetails> otherTreeDetails = 00245 other.getTreeDetails(); 00246 std::lock_guard<std::mutex> lockGuard(mutex); 00247 treeDetails = otherTreeDetails; 00248 return *this; 00249 } 00250 00251 Result 00252 Tree::setWorkingDirectory(const std::string& newWorkingDirectory) 00253 { 00254 // This method sets the working directory regardless of whether it 00255 // succeeds -- that way if it doesn't, future relative paths on this Tree 00256 // will result in errors instead of operating on the prior working 00257 // directory. 00258 00259 ClientImpl::TimePoint timeout = 00260 ClientImpl::absTimeout(treeDetails->timeoutNanos); 00261 00262 std::lock_guard<std::mutex> lockGuard(mutex); 00263 std::string realPath; 00264 Result result = treeDetails->clientImpl->canonicalize( 00265 newWorkingDirectory, 00266 treeDetails->workingDirectory, 00267 realPath); 00268 std::shared_ptr<TreeDetails> newTreeDetails(new TreeDetails(*treeDetails)); 00269 if (result.status != Status::OK) { 00270 newTreeDetails->workingDirectory = Core::StringUtil::format( 00271 "invalid from prior call to setWorkingDirectory('%s') " 00272 "relative to '%s'", 00273 newWorkingDirectory.c_str(), 00274 treeDetails->workingDirectory.c_str()); 00275 treeDetails = newTreeDetails; 00276 return result; 00277 } 00278 newTreeDetails->workingDirectory = realPath; 00279 treeDetails = newTreeDetails; 00280 return treeDetails->clientImpl->makeDirectory(realPath, "", 00281 treeDetails->condition, 00282 timeout); 00283 } 00284 00285 void 00286 Tree::setWorkingDirectoryEx(const std::string& workingDirectory) 00287 { 00288 throwException(setWorkingDirectory(workingDirectory), 00289 treeDetails->timeoutNanos); 00290 } 00291 00292 std::string 00293 Tree::getWorkingDirectory() const 00294 { 00295 std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails(); 00296 return treeDetails->workingDirectory; 00297 } 00298 00299 Result 00300 Tree::setCondition(const std::string& path, const std::string& value) 00301 { 00302 // This method sets the condition regardless of whether it succeeds -- that 00303 // way if it doesn't, future calls on this Tree will result in errors 00304 // instead of operating on the prior condition. 00305 00306 std::lock_guard<std::mutex> lockGuard(mutex); 00307 std::string realPath; 00308 std::shared_ptr<TreeDetails> newTreeDetails(new TreeDetails(*treeDetails)); 00309 if (path.empty()) { 00310 newTreeDetails->condition = {"", ""}; 00311 } else { 00312 Result result = treeDetails->clientImpl->canonicalize( 00313 path, 00314 treeDetails->workingDirectory, 00315 realPath); 00316 if (result.status != Status::OK) { 00317 newTreeDetails->condition = { 00318 Core::StringUtil::format( 00319 "invalid from prior call to setCondition('%s') " 00320 "relative to '%s'", 00321 path.c_str(), 00322 treeDetails->workingDirectory.c_str()), 00323 value, 00324 }; 00325 treeDetails = newTreeDetails; 00326 return result; 00327 } 00328 newTreeDetails->condition = {realPath, value}; 00329 } 00330 treeDetails = newTreeDetails; 00331 return Result(); 00332 } 00333 00334 void 00335 Tree::setConditionEx(const std::string& path, const std::string& value) 00336 { 00337 throwException(setCondition(path, value), treeDetails->timeoutNanos); 00338 } 00339 00340 std::pair<std::string, std::string> 00341 Tree::getCondition() const 00342 { 00343 std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails(); 00344 return treeDetails->condition; 00345 } 00346 00347 uint64_t 00348 Tree::getTimeout() const 00349 { 00350 std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails(); 00351 return treeDetails->timeoutNanos; 00352 } 00353 00354 void 00355 Tree::setTimeout(uint64_t nanoseconds) 00356 { 00357 std::lock_guard<std::mutex> lockGuard(mutex); 00358 std::shared_ptr<TreeDetails> newTreeDetails(new TreeDetails(*treeDetails)); 00359 newTreeDetails->timeoutNanos = nanoseconds; 00360 treeDetails = newTreeDetails; 00361 } 00362 00363 Result 00364 Tree::makeDirectory(const std::string& path) 00365 { 00366 std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails(); 00367 return treeDetails->clientImpl->makeDirectory( 00368 path, 00369 treeDetails->workingDirectory, 00370 treeDetails->condition, 00371 ClientImpl::absTimeout(treeDetails->timeoutNanos)); 00372 } 00373 00374 void 00375 Tree::makeDirectoryEx(const std::string& path) 00376 { 00377 throwException(makeDirectory(path), treeDetails->timeoutNanos); 00378 } 00379 00380 Result 00381 Tree::listDirectory(const std::string& path, 00382 std::vector<std::string>& children) const 00383 { 00384 std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails(); 00385 return treeDetails->clientImpl->listDirectory( 00386 path, 00387 treeDetails->workingDirectory, 00388 treeDetails->condition, 00389 ClientImpl::absTimeout(treeDetails->timeoutNanos), 00390 children); 00391 } 00392 00393 std::vector<std::string> 00394 Tree::listDirectoryEx(const std::string& path) const 00395 { 00396 std::vector<std::string> children; 00397 throwException(listDirectory(path, children), treeDetails->timeoutNanos); 00398 return children; 00399 } 00400 00401 Result 00402 Tree::removeDirectory(const std::string& path) 00403 { 00404 std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails(); 00405 return treeDetails->clientImpl->removeDirectory( 00406 path, 00407 treeDetails->workingDirectory, 00408 treeDetails->condition, 00409 ClientImpl::absTimeout(treeDetails->timeoutNanos)); 00410 } 00411 00412 void 00413 Tree::removeDirectoryEx(const std::string& path) 00414 { 00415 throwException(removeDirectory(path), treeDetails->timeoutNanos); 00416 } 00417 00418 Result 00419 Tree::write(const std::string& path, const std::string& contents) 00420 { 00421 std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails(); 00422 return treeDetails->clientImpl->write( 00423 path, 00424 treeDetails->workingDirectory, 00425 contents, 00426 treeDetails->condition, 00427 ClientImpl::absTimeout(treeDetails->timeoutNanos)); 00428 } 00429 00430 void 00431 Tree::writeEx(const std::string& path, const std::string& contents) 00432 { 00433 throwException(write(path, contents), treeDetails->timeoutNanos); 00434 } 00435 00436 Result 00437 Tree::read(const std::string& path, std::string& contents) const 00438 { 00439 std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails(); 00440 return treeDetails->clientImpl->read( 00441 path, 00442 treeDetails->workingDirectory, 00443 treeDetails->condition, 00444 ClientImpl::absTimeout(treeDetails->timeoutNanos), 00445 contents); 00446 } 00447 00448 std::string 00449 Tree::readEx(const std::string& path) const 00450 { 00451 std::string contents; 00452 throwException(read(path, contents), treeDetails->timeoutNanos); 00453 return contents; 00454 } 00455 00456 Result 00457 Tree::removeFile(const std::string& path) 00458 { 00459 std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails(); 00460 return treeDetails->clientImpl->removeFile( 00461 path, 00462 treeDetails->workingDirectory, 00463 treeDetails->condition, 00464 ClientImpl::absTimeout(treeDetails->timeoutNanos)); 00465 } 00466 00467 void 00468 Tree::removeFileEx(const std::string& path) 00469 { 00470 throwException(removeFile(path), treeDetails->timeoutNanos); 00471 } 00472 00473 std::shared_ptr<const TreeDetails> 00474 Tree::getTreeDetails() const 00475 { 00476 std::shared_ptr<const TreeDetails> ret; 00477 std::lock_guard<std::mutex> lockGuard(mutex); 00478 ret = treeDetails; 00479 return ret; 00480 } 00481 00482 ////////// TestingCallbacks ////////// 00483 00484 TestingCallbacks::TestingCallbacks() 00485 { 00486 } 00487 00488 TestingCallbacks::~TestingCallbacks() 00489 { 00490 } 00491 00492 bool 00493 TestingCallbacks::stateMachineQuery( 00494 Protocol::Client::StateMachineQuery_Request& request, 00495 Protocol::Client::StateMachineQuery_Response& response) 00496 { 00497 return false; 00498 } 00499 00500 bool 00501 TestingCallbacks::stateMachineCommand( 00502 Protocol::Client::StateMachineCommand_Request& request, 00503 Protocol::Client::StateMachineCommand_Response& response) 00504 { 00505 return false; 00506 } 00507 00508 ////////// Cluster ////////// 00509 00510 Cluster::Cluster(std::shared_ptr<TestingCallbacks> testingCallbacks, 00511 const std::map<std::string, std::string>& options) 00512 : clientImpl(std::make_shared<MockClientImpl>( 00513 testingCallbacks ? testingCallbacks 00514 : std::make_shared<TestingCallbacks>())) 00515 { 00516 clientImpl->init("-MOCK-"); 00517 } 00518 00519 Cluster::Cluster(const std::string& hosts, 00520 const std::map<std::string, std::string>& options) 00521 : clientImpl(std::make_shared<ClientImpl>(options)) 00522 { 00523 #if DEBUG // for testing purposes only 00524 if (hosts == "-MOCK-SKIP-INIT-") 00525 return; 00526 #endif 00527 clientImpl->init(hosts); 00528 } 00529 00530 Cluster::~Cluster() 00531 { 00532 } 00533 00534 std::pair<uint64_t, Configuration> 00535 Cluster::getConfiguration() const 00536 { 00537 const uint64_t timeout(0); 00538 GetConfigurationResult r = getConfiguration2(timeout); 00539 return std::make_pair(r.configuration, r.servers); 00540 } 00541 00542 GetConfigurationResult 00543 Cluster::getConfiguration2(uint64_t timeoutNanoseconds) const 00544 { 00545 return clientImpl->getConfiguration( 00546 ClientImpl::absTimeout(timeoutNanoseconds)); 00547 } 00548 00549 GetConfigurationResult 00550 Cluster::getConfiguration2Ex(uint64_t timeoutNanoseconds) const 00551 { 00552 GetConfigurationResult result(getConfiguration2(timeoutNanoseconds)); 00553 if (result.status == GetConfigurationResult::TIMEOUT) { 00554 throw TimeoutException(result.error); 00555 } 00556 return result; 00557 } 00558 00559 ConfigurationResult 00560 Cluster::setConfiguration(uint64_t oldId, 00561 const Configuration& newConfiguration) 00562 { 00563 const uint64_t timeout(0); 00564 return setConfiguration2(oldId, newConfiguration, timeout); 00565 } 00566 00567 ConfigurationResult 00568 Cluster::setConfiguration2(uint64_t oldId, 00569 const Configuration& newConfiguration, 00570 uint64_t timeoutNanoseconds) 00571 { 00572 return clientImpl->setConfiguration( 00573 oldId, newConfiguration, ClientImpl::absTimeout(timeoutNanoseconds)); 00574 } 00575 00576 ConfigurationResult 00577 Cluster::setConfiguration2Ex(uint64_t oldId, 00578 const Configuration& newConfiguration, 00579 uint64_t timeoutNanoseconds) 00580 { 00581 ConfigurationResult result = setConfiguration2(oldId, 00582 newConfiguration, 00583 timeoutNanoseconds); 00584 if (result.status == ConfigurationResult::BAD) { 00585 throw ConfigurationExceptionBad(result.error); 00586 } 00587 if (result.status == ConfigurationResult::CHANGED) { 00588 throw ConfigurationExceptionChanged(result.error); 00589 } 00590 if (result.status == ConfigurationResult::TIMEOUT) { 00591 throw TimeoutException(result.error); 00592 } 00593 return result; 00594 } 00595 00596 Result 00597 Cluster::getServerInfo(const std::string& host, 00598 uint64_t timeoutNanoseconds, 00599 Server& info) 00600 { 00601 return clientImpl->getServerInfo( 00602 host, 00603 ClientImpl::absTimeout(timeoutNanoseconds), 00604 info); 00605 } 00606 00607 Server 00608 Cluster::getServerInfoEx(const std::string& host, 00609 uint64_t timeoutNanoseconds) 00610 { 00611 Server info; 00612 throwException(getServerInfo(host, timeoutNanoseconds, info), 00613 timeoutNanoseconds); 00614 return info; 00615 } 00616 00617 Result 00618 Cluster::getServerStats(const std::string& host, 00619 uint64_t timeoutNanoseconds, 00620 Protocol::ServerStats& stats) 00621 { 00622 00623 Protocol::ServerControl::ServerStatsGet::Request request; 00624 Protocol::ServerControl::ServerStatsGet::Response response; 00625 Result result = clientImpl->serverControl( 00626 host, 00627 ClientImpl::absTimeout(timeoutNanoseconds), 00628 Protocol::ServerControl::OpCode::SERVER_STATS_GET, 00629 request, 00630 response); 00631 stats = response.server_stats(); 00632 return result; 00633 } 00634 00635 Protocol::ServerStats 00636 Cluster::getServerStatsEx(const std::string& host, 00637 uint64_t timeoutNanoseconds) 00638 { 00639 Protocol::ServerStats stats; 00640 throwException(getServerStats(host, timeoutNanoseconds, stats), 00641 timeoutNanoseconds); 00642 return stats; 00643 } 00644 00645 Tree 00646 Cluster::getTree() 00647 { 00648 return Tree(clientImpl, "/"); 00649 } 00650 00651 } // namespace LogCabin::Client 00652 } // namespace LogCabin