Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
2dot5
ClickHouse
提交
fccbc82c
C
ClickHouse
项目概览
2dot5
/
ClickHouse
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
C
ClickHouse
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
fccbc82c
编写于
3月 17, 2017
作者:
A
Alexey Zatelepin
提交者:
alexey-milovidov
3月 19, 2017
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
translate comments
上级
faadab30
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
183 addition
and
173 deletion
+183
-173
dbms/include/DB/Common/ConfigProcessor.h
dbms/include/DB/Common/ConfigProcessor.h
+25
-20
dbms/src/Common/ConfigProcessor.cpp
dbms/src/Common/ConfigProcessor.cpp
+12
-9
libs/libzkutil/include/zkutil/Types.h
libs/libzkutil/include/zkutil/Types.h
+3
-0
libs/libzkutil/include/zkutil/ZooKeeper.h
libs/libzkutil/include/zkutil/ZooKeeper.h
+121
-125
libs/libzkutil/src/ZooKeeper.cpp
libs/libzkutil/src/ZooKeeper.cpp
+22
-19
未找到文件。
dbms/include/DB/Common/ConfigProcessor.h
浏览文件 @
fccbc82c
...
...
@@ -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
,
...
...
dbms/src/Common/ConfigProcessor.cpp
浏览文件 @
fccbc82c
...
...
@@ -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
)
...
...
libs/libzkutil/include/zkutil/Types.h
浏览文件 @
fccbc82c
...
...
@@ -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
...
...
libs/libzkutil/include/zkutil/ZooKeeper.h
浏览文件 @
fccbc82c
...
...
@@ -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
)
...
...
libs/libzkutil/src/ZooKeeper.cpp
浏览文件 @
fccbc82c
...
...
@@ -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
};
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录