2014-03-07 13:50:58 +00:00
|
|
|
|
#pragma once
|
2014-10-16 01:46:56 +00:00
|
|
|
|
|
2014-03-07 19:18:48 +00:00
|
|
|
|
#include <zkutil/Types.h>
|
|
|
|
|
#include <zkutil/KeeperException.h>
|
2014-03-13 14:49:17 +00:00
|
|
|
|
#include <Poco/Util/LayeredConfiguration.h>
|
2014-06-04 13:48:36 +00:00
|
|
|
|
#include <unordered_set>
|
2014-10-16 01:46:56 +00:00
|
|
|
|
#include <future>
|
2015-04-16 07:22:29 +00:00
|
|
|
|
#include <memory>
|
2016-01-24 05:00:24 +00:00
|
|
|
|
#include <mutex>
|
2016-01-21 16:30:05 +00:00
|
|
|
|
#include <string>
|
2015-09-29 19:19:54 +00:00
|
|
|
|
#include <common/logger_useful.h>
|
2016-10-24 13:47:15 +00:00
|
|
|
|
#include <DB/Common/ProfileEvents.h>
|
|
|
|
|
#include <DB/Common/CurrentMetrics.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace ProfileEvents
|
|
|
|
|
{
|
|
|
|
|
extern const Event CannotRemoveEphemeralNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace CurrentMetrics
|
|
|
|
|
{
|
|
|
|
|
extern const Metric EphemeralNode;
|
|
|
|
|
}
|
2014-03-07 13:50:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace zkutil
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
const UInt32 DEFAULT_SESSION_TIMEOUT = 30000;
|
2015-01-22 10:33:20 +00:00
|
|
|
|
const UInt32 MEDIUM_SESSION_TIMEOUT = 120000;
|
2014-11-26 18:58:31 +00:00
|
|
|
|
const UInt32 BIG_SESSION_TIMEOUT = 600000;
|
2014-03-07 13:50:58 +00:00
|
|
|
|
|
2017-03-17 00:44:00 +00:00
|
|
|
|
struct WatchContext;
|
2014-06-04 13:48:36 +00:00
|
|
|
|
|
2016-04-09 02:03:44 +00:00
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// ZooKeeper session. The interface is substantially different from the usual libzookeeper API.
|
|
|
|
|
///
|
|
|
|
|
/// Poco::Event objects are used for watches. The event is set only once on the first
|
|
|
|
|
/// watch notification.
|
|
|
|
|
/// Callback-based watch interface is also provided.
|
|
|
|
|
///
|
|
|
|
|
/// Read-only methods retry retry_num times if recoverable errors like OperationTimeout
|
|
|
|
|
/// or ConnectionLoss are encountered.
|
|
|
|
|
///
|
|
|
|
|
/// Modifying methods do not retry, because it leads to problems of the double-delete type.
|
|
|
|
|
///
|
|
|
|
|
/// Methods with names not starting at try- raise KeeperException on any error.
|
|
|
|
|
class ZooKeeper
|
2014-03-07 13:50:58 +00:00
|
|
|
|
{
|
|
|
|
|
public:
|
2016-04-09 02:03:44 +00:00
|
|
|
|
using Ptr = std::shared_ptr<ZooKeeper>;
|
2014-04-24 08:27:39 +00:00
|
|
|
|
|
2014-11-26 18:58:51 +00:00
|
|
|
|
ZooKeeper(const std::string & hosts, int32_t session_timeout_ms = DEFAULT_SESSION_TIMEOUT);
|
2014-03-07 13:50:58 +00:00
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/** Config of the form:
|
2014-03-13 14:56:31 +00:00
|
|
|
|
<zookeeper>
|
|
|
|
|
<node>
|
|
|
|
|
<host>example1</host>
|
|
|
|
|
<port>2181</port>
|
|
|
|
|
</node>
|
|
|
|
|
<node>
|
|
|
|
|
<host>example2</host>
|
|
|
|
|
<port>2181</port>
|
|
|
|
|
</node>
|
|
|
|
|
<session_timeout_ms>30000</session_timeout_ms>
|
|
|
|
|
</zookeeper>
|
|
|
|
|
*/
|
2014-07-08 12:45:10 +00:00
|
|
|
|
ZooKeeper(const Poco::Util::AbstractConfiguration & config, const std::string & config_name);
|
2014-03-13 14:49:17 +00:00
|
|
|
|
|
2014-03-22 14:44:44 +00:00
|
|
|
|
~ZooKeeper();
|
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Creates a new session with the same parameters. This method can be used for reconnecting
|
|
|
|
|
/// after the session has expired.
|
|
|
|
|
/// This object remains unchanged, and the new session is returned.
|
2014-04-24 08:27:39 +00:00
|
|
|
|
Ptr startNewSession() const;
|
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Returns true, if the session has expired forever.
|
|
|
|
|
/// This is possible only if the connection has been established, then lost and re-established
|
|
|
|
|
/// again, but too late.
|
|
|
|
|
/// In contrast, if, for instance, the server name or port is misconfigured, connection
|
|
|
|
|
/// attempts will continue indefinitely, expired() will return false and all method calls
|
|
|
|
|
/// will raise ConnectionLoss exception.
|
|
|
|
|
/// Also returns true if is_dirty flag is set - a request to close the session ASAP.
|
2014-04-24 08:27:39 +00:00
|
|
|
|
bool expired();
|
2014-03-07 13:50:58 +00:00
|
|
|
|
|
2014-11-30 07:01:00 +00:00
|
|
|
|
ACLPtr getDefaultACL();
|
2014-03-07 13:50:58 +00:00
|
|
|
|
|
2014-11-30 07:01:00 +00:00
|
|
|
|
void setDefaultACL(ACLPtr new_acl);
|
2014-03-07 19:18:48 +00:00
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Create a znode. ACL set by setDefaultACL is used (full access to everybody by default).
|
|
|
|
|
/// Throw an exception if something went wrong.
|
2014-06-04 13:48:36 +00:00
|
|
|
|
std::string create(const std::string & path, const std::string & data, int32_t mode);
|
2014-03-07 13:50:58 +00:00
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Doesn not throw in the following cases:
|
|
|
|
|
/// * The parent for the created node does not exist
|
|
|
|
|
/// * The parent is ephemeral.
|
|
|
|
|
/// * The node already exists.
|
|
|
|
|
/// In case of other errors throws an exception.
|
2016-04-09 02:03:44 +00:00
|
|
|
|
int32_t tryCreate(const std::string & path, const std::string & data, int32_t mode, std::string & path_created);
|
2014-06-04 13:48:36 +00:00
|
|
|
|
int32_t tryCreate(const std::string & path, const std::string & data, int32_t mode);
|
2014-07-03 17:24:17 +00:00
|
|
|
|
int32_t tryCreateWithRetries(const std::string & path, const std::string & data, int32_t mode,
|
2016-04-09 02:03:44 +00:00
|
|
|
|
std::string & path_created, size_t * attempt = nullptr);
|
2014-03-07 13:50:58 +00:00
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Create a Persistent node.
|
|
|
|
|
/// Does nothing if the node already exists.
|
|
|
|
|
/// Retries on ConnectionLoss or OperationTimeout.
|
2014-06-27 17:52:50 +00:00
|
|
|
|
void createIfNotExists(const std::string & path, const std::string & data);
|
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Creates all non-existent ancestors of the given path with empty contents.
|
|
|
|
|
/// Does not create the node itself.
|
2014-08-11 14:05:38 +00:00
|
|
|
|
void createAncestors(const std::string & path);
|
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Remove the node if the version matches. (if version == -1, remove any version).
|
2014-03-07 13:50:58 +00:00
|
|
|
|
void remove(const std::string & path, int32_t version = -1);
|
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Removes the node. In case of network errors tries to remove again.
|
|
|
|
|
/// ZNONODE error for the second and the following tries is ignored.
|
2015-05-15 14:53:39 +00:00
|
|
|
|
void removeWithRetries(const std::string & path, int32_t version = -1);
|
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Doesn't throw in the following cases:
|
|
|
|
|
/// * The node doesn't exist
|
|
|
|
|
/// * Versions don't match
|
|
|
|
|
/// * The node has children.
|
2014-06-04 13:48:36 +00:00
|
|
|
|
int32_t tryRemove(const std::string & path, int32_t version = -1);
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Retries in case of network errors, returns ZNONODE if the node is already removed.
|
2014-07-03 17:24:17 +00:00
|
|
|
|
int32_t tryRemoveWithRetries(const std::string & path, int32_t version = -1, size_t * attempt = nullptr);
|
2014-03-07 13:50:58 +00:00
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// The same, but sets is_dirty flag if all removal attempts were unsuccessful.
|
|
|
|
|
/// This is needed because the session might still exist after all retries,
|
|
|
|
|
/// even if more time than session_timeout has passed.
|
|
|
|
|
/// So we do not rely on the ephemeral node being deleted and set is_dirty to
|
|
|
|
|
/// try and close the session ASAP.
|
|
|
|
|
/** Ridiculously Long Delay to Expire
|
2016-04-09 02:03:44 +00:00
|
|
|
|
When disconnects do happen, the common case should be a very* quick
|
|
|
|
|
reconnect to another server, but an extended network outage may
|
|
|
|
|
introduce a long delay before a client can reconnect to the ZooKeep‐
|
|
|
|
|
er service. Some developers wonder why the ZooKeeper client li‐
|
|
|
|
|
brary doesn’t simply decide at some point (perhaps twice the session
|
|
|
|
|
timeout) that enough is enough and kill the session itself.
|
|
|
|
|
There are two answers to this. First, ZooKeeper leaves this kind of
|
|
|
|
|
policy decision up to the developer. Developers can easily implement
|
|
|
|
|
such a policy by closing the handle themselves. Second, when a Zoo‐
|
|
|
|
|
Keeper ensemble goes down, time freezes. Thus, when the ensemble is
|
|
|
|
|
brought back up, session timeouts are restarted. If processes using
|
|
|
|
|
ZooKeeper hang in there, they may find out that the long timeout was
|
|
|
|
|
due to an extended ensemble failure that has recovered and pick right
|
|
|
|
|
up where they left off without any additional startup delay.
|
|
|
|
|
|
|
|
|
|
ZooKeeper: Distributed Process Coordination p118
|
|
|
|
|
*/
|
|
|
|
|
int32_t tryRemoveEphemeralNodeWithRetries(const std::string & path, int32_t version = -1, size_t * attempt = nullptr);
|
|
|
|
|
|
2017-03-17 00:44:00 +00:00
|
|
|
|
bool exists(const std::string & path, Stat * stat = nullptr, const EventPtr & watch = nullptr);
|
|
|
|
|
bool existsWatch(const std::string & path, Stat * stat, const WatchCallback & watch_callback);
|
2014-03-07 13:50:58 +00:00
|
|
|
|
|
2017-03-17 00:44:00 +00:00
|
|
|
|
std::string get(const std::string & path, Stat * stat = nullptr, const EventPtr & watch = nullptr);
|
2014-03-07 13:50:58 +00:00
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Doesn't not throw in the following cases:
|
|
|
|
|
/// * The node doesn't exist. Returns false in this case.
|
2017-03-17 00:44:00 +00:00
|
|
|
|
bool tryGet(const std::string & path, std::string & res, Stat * stat = nullptr, const EventPtr & watch = nullptr, int * code = nullptr);
|
|
|
|
|
|
|
|
|
|
bool tryGetWatch(const std::string & path, std::string & res, Stat * stat, const WatchCallback & watch_callback, int * code = nullptr);
|
2014-03-07 13:50:58 +00:00
|
|
|
|
|
|
|
|
|
void set(const std::string & path, const std::string & data,
|
|
|
|
|
int32_t version = -1, Stat * stat = nullptr);
|
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Creates the node if it doesn't exist. Updates its contents otherwise.
|
2015-06-09 12:30:30 +00:00
|
|
|
|
void createOrUpdate(const std::string & path, const std::string & data, int32_t mode);
|
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Doesn't not throw in the following cases:
|
|
|
|
|
/// * The node doesn't exist.
|
|
|
|
|
/// * Versions do not match.
|
2014-06-04 13:48:36 +00:00
|
|
|
|
int32_t trySet(const std::string & path, const std::string & data,
|
2014-03-07 19:18:48 +00:00
|
|
|
|
int32_t version = -1, Stat * stat = nullptr);
|
|
|
|
|
|
2014-03-07 13:50:58 +00:00
|
|
|
|
Strings getChildren(const std::string & path,
|
|
|
|
|
Stat * stat = nullptr,
|
2017-03-17 00:44:00 +00:00
|
|
|
|
const EventPtr & watch = nullptr);
|
2014-03-07 13:50:58 +00:00
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Doesn't not throw in the following cases:
|
|
|
|
|
/// * The node doesn't exist.
|
2014-06-04 13:48:36 +00:00
|
|
|
|
int32_t tryGetChildren(const std::string & path, Strings & res,
|
2014-03-07 19:18:48 +00:00
|
|
|
|
Stat * stat = nullptr,
|
2017-03-17 00:44:00 +00:00
|
|
|
|
const EventPtr & watch = nullptr);
|
2014-03-07 19:18:48 +00:00
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Performs several operations in a transaction.
|
|
|
|
|
/// Throws on every error.
|
2014-03-07 19:18:48 +00:00
|
|
|
|
OpResultsPtr multi(const Ops & ops);
|
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Throws only if some operation has returned an "unexpected" error
|
|
|
|
|
/// - an error that would cause the corresponding try- method to throw.
|
2014-06-04 13:48:36 +00:00
|
|
|
|
int32_t tryMulti(const Ops & ops, OpResultsPtr * out_results = nullptr);
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Use only with read-only operations.
|
2014-07-03 11:22:12 +00:00
|
|
|
|
int32_t tryMultiWithRetries(const Ops & ops, OpResultsPtr * out_results = nullptr, size_t * attempt = nullptr);
|
2014-03-07 13:50:58 +00:00
|
|
|
|
|
2016-10-26 22:27:38 +00:00
|
|
|
|
Int64 getClientID();
|
2014-03-22 14:44:44 +00:00
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Remove the node with the subtree. If someone concurrently adds or removes a node
|
|
|
|
|
/// in the subtree, the result is undefined.
|
2014-03-22 14:44:44 +00:00
|
|
|
|
void removeRecursive(const std::string & path);
|
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Remove the node with the subtree. If someone concurrently removes a node in the subtree,
|
|
|
|
|
/// this will not cause errors.
|
|
|
|
|
/// For instance, you can call this method twice concurrently for the same node and the end
|
|
|
|
|
/// result would be the same as for the single call.
|
2014-07-07 09:51:42 +00:00
|
|
|
|
void tryRemoveRecursive(const std::string & path);
|
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Wait for the node to disappear or return immediately if it doesn't exist.
|
2015-09-11 02:13:59 +00:00
|
|
|
|
void waitForDisappear(const std::string & path);
|
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Async interface (a small subset of operations is implemented).
|
|
|
|
|
///
|
|
|
|
|
/// Usage:
|
|
|
|
|
///
|
|
|
|
|
/// // Non-blocking calls:
|
|
|
|
|
/// auto future1 = zk.asyncGet("/path1");
|
|
|
|
|
/// auto future2 = zk.asyncGet("/path2");
|
|
|
|
|
/// ...
|
|
|
|
|
///
|
|
|
|
|
/// // These calls can block until the operations are completed:
|
|
|
|
|
/// auto result1 = future1.get();
|
|
|
|
|
/// auto result2 = future2.get();
|
|
|
|
|
///
|
|
|
|
|
/// Future should not be destroyed before the result is gotten.
|
2014-10-16 20:05:26 +00:00
|
|
|
|
|
|
|
|
|
template <typename Result, typename... TaskParams>
|
|
|
|
|
class Future
|
|
|
|
|
{
|
|
|
|
|
friend class ZooKeeper;
|
|
|
|
|
private:
|
|
|
|
|
using Task = std::packaged_task<Result (TaskParams...)>;
|
|
|
|
|
using TaskPtr = std::unique_ptr<Task>;
|
2014-12-14 04:38:11 +00:00
|
|
|
|
using TaskPtrPtr = std::unique_ptr<TaskPtr>;
|
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Everything is complicated.
|
|
|
|
|
///
|
|
|
|
|
/// In libzookeeper async interface a function (e.g. zoo_aget)
|
|
|
|
|
/// accepts a pointer to a standalone callback function and void* pointer to the context
|
|
|
|
|
/// which is then passed to the callback.
|
|
|
|
|
/// The caller is responsible for ensuring that the context lives until the callback
|
|
|
|
|
/// is finished and we can't simply pass ownership of the context into function object.
|
|
|
|
|
/// Instead, we save the context in a Future object and return it to the caller.
|
|
|
|
|
/// The cantext will live until the Future lives.
|
|
|
|
|
/// Context data is wrapped in an unique_ptr so that its address (which is passed to
|
|
|
|
|
/// libzookeeper) remains unchanged after the Future is returned from the function.
|
|
|
|
|
///
|
|
|
|
|
/// The second problem is that after std::promise has been fulfilled, and the user
|
|
|
|
|
/// has gotten the result from std::future, the Future object can be destroyed
|
|
|
|
|
/// before the std::promise::set_value() call that fulfils the promise completes in another
|
|
|
|
|
/// thread.
|
|
|
|
|
/// See http://stackoverflow.com/questions/10843304/race-condition-in-pthread-once
|
|
|
|
|
/// To combat this we use the second unique_ptr. Inside the callback, the void* context
|
|
|
|
|
/// is cast to unique_ptr and moved into the local unique_ptr to prolong the lifetime of
|
|
|
|
|
/// the context data.
|
2014-12-14 04:38:11 +00:00
|
|
|
|
|
|
|
|
|
TaskPtrPtr task;
|
|
|
|
|
std::future<Result> future;
|
2014-10-16 20:05:26 +00:00
|
|
|
|
|
|
|
|
|
template <typename... Args>
|
2014-12-14 04:38:11 +00:00
|
|
|
|
Future(Args &&... args) : task(new TaskPtr(new Task(std::forward<Args>(args)...))), future((*task)->get_future()) {}
|
2014-10-16 20:05:26 +00:00
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
Result get()
|
|
|
|
|
{
|
2014-12-14 04:38:11 +00:00
|
|
|
|
return future.get();
|
2014-10-16 20:05:26 +00:00
|
|
|
|
}
|
2015-10-13 11:16:32 +00:00
|
|
|
|
|
|
|
|
|
Future(Future &&) = default;
|
|
|
|
|
Future & operator= (Future &&) = default;
|
|
|
|
|
|
|
|
|
|
~Future()
|
|
|
|
|
{
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// If nobody has waited for the result, we must wait for it before the object is
|
|
|
|
|
/// destroyed, because the object contents can still be used in the callback.
|
2015-10-13 11:16:32 +00:00
|
|
|
|
if (future.valid())
|
|
|
|
|
future.wait();
|
|
|
|
|
}
|
2014-10-16 20:05:26 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2014-10-16 01:46:56 +00:00
|
|
|
|
struct ValueAndStat
|
|
|
|
|
{
|
|
|
|
|
std::string value;
|
|
|
|
|
Stat stat;
|
|
|
|
|
};
|
|
|
|
|
|
2014-10-16 20:05:26 +00:00
|
|
|
|
using GetFuture = Future<ValueAndStat, int, const char *, int, const Stat *>;
|
|
|
|
|
GetFuture asyncGet(const std::string & path);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct ValueAndStatAndExists
|
|
|
|
|
{
|
|
|
|
|
std::string value;
|
|
|
|
|
Stat stat;
|
|
|
|
|
bool exists;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using TryGetFuture = Future<ValueAndStatAndExists, int, const char *, int, const Stat *>;
|
|
|
|
|
TryGetFuture asyncTryGet(const std::string & path);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct StatAndExists
|
|
|
|
|
{
|
|
|
|
|
Stat stat;
|
|
|
|
|
bool exists;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using ExistsFuture = Future<StatAndExists, int, const Stat *>;
|
|
|
|
|
ExistsFuture asyncExists(const std::string & path);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
using GetChildrenFuture = Future<Strings, int, const String_vector *>;
|
|
|
|
|
GetChildrenFuture asyncGetChildren(const std::string & path);
|
2014-10-16 01:46:56 +00:00
|
|
|
|
|
|
|
|
|
|
2015-09-23 20:51:01 +00:00
|
|
|
|
using RemoveFuture = Future<void, int>;
|
|
|
|
|
RemoveFuture asyncRemove(const std::string & path);
|
|
|
|
|
|
|
|
|
|
|
2014-06-04 13:48:36 +00:00
|
|
|
|
static std::string error2string(int32_t code);
|
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Max size of node contents in bytes.
|
|
|
|
|
/// In 3.4.5 max node size is 1Mb.
|
2014-06-04 13:48:36 +00:00
|
|
|
|
static const size_t MAX_NODE_SIZE = 1048576;
|
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Length of the suffix that ZooKeeper adds to sequential nodes.
|
|
|
|
|
/// In fact it is smaller, but round it up for convenience.
|
2014-06-04 13:48:36 +00:00
|
|
|
|
static const size_t SEQUENTIAL_SUFFIX_SIZE = 64;
|
2015-09-23 01:26:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
zhandle_t * getHandle() { return impl; }
|
|
|
|
|
|
2014-03-07 13:50:58 +00:00
|
|
|
|
private:
|
2017-03-17 00:44:00 +00:00
|
|
|
|
friend struct WatchContext;
|
2014-09-12 00:32:27 +00:00
|
|
|
|
friend class EphemeralNodeHolder;
|
2014-06-30 11:33:06 +00:00
|
|
|
|
|
2016-01-28 01:00:42 +00:00
|
|
|
|
void init(const std::string & hosts, int32_t session_timeout_ms);
|
2014-06-04 13:48:36 +00:00
|
|
|
|
void removeChildrenRecursive(const std::string & path);
|
2014-07-07 09:51:42 +00:00
|
|
|
|
void tryRemoveChildrenRecursive(const std::string & path);
|
2017-03-17 00:44:00 +00:00
|
|
|
|
|
|
|
|
|
static WatchCallback callbackForEvent(const EventPtr & event);
|
|
|
|
|
WatchContext * createContext(WatchCallback && callback);
|
|
|
|
|
static void destroyContext(WatchContext * context);
|
|
|
|
|
static void processCallback(zhandle_t * zh, int type, int state, const char * path, void * watcher_ctx);
|
2014-04-25 13:55:15 +00:00
|
|
|
|
|
2014-06-04 16:48:55 +00:00
|
|
|
|
template <class T>
|
2014-10-16 01:21:03 +00:00
|
|
|
|
int32_t retry(T && operation, size_t * attempt = nullptr)
|
2014-06-04 16:48:55 +00:00
|
|
|
|
{
|
|
|
|
|
int32_t code = operation();
|
2014-07-03 11:22:12 +00:00
|
|
|
|
if (attempt)
|
|
|
|
|
*attempt = 0;
|
2014-07-03 17:24:17 +00:00
|
|
|
|
for (size_t i = 0; (i < retry_num) && (code == ZOPERATIONTIMEOUT || code == ZCONNECTIONLOSS); ++i)
|
2014-06-04 16:48:55 +00:00
|
|
|
|
{
|
2014-07-03 11:22:12 +00:00
|
|
|
|
if (attempt)
|
|
|
|
|
*attempt = i;
|
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// If the connection has been lost, wait timeout/3 hoping for connection re-establishment.
|
2014-11-27 12:25:23 +00:00
|
|
|
|
static const int MAX_SLEEP_TIME = 10;
|
2014-07-01 13:40:07 +00:00
|
|
|
|
if (code == ZCONNECTIONLOSS)
|
2014-11-27 12:25:23 +00:00
|
|
|
|
usleep(std::min(session_timeout_ms * 1000 / 3, MAX_SLEEP_TIME * 1000 * 1000));
|
2014-07-01 13:40:07 +00:00
|
|
|
|
|
2014-07-08 10:13:29 +00:00
|
|
|
|
LOG_WARNING(log, "Error on attempt " << i << ": " << error2string(code) << ". Retry");
|
2014-06-04 16:48:55 +00:00
|
|
|
|
code = operation();
|
|
|
|
|
}
|
2014-07-03 11:22:12 +00:00
|
|
|
|
|
2014-06-04 16:48:55 +00:00
|
|
|
|
return code;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// The following methods don't throw exceptions but return error codes.
|
2016-04-09 02:03:44 +00:00
|
|
|
|
int32_t createImpl(const std::string & path, const std::string & data, int32_t mode, std::string & path_created);
|
2014-06-04 16:48:55 +00:00
|
|
|
|
int32_t removeImpl(const std::string & path, int32_t version = -1);
|
2017-03-17 00:44:00 +00:00
|
|
|
|
int32_t getImpl(const std::string & path, std::string & res, Stat * stat, WatchCallback watch_callback);
|
|
|
|
|
int32_t setImpl(const std::string & path, const std::string & data, int32_t version = -1, Stat * stat = nullptr);
|
|
|
|
|
int32_t getChildrenImpl(const std::string & path, Strings & res, Stat * stat, WatchCallback watch_callback);
|
2014-06-04 16:48:55 +00:00
|
|
|
|
int32_t multiImpl(const Ops & ops, OpResultsPtr * out_results = nullptr);
|
2017-03-17 00:44:00 +00:00
|
|
|
|
int32_t existsImpl(const std::string & path, Stat * stat_, WatchCallback watch_callback);
|
2014-06-04 16:48:55 +00:00
|
|
|
|
|
2014-05-13 10:10:26 +00:00
|
|
|
|
std::string hosts;
|
2014-11-26 18:58:51 +00:00
|
|
|
|
int32_t session_timeout_ms;
|
2014-03-21 18:58:24 +00:00
|
|
|
|
|
2016-01-24 05:00:24 +00:00
|
|
|
|
std::mutex mutex;
|
2014-11-30 07:02:36 +00:00
|
|
|
|
ACLPtr default_acl;
|
2014-06-04 13:48:36 +00:00
|
|
|
|
zhandle_t * impl;
|
2014-05-19 09:21:57 +00:00
|
|
|
|
|
2017-03-17 00:44:00 +00:00
|
|
|
|
std::unordered_set<WatchContext *> watch_context_store;
|
2014-06-04 16:48:55 +00:00
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Retries number in case of OperationTimeout or ConnectionLoss errors.
|
2016-04-09 02:03:44 +00:00
|
|
|
|
static constexpr size_t retry_num = 3;
|
2014-07-01 13:40:07 +00:00
|
|
|
|
Logger * log = nullptr;
|
2016-04-09 02:03:44 +00:00
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// If true, there were unsuccessfull attempts to remove ephemeral nodes.
|
|
|
|
|
/// It is better to close the session to remove ephemeral nodes with certainty
|
|
|
|
|
/// instead of continuing to use re-established session.
|
2016-04-09 02:03:44 +00:00
|
|
|
|
bool is_dirty = false;
|
2014-03-07 13:50:58 +00:00
|
|
|
|
};
|
|
|
|
|
|
2016-04-09 02:03:44 +00:00
|
|
|
|
using ZooKeeperPtr = ZooKeeper::Ptr;
|
2014-04-25 13:55:15 +00:00
|
|
|
|
|
2014-03-22 14:44:44 +00:00
|
|
|
|
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Creates an ephemeral node in the constructor, removes it in the destructor.
|
2014-03-22 14:44:44 +00:00
|
|
|
|
class EphemeralNodeHolder
|
|
|
|
|
{
|
|
|
|
|
public:
|
2016-04-09 02:03:44 +00:00
|
|
|
|
using Ptr = std::shared_ptr<EphemeralNodeHolder>;
|
2014-03-22 14:44:44 +00:00
|
|
|
|
|
|
|
|
|
EphemeralNodeHolder(const std::string & path_, ZooKeeper & zookeeper_, bool create, bool sequential, const std::string & data)
|
|
|
|
|
: path(path_), zookeeper(zookeeper_)
|
|
|
|
|
{
|
|
|
|
|
if (create)
|
2014-04-04 10:37:33 +00:00
|
|
|
|
path = zookeeper.create(path, data, sequential ? CreateMode::EphemeralSequential : CreateMode::Ephemeral);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string getPath() const
|
|
|
|
|
{
|
|
|
|
|
return path;
|
2014-03-22 14:44:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Ptr create(const std::string & path, ZooKeeper & zookeeper, const std::string & data = "")
|
|
|
|
|
{
|
2016-04-09 02:03:44 +00:00
|
|
|
|
return std::make_shared<EphemeralNodeHolder>(path, zookeeper, true, false, data);
|
2014-03-22 14:44:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Ptr createSequential(const std::string & path, ZooKeeper & zookeeper, const std::string & data = "")
|
|
|
|
|
{
|
2016-04-09 02:03:44 +00:00
|
|
|
|
return std::make_shared<EphemeralNodeHolder>(path, zookeeper, true, true, data);
|
2014-03-22 14:44:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Ptr existing(const std::string & path, ZooKeeper & zookeeper)
|
|
|
|
|
{
|
2016-04-09 02:03:44 +00:00
|
|
|
|
return std::make_shared<EphemeralNodeHolder>(path, zookeeper, false, false, "");
|
2014-03-22 14:44:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~EphemeralNodeHolder()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2017-03-16 22:39:52 +00:00
|
|
|
|
/// Important: if the ZooKeeper is temporarily unavailable, repeated attempts to
|
|
|
|
|
/// delete the node are made.
|
|
|
|
|
/// Otherwise it is possible that EphemeralNodeHolder is destroyed,
|
|
|
|
|
/// but the session has recovered and the node in ZooKeeper remains for the long time.
|
2016-04-09 02:03:44 +00:00
|
|
|
|
zookeeper.tryRemoveEphemeralNodeWithRetries(path);
|
2014-03-22 14:44:44 +00:00
|
|
|
|
}
|
2014-09-12 00:32:27 +00:00
|
|
|
|
catch (const KeeperException & e)
|
|
|
|
|
{
|
2016-10-24 13:47:15 +00:00
|
|
|
|
ProfileEvents::increment(ProfileEvents::CannotRemoveEphemeralNode);
|
2016-10-24 11:44:34 +00:00
|
|
|
|
DB::tryLogCurrentException(__PRETTY_FUNCTION__);
|
2014-09-12 00:32:27 +00:00
|
|
|
|
}
|
2014-03-22 14:44:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
std::string path;
|
|
|
|
|
ZooKeeper & zookeeper;
|
2016-10-24 13:47:15 +00:00
|
|
|
|
CurrentMetrics::Increment metric_increment{CurrentMetrics::EphemeralNode};
|
2014-03-22 14:44:44 +00:00
|
|
|
|
};
|
|
|
|
|
|
2016-04-09 02:03:44 +00:00
|
|
|
|
using EphemeralNodeHolderPtr = EphemeralNodeHolder::Ptr;
|
2014-03-22 14:44:44 +00:00
|
|
|
|
|
2014-03-07 13:50:58 +00:00
|
|
|
|
}
|