mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 23:21:59 +00:00
translate comments
This commit is contained in:
parent
faadab3034
commit
fccbc82c24
@ -31,29 +31,37 @@ class ConfigProcessor
|
||||
public:
|
||||
using Substitutions = std::vector<std::pair<std::string, std::string> >;
|
||||
|
||||
/// log_to_console нужно использовать, если система логгирования еще не инициализирована.
|
||||
/// Set log_to_console to true if the logging subsystem is not initialized yet.
|
||||
ConfigProcessor(bool throw_on_bad_incl = false, bool log_to_console = false, const Substitutions & substitutions = Substitutions());
|
||||
|
||||
~ConfigProcessor();
|
||||
|
||||
/** Выполняет подстановки в конфиге и возвращает XML-документ.
|
||||
*
|
||||
* Пусть в качестве path передана "/path/file.xml"
|
||||
* 1) Объединяем xml-дерево из /path/file.xml со всеми деревьями из файлов /path/{conf,file}.d/ *.{conf,xml}
|
||||
* Если у элемента есть атрибут replace, заменяем на него подходящий элемент.
|
||||
* Если у элемента есть атрибут remove, удаляем подходящий элемент.
|
||||
* Иначе объединяем детей рекурсивно.
|
||||
* 2) Берем из конфига путь к файлу, из которого будем делать подстановки: <include_from>/path2/metrika.xml</include_from>.
|
||||
* Если путь не указан, используем /etc/metrika.xml
|
||||
* 3) Заменяем элементы вида "<foo incl="bar"/>" на "<foo>содержимое элемента yandex.bar из metrika.xml</foo>"
|
||||
* 4) Заменяет "<layer/>" на "<layer>номер слоя из имени хоста</layer>"
|
||||
*/
|
||||
/// Perform config includes and substitutions and return the resulting XML-document.
|
||||
///
|
||||
/// Suppose path is "/path/file.xml"
|
||||
/// 1) Merge XML trees of /path/file.xml with XML trees of all files from /path/{conf,file}.d/*.{conf,xml}
|
||||
/// * If an element has a "replace" attribute, replace the matching element with it.
|
||||
/// * If an element has a "remove" attribute, remove the matching element.
|
||||
/// * Else, recursively merge child elements.
|
||||
/// 2) Determine the includes file from the config: <include_from>/path2/metrika.xml</include_from>
|
||||
/// If this path is not configured, use /etc/metrika.xml
|
||||
/// 3) Replace elements matching the "<foo incl="bar"/>" pattern with
|
||||
/// "<foo>contents of the yandex/bar element in metrika.xml</foo>"
|
||||
/// 4) If zk_node_cache is non-NULL, replace elements matching the "<foo from_zk="/bar">" pattern with
|
||||
/// "<foo>contents of the /bar ZooKeeper node</foo>".
|
||||
/// If has_zk_includes is non-NULL and there are such elements, set has_zk_includes to true.
|
||||
/// 5) (Yandex.Metrika-specific) Substitute "<layer/>" with "<layer>layer number from the hostname</layer>".
|
||||
XMLDocumentPtr processConfig(
|
||||
const std::string & path,
|
||||
bool * has_zk_includes = nullptr,
|
||||
zkutil::ZooKeeperNodeCache * zk_node_cache = nullptr);
|
||||
|
||||
|
||||
/// loadConfig* functions apply processConfig and create Poco::Util::XMLConfiguration.
|
||||
/// The resulting XML document is saved into a file with the name
|
||||
/// resulting from adding "-preprocessed" suffix to the path file name.
|
||||
/// E.g., config.xml -> config-preprocessed.xml
|
||||
|
||||
struct LoadedConfig
|
||||
{
|
||||
ConfigurationPtr configuration;
|
||||
@ -62,16 +70,13 @@ public:
|
||||
bool preprocessed_written;
|
||||
};
|
||||
|
||||
/** Делает processConfig и создает из результата Poco::Util::XMLConfiguration.
|
||||
* Еще сохраняет результат в файл по пути, полученному из path приписыванием строки "-preprocessed" к имени файла.
|
||||
*/
|
||||
|
||||
/// If allow_zk_includes is true, expects that the configuration xml can contain from_zk nodes.
|
||||
/// If the xml contains them, set has_zk_includes to true and don't write config-preprocessed.xml,
|
||||
/// If allow_zk_includes is true, expect that the configuration XML can contain from_zk nodes.
|
||||
/// If it is the case, set has_zk_includes to true and don't write config-preprocessed.xml,
|
||||
/// expecting that config would be reloaded with zookeeper later.
|
||||
|
||||
LoadedConfig loadConfig(const std::string & path, bool allow_zk_includes = false);
|
||||
|
||||
/// If fallback_to_preprocessed is true, then if KeeperException is thrown during config
|
||||
/// processing, load the configuration from the preprocessed file.
|
||||
LoadedConfig loadConfigWithZooKeeperIncludes(
|
||||
const std::string & path,
|
||||
zkutil::ZooKeeperNodeCache & zk_node_cache,
|
||||
|
@ -20,7 +20,7 @@ static bool endsWith(const std::string & s, const std::string & suffix)
|
||||
return s.size() >= suffix.size() && s.substr(s.size() - suffix.size()) == suffix;
|
||||
}
|
||||
|
||||
/// Извлекает из строки первое попавшееся число, состоящее из хотя бы двух цифр.
|
||||
/// Extracts from a string the first encountered number consisting of at least two digits.
|
||||
static std::string numberFromHost(const std::string & s)
|
||||
{
|
||||
for (size_t i = 0; i < s.size(); ++i)
|
||||
@ -66,13 +66,14 @@ ConfigProcessor::~ConfigProcessor()
|
||||
}
|
||||
|
||||
|
||||
/// Вектор из имени элемента и отсортированного списка имен и значений атрибутов (кроме атрибутов replace и remove).
|
||||
/// Взаимно однозначно задает имя элемента и список его атрибутов. Нужен, чтобы сравнивать элементы.
|
||||
/// Vector containing the name of the element and a sorted list of attribute names and values
|
||||
/// (except "remove" and "replace" attributes).
|
||||
/// Serves as a unique identifier of the element contents for comparison.
|
||||
using ElementIdentifier = std::vector<std::string>;
|
||||
|
||||
using NamedNodeMapPtr = Poco::AutoPtr<Poco::XML::NamedNodeMap>;
|
||||
/// NOTE Можно избавиться от использования Node.childNodes() и итерации по полученному списку, потому что
|
||||
/// доступ к i-му элементу этого списка работает за O(i).
|
||||
/// NOTE getting rid of iterating over the result of Node.childNodes() call is a good idea
|
||||
/// because accessing the i-th element of this list takes O(i) time.
|
||||
using NodeListPtr = Poco::AutoPtr<Poco::XML::NodeList>;
|
||||
|
||||
static ElementIdentifier getElementIdentifier(Node * element)
|
||||
@ -107,7 +108,8 @@ static Node * getRootNode(Document * document)
|
||||
for (size_t i = 0; i < children->length(); ++i)
|
||||
{
|
||||
Node * child = children->item(i);
|
||||
/// Кроме корневого элемента на верхнем уровне могут быть комментарии. Пропустим их.
|
||||
/// Besides the root element there can be comment nodes on the top level.
|
||||
/// Skip them.
|
||||
if (child->nodeType() == Node::ELEMENT_NODE)
|
||||
return child;
|
||||
}
|
||||
@ -135,7 +137,7 @@ void ConfigProcessor::mergeRecursive(XMLDocumentPtr config, Node * config_root,
|
||||
for (Node * node = config_root->firstChild(); node;)
|
||||
{
|
||||
Node * next_node = node->nextSibling();
|
||||
/// Уберем исходный текст из объединяемой части.
|
||||
/// Remove text from the original config node.
|
||||
if (node->nodeType() == Node::TEXT_NODE && !allWhitespace(node->getNodeValue()))
|
||||
{
|
||||
config_root->removeChild(node);
|
||||
@ -241,7 +243,8 @@ void ConfigProcessor::doIncludesRecursive(
|
||||
if (node->nodeType() != Node::ELEMENT_NODE)
|
||||
return;
|
||||
|
||||
/// Будем заменять <layer> на число из имени хоста, только если во входном файле есть тег <layer>, и он пустой, и у него нет атрибутов
|
||||
/// Substitute <layer> for the number extracted from the hostname only if there is an
|
||||
/// empty <layer> tag without attributes in the original file.
|
||||
if ( node->nodeName() == "layer" &&
|
||||
!node->hasAttributes() &&
|
||||
!node->hasChildNodes() &&
|
||||
@ -259,7 +262,7 @@ void ConfigProcessor::doIncludesRecursive(
|
||||
if (incl_attribute && from_zk_attribute)
|
||||
throw Poco::Exception("both incl and from_zk attributes set for element <" + node->nodeName() + ">");
|
||||
|
||||
/// Заменять имеющееся значение, а не добавлять к нему.
|
||||
/// Replace the original contents, not add to it.
|
||||
bool replace = attributes->getNamedItem("replace");
|
||||
|
||||
auto process_include = [&](const Node * include_attr, const std::function<Node * (const std::string &)> & get_node, const char * error_msg)
|
||||
|
@ -126,6 +126,9 @@ using EventPtr = std::shared_ptr<Poco::Event>;
|
||||
class ZooKeeper;
|
||||
|
||||
/// Callback to call when the watch fires.
|
||||
/// Because callbacks are called in the single "completion" thread internal to libzookeeper,
|
||||
/// they must execute as quickly as possible (preferably just set some notification).
|
||||
/// Parameters:
|
||||
/// zookeeper - zookeeper session to which the fired watch belongs
|
||||
/// type - event type, one of the *_EVENT constants from zookeeper.h
|
||||
/// state - session connection state, one of the *_STATE constants from zookeeper.h
|
||||
|
@ -34,21 +34,26 @@ const UInt32 BIG_SESSION_TIMEOUT = 600000;
|
||||
struct WatchContext;
|
||||
|
||||
|
||||
/** Сессия в ZooKeeper. Интерфейс существенно отличается от обычного API ZooKeeper.
|
||||
* Вместо callback-ов для watch-ей используются Poco::Event. Для указанного события вызывается set() только при первом вызове watch.
|
||||
* Методы на чтение при восстанавливаемых ошибках OperationTimeout, ConnectionLoss пытаются еще retry_num раз.
|
||||
* Методы на запись не пытаются повторить при восстанавливаемых ошибках, т.к. это приводит к проблеммам типа удаления дважды одного и того же.
|
||||
*
|
||||
* Методы с названиями, не начинающимися с try, бросают исключение при любой ошибке.
|
||||
*/
|
||||
class ZooKeeper
|
||||
/// 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
|
||||
{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<ZooKeeper>;
|
||||
|
||||
ZooKeeper(const std::string & hosts, int32_t session_timeout_ms = DEFAULT_SESSION_TIMEOUT);
|
||||
|
||||
/** конфиг вида
|
||||
/** Config of the form:
|
||||
<zookeeper>
|
||||
<node>
|
||||
<host>example1</host>
|
||||
@ -65,73 +70,68 @@ public:
|
||||
|
||||
~ZooKeeper();
|
||||
|
||||
/** Создает новую сессию с теми же параметрами. Можно использовать для переподключения, если сессия истекла.
|
||||
* Новой сессии соответствует только возвращенный экземпляр ZooKeeper, этот экземпляр не изменяется.
|
||||
*/
|
||||
/// 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.
|
||||
Ptr startNewSession() const;
|
||||
|
||||
/** Возвращает true, если сессия навсегда завершена.
|
||||
* Это возможно только если соединение было установлено, потом разорвалось, потом снова восстановилось, но слишком поздно.
|
||||
* С другой стороны, если, например, указан неправильный сервер или порт, попытки соединения будут продолжаться бесконечно,
|
||||
* expired() будет возвращать false, и все вызовы будут выбрасывать исключение ConnectionLoss.
|
||||
* Также возвращает true, если выставлен флаг is_dirty - просьба побыстрее завершить сессию.
|
||||
*/
|
||||
/// 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.
|
||||
bool expired();
|
||||
|
||||
ACLPtr getDefaultACL();
|
||||
|
||||
void setDefaultACL(ACLPtr new_acl);
|
||||
|
||||
/** Создать znode. Используется ACL, выставленный вызовом setDefaultACL (по умолчанию, всем полный доступ).
|
||||
* Если что-то пошло не так, бросить исключение.
|
||||
*/
|
||||
/// Create a znode. ACL set by setDefaultACL is used (full access to everybody by default).
|
||||
/// Throw an exception if something went wrong.
|
||||
std::string create(const std::string & path, const std::string & data, int32_t mode);
|
||||
|
||||
/** Не бросает исключение при следующих ошибках:
|
||||
* - Нет родителя создаваемой ноды.
|
||||
* - Родитель эфемерный.
|
||||
* - Такая нода уже есть.
|
||||
* При остальных ошибках бросает исключение.
|
||||
*/
|
||||
/// 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.
|
||||
int32_t tryCreate(const std::string & path, const std::string & data, int32_t mode, std::string & path_created);
|
||||
int32_t tryCreate(const std::string & path, const std::string & data, int32_t mode);
|
||||
int32_t tryCreateWithRetries(const std::string & path, const std::string & data, int32_t mode,
|
||||
std::string & path_created, size_t * attempt = nullptr);
|
||||
|
||||
/** создает Persistent ноду.
|
||||
* Игнорирует, если нода уже создана.
|
||||
* Пытается сделать retry при ConnectionLoss или OperationTimeout
|
||||
*/
|
||||
/// Create a Persistent node.
|
||||
/// Does nothing if the node already exists.
|
||||
/// Retries on ConnectionLoss or OperationTimeout.
|
||||
void createIfNotExists(const std::string & path, const std::string & data);
|
||||
|
||||
/** Создает всех еще не существующих предков ноды, с пустыми данными. Саму указанную ноду не создает.
|
||||
*/
|
||||
/// Creates all non-existent ancestors of the given path with empty contents.
|
||||
/// Does not create the node itself.
|
||||
void createAncestors(const std::string & path);
|
||||
|
||||
/** Удалить ноду, если ее версия равна version (если -1, подойдет любая версия).
|
||||
*/
|
||||
/// Remove the node if the version matches. (if version == -1, remove any version).
|
||||
void remove(const std::string & path, int32_t version = -1);
|
||||
|
||||
/** Удаляет ноду. В случае сетевых ошибок пробует удалять повторно.
|
||||
* Ошибка ZNONODE для второй и последующих попыток игнорируется
|
||||
*/
|
||||
/// Removes the node. In case of network errors tries to remove again.
|
||||
/// ZNONODE error for the second and the following tries is ignored.
|
||||
void removeWithRetries(const std::string & path, int32_t version = -1);
|
||||
|
||||
/** Не бросает исключение при следующих ошибках:
|
||||
* - Такой ноды нет.
|
||||
* - У ноды другая версия.
|
||||
* - У ноды есть дети.
|
||||
*/
|
||||
/// Doesn't throw in the following cases:
|
||||
/// * The node doesn't exist
|
||||
/// * Versions don't match
|
||||
/// * The node has children.
|
||||
int32_t tryRemove(const std::string & path, int32_t version = -1);
|
||||
/// Если есть проблемы с сетью может сам удалить ноду и вернуть ZNONODE
|
||||
/// Retries in case of network errors, returns ZNONODE if the node is already removed.
|
||||
int32_t tryRemoveWithRetries(const std::string & path, int32_t version = -1, size_t * attempt = nullptr);
|
||||
|
||||
/** То же самое, но также выставляет флаг is_dirty, если все попытки удалить были неуспешными.
|
||||
* Это делается, потому что сессия может ещё жить после всех попыток, даже если прошло больше session_timeout времени.
|
||||
* Поэтому не стоит рассчитывать, что эфемерная нода действительно будет удалена.
|
||||
* Но флаг is_dirty позволит побыстрее завершить сессию.
|
||||
|
||||
Ridiculously Long Delay to Expire
|
||||
/// 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
|
||||
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‐
|
||||
@ -156,9 +156,8 @@ public:
|
||||
|
||||
std::string get(const std::string & path, Stat * stat = nullptr, const EventPtr & watch = nullptr);
|
||||
|
||||
/** Не бросает исключение при следующих ошибках:
|
||||
* - Такой ноды нет. В таком случае возвращает false.
|
||||
*/
|
||||
/// Doesn't not throw in the following cases:
|
||||
/// * The node doesn't exist. Returns false in this case.
|
||||
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);
|
||||
@ -166,13 +165,12 @@ public:
|
||||
void set(const std::string & path, const std::string & data,
|
||||
int32_t version = -1, Stat * stat = nullptr);
|
||||
|
||||
/** Создает ноду, если ее не существует. Иначе обновляет */
|
||||
/// Creates the node if it doesn't exist. Updates its contents otherwise.
|
||||
void createOrUpdate(const std::string & path, const std::string & data, int32_t mode);
|
||||
|
||||
/** Не бросает исключение при следующих ошибках:
|
||||
* - Такой ноды нет.
|
||||
* - У ноды другая версия.
|
||||
*/
|
||||
/// Doesn't not throw in the following cases:
|
||||
/// * The node doesn't exist.
|
||||
/// * Versions do not match.
|
||||
int32_t trySet(const std::string & path, const std::string & data,
|
||||
int32_t version = -1, Stat * stat = nullptr);
|
||||
|
||||
@ -180,54 +178,51 @@ public:
|
||||
Stat * stat = nullptr,
|
||||
const EventPtr & watch = nullptr);
|
||||
|
||||
/** Не бросает исключение при следующих ошибках:
|
||||
* - Такой ноды нет.
|
||||
*/
|
||||
/// Doesn't not throw in the following cases:
|
||||
/// * The node doesn't exist.
|
||||
int32_t tryGetChildren(const std::string & path, Strings & res,
|
||||
Stat * stat = nullptr,
|
||||
const EventPtr & watch = nullptr);
|
||||
|
||||
/** Транзакционно выполняет несколько операций. При любой ошибке бросает исключение.
|
||||
*/
|
||||
/// Performs several operations in a transaction.
|
||||
/// Throws on every error.
|
||||
OpResultsPtr multi(const Ops & ops);
|
||||
|
||||
/** Бросает исключение только если какая-нибудь операция вернула "неожиданную" ошибку - такую ошибку,
|
||||
* увидев которую соответствующий метод try* бросил бы исключение. */
|
||||
/// Throws only if some operation has returned an "unexpected" error
|
||||
/// - an error that would cause the corresponding try- method to throw.
|
||||
int32_t tryMulti(const Ops & ops, OpResultsPtr * out_results = nullptr);
|
||||
/** Использовать только для методов на чтение */
|
||||
/// Use only with read-only operations.
|
||||
int32_t tryMultiWithRetries(const Ops & ops, OpResultsPtr * out_results = nullptr, size_t * attempt = nullptr);
|
||||
|
||||
Int64 getClientID();
|
||||
|
||||
/** Удаляет ноду вместе с поддеревом. Если в это время кто-то добавит иили удалит ноду в поддереве, результат не определен.
|
||||
*/
|
||||
/// Remove the node with the subtree. If someone concurrently adds or removes a node
|
||||
/// in the subtree, the result is undefined.
|
||||
void removeRecursive(const std::string & path);
|
||||
|
||||
/** Удаляет ноду вместе с поддеревом. Если в это время кто-то будет тоже удалять какие-нибудь ноды в поддереве, не будет ошибок.
|
||||
* Например, можно вызвать одновременно дважды для одной ноды, и результат будет тот же, как если вызвать один раз.
|
||||
*/
|
||||
/// 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.
|
||||
void tryRemoveRecursive(const std::string & path);
|
||||
|
||||
/** Подождать, пока нода перестанет существовать или вернуть сразу, если нода не существует.
|
||||
*/
|
||||
/// Wait for the node to disappear or return immediately if it doesn't exist.
|
||||
void waitForDisappear(const std::string & path);
|
||||
|
||||
/** Асинхронный интерфейс (реализовано небольшое подмножество операций).
|
||||
*
|
||||
* Использование:
|
||||
*
|
||||
* // Эти вызовы не блокируются.
|
||||
* auto future1 = zk.asyncGet("/path1");
|
||||
* auto future2 = zk.asyncGet("/path2");
|
||||
* ...
|
||||
*
|
||||
* // Эти вызовы могут заблокироваться до выполнения операции.
|
||||
* auto result1 = future1.get();
|
||||
* auto result2 = future2.get();
|
||||
*
|
||||
* future не должна быть уничтожена до получения результата.
|
||||
* Результат обязательно необходимо получать.
|
||||
*/
|
||||
/// 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.
|
||||
|
||||
template <typename Result, typename... TaskParams>
|
||||
class Future
|
||||
@ -238,22 +233,26 @@ public:
|
||||
using TaskPtr = std::unique_ptr<Task>;
|
||||
using TaskPtrPtr = std::unique_ptr<TaskPtr>;
|
||||
|
||||
/** Всё очень сложно.
|
||||
*
|
||||
* В асинхронном интерфейсе libzookeeper, функция (например, zoo_aget)
|
||||
* принимает указатель на свободную функцию-коллбэк и void* указатель на данные.
|
||||
* Указатель на данные потом передаётся в callback.
|
||||
* Это значит, что мы должны сами обеспечить, чтобы данные жили во время работы этой функции и до конца работы callback-а,
|
||||
* и не можем просто так передать владение данными внутрь функции.
|
||||
* Для этого, мы засовываем данные в объект Future, который возвращается пользователю. Данные будут жить, пока живёт объект Future.
|
||||
* Данные засунуты в unique_ptr, чтобы при возврате объекта Future из функции, их адрес (который передаётся в libzookeeper) не менялся.
|
||||
*
|
||||
* Вторая проблема состоит в том, что после того, как std::promise был удовлетворён, и пользователь получил результат из std::future,
|
||||
* объект Future может быть уничтожен, при чём раньше, чем завершит работу в другом потоке функция, которая удовлетворяет promise.
|
||||
* См. http://stackoverflow.com/questions/10843304/race-condition-in-pthread-once
|
||||
* Чтобы этого избежать, используется второй unique_ptr. Внутри callback-а, void* данные преобразуются в unique_ptr, и
|
||||
* перемещаются в локальную переменную unique_ptr, чтобы продлить время жизни данных.
|
||||
*/
|
||||
/// 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.
|
||||
|
||||
TaskPtrPtr task;
|
||||
std::future<Result> future;
|
||||
@ -272,9 +271,8 @@ public:
|
||||
|
||||
~Future()
|
||||
{
|
||||
/** Если никто не дождался результата, то мы должны его дождаться перед уничтожением объекта,
|
||||
* так как данные этого объекта могут всё ещё использоваться в колбэке.
|
||||
*/
|
||||
/// 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.
|
||||
if (future.valid())
|
||||
future.wait();
|
||||
}
|
||||
@ -322,12 +320,12 @@ public:
|
||||
|
||||
static std::string error2string(int32_t code);
|
||||
|
||||
/// максимальный размер данных в узле в байтах
|
||||
/// В версии 3.4.5. максимальный размер узла 1 Mb
|
||||
/// Max size of node contents in bytes.
|
||||
/// In 3.4.5 max node size is 1Mb.
|
||||
static const size_t MAX_NODE_SIZE = 1048576;
|
||||
|
||||
/// Размер прибавляемого ZooKeeper суффикса при создании Sequential ноды
|
||||
/// На самом деле размер меньше, но для удобства округлим в верхнюю сторону
|
||||
/// Length of the suffix that ZooKeeper adds to sequential nodes.
|
||||
/// In fact it is smaller, but round it up for convenience.
|
||||
static const size_t SEQUENTIAL_SUFFIX_SIZE = 64;
|
||||
|
||||
|
||||
@ -357,7 +355,7 @@ private:
|
||||
if (attempt)
|
||||
*attempt = i;
|
||||
|
||||
/// если потеряно соединение подождем timeout/3, авось восстановится
|
||||
/// If the connection has been lost, wait timeout/3 hoping for connection re-establishment.
|
||||
static const int MAX_SLEEP_TIME = 10;
|
||||
if (code == ZCONNECTIONLOSS)
|
||||
usleep(std::min(session_timeout_ms * 1000 / 3, MAX_SLEEP_TIME * 1000 * 1000));
|
||||
@ -369,7 +367,7 @@ private:
|
||||
return code;
|
||||
}
|
||||
|
||||
/// методы не бросают исключений, а возвращают коды ошибок
|
||||
/// The following methods don't throw exceptions but return error codes.
|
||||
int32_t createImpl(const std::string & path, const std::string & data, int32_t mode, std::string & path_created);
|
||||
int32_t removeImpl(const std::string & path, int32_t version = -1);
|
||||
int32_t getImpl(const std::string & path, std::string & res, Stat * stat, WatchCallback watch_callback);
|
||||
@ -387,22 +385,20 @@ private:
|
||||
|
||||
std::unordered_set<WatchContext *> watch_context_store;
|
||||
|
||||
/// Количество попыток повторить операцию чтения при OperationTimeout, ConnectionLoss
|
||||
/// Retries number in case of OperationTimeout or ConnectionLoss errors.
|
||||
static constexpr size_t retry_num = 3;
|
||||
Logger * log = nullptr;
|
||||
|
||||
/** При работе с сессией были неудачные попытки удалить эфемерные ноды,
|
||||
* после которых лучше завершить сессию (чтобы эфемерные ноды всё-таки удалились)
|
||||
* вместо того, чтобы продолжить пользоваться восстановившейся сессией.
|
||||
*/
|
||||
/// 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.
|
||||
bool is_dirty = false;
|
||||
};
|
||||
|
||||
using ZooKeeperPtr = ZooKeeper::Ptr;
|
||||
|
||||
|
||||
/** В конструкторе создает эфемерную ноду, в деструкторе - удаляет.
|
||||
*/
|
||||
/// Creates an ephemeral node in the constructor, removes it in the destructor.
|
||||
class EphemeralNodeHolder
|
||||
{
|
||||
public:
|
||||
@ -439,10 +435,10 @@ public:
|
||||
{
|
||||
try
|
||||
{
|
||||
/** Важно, что в случае недоступности ZooKeeper, делаются повторные попытки удалить ноду.
|
||||
* Иначе возможна ситуация, когда объект EphemeralNodeHolder уничтожен,
|
||||
* но сессия восстановится в течние session timeout, и эфемерная нода в ZooKeeper останется ещё надолго.
|
||||
*/
|
||||
/// 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.
|
||||
zookeeper.tryRemoveEphemeralNodeWithRetries(path);
|
||||
}
|
||||
catch (const KeeperException & e)
|
||||
|
@ -45,7 +45,7 @@ void check(int32_t code, const std::string path = "")
|
||||
|
||||
struct WatchContext
|
||||
{
|
||||
/// существует все время существования WatchContext
|
||||
/// ZooKeeper instance exists for the entire WatchContext lifetime.
|
||||
ZooKeeper & zk;
|
||||
WatchCallback callback;
|
||||
CurrentMetrics::Increment metric_increment{CurrentMetrics::ZooKeeperWatch};
|
||||
@ -64,7 +64,8 @@ void ZooKeeper::processCallback(zhandle_t * zh, int type, int state, const char
|
||||
WatchContext * context = static_cast<WatchContext *>(watcher_ctx);
|
||||
context->process(type, state, path);
|
||||
|
||||
/// Гарантируется, что не-ZOO_SESSION_EVENT событие придет ровно один раз (https://issues.apache.org/jira/browse/ZOOKEEPER-890).
|
||||
/// It is guaranteed that non-ZOO_SESSION_EVENT notification will be delivered only once
|
||||
/// (https://issues.apache.org/jira/browse/ZOOKEEPER-890)
|
||||
if (type != ZOO_SESSION_EVENT)
|
||||
destroyContext(context);
|
||||
}
|
||||
@ -114,7 +115,7 @@ struct ZooKeeperArgs
|
||||
else throw KeeperException(std::string("Unknown key ") + key + " in config file");
|
||||
}
|
||||
|
||||
/// перемешиваем порядок хостов, чтобы сделать нагрузку на zookeeper более равномерной
|
||||
/// Shuffle the hosts to distribute the load among ZooKeeper nodes.
|
||||
std::random_shuffle(hosts_strings.begin(), hosts_strings.end());
|
||||
|
||||
for (auto & host : hosts_strings)
|
||||
@ -234,7 +235,7 @@ int32_t ZooKeeper::tryGetChildren(const std::string & path, Strings & res,
|
||||
int32_t ZooKeeper::createImpl(const std::string & path, const std::string & data, int32_t mode, std::string & path_created)
|
||||
{
|
||||
int code;
|
||||
/// имя ноды может быть больше переданного пути, если создается sequential нода.
|
||||
/// The name of the created node can be longer than path if the sequential node is created.
|
||||
size_t name_buffer_size = path.size() + SEQUENTIAL_SUFFIX_SIZE;
|
||||
char * name_buffer = new char[name_buffer_size];
|
||||
|
||||
@ -363,8 +364,8 @@ int32_t ZooKeeper::tryRemoveEphemeralNodeWithRetries(const std::string & path, i
|
||||
}
|
||||
catch (const KeeperException &)
|
||||
{
|
||||
// Установим флажок, который говорит о том, что сессию лучше воспринимать так же как истёкшую,
|
||||
/// чтобы кто-нибудь её пересоздал, и, в случае эфемерной ноды, нода всё-таки была удалена.
|
||||
/// Set the flag indicating that the session is better treated as expired so that someone
|
||||
/// recreates it and the ephemeral nodes are indeed deleted.
|
||||
is_dirty = true;
|
||||
|
||||
throw;
|
||||
@ -437,7 +438,7 @@ int32_t ZooKeeper::getImpl(const std::string & path, std::string & res, Stat * s
|
||||
if (stat_)
|
||||
*stat_ = stat;
|
||||
|
||||
if (buffer_len < 0) /// Такое бывает, если в ноде в ZK лежит NULL. Не будем отличать его от пустой строки.
|
||||
if (buffer_len < 0) /// This can happen if the node contains NULL. Do not distinguish it from the empty string.
|
||||
res.clear();
|
||||
else
|
||||
res.assign(buffer, buffer_len);
|
||||
@ -537,17 +538,19 @@ int32_t ZooKeeper::multiImpl(const Ops & ops_, OpResultsPtr * out_results_)
|
||||
if (ops_.empty())
|
||||
return ZOK;
|
||||
|
||||
/// Workaround ошибки в сишном клиенте ZooKeeper. Если сессия истекла, zoo_multi иногда падает с segfault.
|
||||
/// Наверно, здесь есть race condition, и возможен segfault, если сессия истечет между этой проверкой и zoo_multi.
|
||||
/// TODO: Посмотреть, не исправлено ли это в последней версии клиента, и исправить.
|
||||
/// Workaround of the libzookeeper bug. If the session is expired, zoo_multi sometimes
|
||||
/// segfaults.
|
||||
/// Possibly, there is a race condition and a segfault is still possible if the session
|
||||
/// expires between this check and zoo_multi call.
|
||||
/// TODO: check if the bug is fixed in the latest version of libzookeeper.
|
||||
if (expired())
|
||||
return ZINVALIDSTATE;
|
||||
|
||||
size_t count = ops_.size();
|
||||
OpResultsPtr out_results(new OpResults(count));
|
||||
|
||||
/// копируем структуру, содержащую указатели, дефолтным конструктором копирования
|
||||
/// это безопасно, т.к. у нее нет деструктора
|
||||
/// Copy the struct containing pointers with default copy-constructor.
|
||||
/// It is safe because it hasn't got a destructor.
|
||||
std::vector<zoo_op_t> ops;
|
||||
for (const auto & op : ops_)
|
||||
ops.push_back(*(op->data));
|
||||
@ -631,9 +634,9 @@ void ZooKeeper::tryRemoveChildrenRecursive(const std::string & path)
|
||||
ops.emplace_back(std::make_unique<Op::Remove>(batch.back(), -1));
|
||||
}
|
||||
|
||||
/** Сначала пытаемся удалить детей более быстрым способом - сразу пачкой. Если не получилось,
|
||||
* значит кто-то кроме нас удаляет этих детей, и придется удалять их по одному.
|
||||
*/
|
||||
/// Try to remove the children with a faster method - in bulk. If this fails,
|
||||
/// this means someone is concurrently removing these children and we will have
|
||||
/// to remove them one by one.
|
||||
if (tryMulti(ops) != ZOK)
|
||||
{
|
||||
for (const std::string & child : batch)
|
||||
@ -664,7 +667,7 @@ void ZooKeeper::waitForDisappear(const std::string & path)
|
||||
zkutil::EventPtr event = std::make_shared<Poco::Event>();
|
||||
|
||||
std::string unused;
|
||||
/// get вместо exists, чтобы не утек watch, если ноды уже нет.
|
||||
/// Use get instead of exists to prevent watch leak if the node has already disappeared.
|
||||
if (!tryGet(path, unused, nullptr, event))
|
||||
break;
|
||||
|
||||
@ -684,7 +687,7 @@ ZooKeeper::~ZooKeeper()
|
||||
|
||||
LOG_INFO(&Logger::get("~ZooKeeper"), "Removing " << watch_context_store.size() << " watches");
|
||||
|
||||
/// удаляем WatchContext которые уже никогда не будут обработаны
|
||||
/// Destroy WatchContexts that will never be used.
|
||||
for (WatchContext * context : watch_context_store)
|
||||
delete context;
|
||||
|
||||
@ -739,7 +742,7 @@ ZooKeeper::GetFuture ZooKeeper::asyncGet(const std::string & path)
|
||||
throw KeeperException(rc, path);
|
||||
|
||||
std::string value_str;
|
||||
if (value_len > 0) /// Может быть не так, если в ZK лежит NULL. Мы не отличаем его от пустой строки.
|
||||
if (value_len > 0) /// May be otherwise of the node contains NULL. We don't distinguish it from the empty string.
|
||||
value_str = { value, size_t(value_len) };
|
||||
|
||||
return ValueAndStat{ value_str, stat ? *stat : Stat() };
|
||||
@ -772,7 +775,7 @@ ZooKeeper::TryGetFuture ZooKeeper::asyncTryGet(const std::string & path)
|
||||
throw KeeperException(rc, path);
|
||||
|
||||
std::string value_str;
|
||||
if (value_len > 0) /// Может быть не так, если в ZK лежит NULL. Мы не отличаем его от пустой строки.
|
||||
if (value_len > 0) /// May be otherwise of the node contains NULL. We don't distinguish it from the empty string.
|
||||
value_str = { value, size_t(value_len) };
|
||||
|
||||
return ValueAndStatAndExists{ value_str, stat ? *stat : Stat(), rc != ZNONODE };
|
||||
|
Loading…
Reference in New Issue
Block a user