diff --git a/libs/libzkutil/include/zkutil/KeeperException.h b/libs/libzkutil/include/zkutil/KeeperException.h index ca8a5078fba..3780aab7cd9 100644 --- a/libs/libzkutil/include/zkutil/KeeperException.h +++ b/libs/libzkutil/include/zkutil/KeeperException.h @@ -11,9 +11,9 @@ class KeeperException : public Poco::Exception public: KeeperException(const std::string & msg) : Poco::Exception(msg), code(ReturnCode::Ok) {} KeeperException(const std::string & msg, ReturnCode::type code_) - : Poco::Exception(msg + " (" + ReturnCode::toString(code) + ")"), code(code_) {} + : Poco::Exception(msg + " (" + ReturnCode::toString(code_) + ")"), code(code_) {} KeeperException(ReturnCode::type code_) - : Poco::Exception(ReturnCode::toString(code)), code(code_) {} + : Poco::Exception(ReturnCode::toString(code_)), code(code_) {} ReturnCode::type code; }; diff --git a/libs/libzkutil/include/zkutil/ZooKeeper.h b/libs/libzkutil/include/zkutil/ZooKeeper.h index 1578c32bb30..d2f94e0c1eb 100644 --- a/libs/libzkutil/include/zkutil/ZooKeeper.h +++ b/libs/libzkutil/include/zkutil/ZooKeeper.h @@ -49,7 +49,11 @@ class ZooKeeper public: ZooKeeper(const std::string & hosts, int32_t sessionTimeoutMs = DEFAULT_SESSION_TIMEOUT, WatchFunction * watch = nullptr); - /// Возвращает true, если сессия навсегда завершена. + /** Возвращает true, если сессия навсегда завершена. + * Это возможно только если соединение было установлено, а потом разорвалось. Это достаточно редкая ситуация. + * С другой стороны, если, например, указан неправильный сервер или порт, попытки соединения будут продолжаться бесконечно, + * disconnected() будет возвращать false, и все вызовы будут выбрасывать исключение ConnectionLoss. + */ bool disconnected(); void setDefaultACL(ACLs & acl); @@ -80,7 +84,7 @@ public: bool exists(const std::string & path, Stat * stat = nullptr, WatchFuture * watch = nullptr); - std::string get(const std::string & path, WatchFuture * watch, Stat * stat); + std::string get(const std::string & path, Stat * stat, WatchFuture * watch); /// Возвращает false, если нет такой ноды. При остальных ошибках бросает исключение. bool tryGet(const std::string & path, std::string & data, Stat * stat = nullptr, WatchFuture * watch = nullptr); @@ -92,6 +96,8 @@ public: Stat * stat = nullptr, WatchFuture * watch = nullptr); + void close(); + boost::ptr_vector & multi(const boost::ptr_vector & ops); private: diff --git a/libs/libzkutil/src/ZooKeeper.cpp b/libs/libzkutil/src/ZooKeeper.cpp index b1bb2da992b..f2d9a68c236 100644 --- a/libs/libzkutil/src/ZooKeeper.cpp +++ b/libs/libzkutil/src/ZooKeeper.cpp @@ -3,7 +3,7 @@ #include -#define CHECKED(x) { ReturnCode::type code = x; if (code) throw KeeperException(code); } +#define CHECKED(x) { ReturnCode::type code = x; if (code != ReturnCode::Ok) throw KeeperException(code); } namespace zkutil { @@ -73,7 +73,7 @@ void ZooKeeper::setDefaultACL(ACLs & acl) default_acl = acl; } -std::vector ZooKeeper::getChildren( +Strings ZooKeeper::getChildren( const std::string & path, Stat * stat, WatchFuture * watch) { Stat s; @@ -84,5 +84,88 @@ std::vector ZooKeeper::getChildren( return res; } +std::string ZooKeeper::create(const std::string & path, const std::string & data, CreateMode::type mode) +{ + std::string res; + CHECKED(impl.create(path, data, default_acl, mode, res)); + return res; } +ReturnCode::type ZooKeeper::tryCreate(const std::string & path, const std::string & data, CreateMode::type mode, std::string & pathCreated) +{ + ReturnCode::type code = impl.create(path, data, default_acl, mode, pathCreated); + if (!( code == ReturnCode::Ok || + code == ReturnCode::NoNode || + code == ReturnCode::NodeExists || + code == ReturnCode::NoChildrenForEphemerals)) + return code; + throw KeeperException(code); +} + +void ZooKeeper::remove(const std::string & path, int32_t version) +{ + CHECKED(impl.remove(path, version)); +} + + /** Не бросает исключение при следующих ошибках: + * - Такой ноды нет. + * - У ноды другая версия. + * - У ноды есть дети. + */ +ReturnCode::type ZooKeeper::tryRemove(const std::string & path, int32_t version) +{ + ReturnCode::type code = impl.remove(path, version); + if (!( code == ReturnCode::Ok || + code == ReturnCode::NoNode || + code == ReturnCode::BadVersion || + code == ReturnCode::NotEmpty)) + return code; + throw KeeperException(code); +} + +bool ZooKeeper::exists(const std::string & path, Stat * stat, WatchFuture * watch) +{ + Stat s; + ReturnCode::type code = impl.exists(path, watchForFuture(watch), s); + if (code != ReturnCode::Ok && code != ReturnCode::NoNode) + throw KeeperException(code); + if (stat) + *stat = s; + return code == ReturnCode::Ok; +} + +std::string ZooKeeper::get(const std::string & path, Stat * stat, WatchFuture * watch) +{ + std::string res; + Stat s; + CHECKED(impl.get(path, watchForFuture(watch), res, s)); + if (stat) + *stat = s; + return res; +} + +bool ZooKeeper::tryGet(const std::string & path, std::string & data, Stat * stat, WatchFuture * watch) +{ + Stat s; + ReturnCode::type code = impl.get(path, watchForFuture(watch), data, s); + if (code == ReturnCode::NoNode) + return false; + if (stat) + *stat = s; + return true; +} + +void ZooKeeper::set(const std::string & path, const std::string & data, int32_t version, Stat * stat) +{ + Stat s; + CHECKED(impl.set(path, data, version, s)); + if (stat) + *stat = s; +} + +void ZooKeeper::close() +{ + CHECKED(impl.close()); +} + +} diff --git a/libs/libzkutil/src/tests/zkutil_test.cpp b/libs/libzkutil/src/tests/zkutil_test.cpp index a53fcc5a050..41d06fdcfb5 100644 --- a/libs/libzkutil/src/tests/zkutil_test.cpp +++ b/libs/libzkutil/src/tests/zkutil_test.cpp @@ -3,6 +3,32 @@ #include #include #include +#include +#include + + +void printStat(const zkutil::Stat & s) +{ + std::cout << "Stat:\n"; + std::cout << " czxid: " << s.getczxid() << '\n'; + std::cout << " mzxid: " << s.getmzxid() << '\n'; + std::cout << " ctime: " << s.getctime() << '\n'; + std::cout << " mtime: " << s.getmtime() << '\n'; + std::cout << " version: " << s.getversion() << '\n'; + std::cout << " cversion: " << s.getcversion() << '\n'; + std::cout << " aversion: " << s.getaversion() << '\n'; + std::cout << " ephemeralOwner: " << s.getephemeralOwner() << '\n'; + std::cout << " dataLength: " << s.getdataLength() << '\n'; + std::cout << " numChildren: " << s.getnumChildren() << '\n'; + std::cout << " pzxid: " << s.getpzxid() << std::endl; +} + +void waitForWatch(zkutil::WatchFuture & future) +{ + std::cout << "waiting for watch" << std::endl; + zkutil::WatchEventInfo res = future.get(); + std::cout << "event: " << zkutil::WatchEvent::toString(res.event) << std::endl; +} int main(int argc, char ** argv) @@ -15,42 +41,127 @@ int main(int argc, char ** argv) return 2; } + Logger::root().setChannel(new Poco::ConsoleChannel(std::cout)); + Logger::root().setLevel("trace"); + zkutil::ZooKeeper zk(argv[1]); while (char * line = readline(":3 ")) { + if (zk.disconnected()) + { + std::cerr << "Disconnected" << std::endl; + break; + } + try { std::stringstream ss(line); std::string cmd; - if (!(ss >> cmd)) - continue; + ss >> cmd; if (cmd == "q" || cmd == "quit" || cmd == "exit" || cmd == ":q") break; - if (cmd == "help") - { - std::cout << "commands: q, ls (not yet: stat, get, set, create, remove)" << std::endl; - continue; - } std::string path; ss >> path; if (cmd == "ls") { - std::vector v = zk.getChildren(path); + std::string w; + ss >> w; + bool watch = w == "w"; + zkutil::WatchFuture future; + std::vector v = zk.getChildren(path, nullptr, watch ? &future : nullptr); for (size_t i = 0; i < v.size(); ++i) { std::cout << v[i] << std::endl; } + if (watch) + waitForWatch(future); } + else if (cmd == "create") + { + std::string data, mode; + ss >> data >> mode; + zkutil::CreateMode::type m; + if (mode == "p") + m = zkutil::CreateMode::Persistent; + else if (mode == "ps") + m = zkutil::CreateMode::PersistentSequential; + else if (mode == "e") + m = zkutil::CreateMode::Ephemeral; + else if (mode == "es") + m = zkutil::CreateMode::EphemeralSequential; + else + { + std::cout << "Bad create mode" << std::endl; + continue; + } + std::cout << zk.create(path, data, m) << std::endl; + } + else if (cmd == "remove") + { + zk.remove(path); + } + else if (cmd == "exists") + { + std::string w; + ss >> w; + bool watch = w == "w"; + zkutil::WatchFuture future; + zkutil::Stat stat; + bool e = zk.exists(path, &stat, watch ? &future : nullptr); + if (e) + printStat(stat); + else + std::cout << path << " does not exist" << std::endl; + if (watch) + waitForWatch(future); + } + else if (cmd == "get") + { + std::string w; + ss >> w; + bool watch = w == "w"; + zkutil::WatchFuture future; + zkutil::Stat stat; + std::string data = zk.get(path, &stat, watch ? &future : nullptr); + std::cout << "Data: " << data << std::endl; + printStat(stat); + if (watch) + waitForWatch(future); + } + else if (cmd == "set") + { + std::string data; + int version = -1; + ss >> data >> version; + zkutil::Stat stat; + zk.set(path, data, version, &stat); + printStat(stat); + } + else if (cmd != "") + { + std::cout << "commands:\n"; + std::cout << " q\n"; + std::cout << " ls path [w]\n"; + std::cout << " create path data (p|ps|e|es)\n"; + std::cout << " remove path\n"; + std::cout << " exists path [w]\n"; + std::cout << " get path [w]\n"; + std::cout << " set path data [version]" << std::endl; + continue; + } + } catch (zkutil::KeeperException & e) { std::cerr << "KeeperException: " << e.displayText() << std::endl; } } + + zk.close(); } catch (zkutil::KeeperException & e) {