ClickHouse/dbms/include/DB/Storages/MergeTree/AbandonableLockInZooKeeper.h

107 lines
3.1 KiB
C
Raw Normal View History

2014-04-02 07:59:43 +00:00
#pragma once
#include <zkutil/ZooKeeper.h>
#include <DB/Core/Exception.h>
namespace DB
{
/** Примитив синхронизации. Работает следующим образом:
* При создании создает неэфемерную инкрементную ноду и помечает ее как заблокированную (LOCKED).
* unlock() разблокирует ее (UNLOCKED).
* При вызове деструктора или завершении сессии в ZooKeeper, переходит в состояние ABANDONED.
* (В том числе при падении программы)
*/
class AbandonableLockInZooKeeper
{
public:
enum State
{
UNLOCKED,
LOCKED,
ABANDONED,
};
2014-04-02 10:10:37 +00:00
AbandonableLockInZooKeeper(
2014-04-02 07:59:43 +00:00
const String & path_prefix_, const String & temp_path, zkutil::ZooKeeper & zookeeper_)
: zookeeper(zookeeper_), path_prefix(path_prefix_)
{
/// Создадим вспомогательную эфемерную ноду.
2014-04-03 13:30:21 +00:00
holder_path = zookeeper.create(temp_path + "/abandonable_lock-", "", zkutil::CreateMode::EphemeralSequential);
2014-04-02 07:59:43 +00:00
/// Запишем в основную ноду путь к вспомогательной.
path = zookeeper.create(path_prefix, holder_path, zkutil::CreateMode::PersistentSequential);
}
String getPath()
{
return path;
}
/// Распарсить число в конце пути.
UInt64 getNumber()
{
2014-04-02 10:10:37 +00:00
return static_cast<UInt64>(atol(path.substr(path_prefix.size()).c_str()));
2014-04-02 07:59:43 +00:00
}
void unlock()
{
zookeeper.remove(path);
zookeeper.remove(holder_path);
}
2014-04-02 10:10:37 +00:00
/// Добавляет в список действия, эквивалентные unlock().
void getUnlockOps(zkutil::Ops & ops)
{
ops.push_back(new zkutil::Op::Remove(path, -1));
ops.push_back(new zkutil::Op::Remove(holder_path, -1));
}
~AbandonableLockInZooKeeper()
2014-04-02 07:59:43 +00:00
{
try
{
2014-04-02 10:10:37 +00:00
zookeeper.tryRemove(holder_path);
zookeeper.trySet(path, ""); /// Это не обязательно.
2014-04-02 07:59:43 +00:00
}
catch (...)
{
2014-04-02 10:10:37 +00:00
tryLogCurrentException("~AbandonableLockInZooKeeper");
2014-04-02 07:59:43 +00:00
}
}
static State check(const String & path, zkutil::ZooKeeper & zookeeper)
{
String holder_path;
/// Если нет основной ноды, UNLOCKED.
if (!zookeeper.tryGet(path, holder_path))
return UNLOCKED;
/// Если в основной ноде нет пути к вспомогательной, ABANDONED.
if (holder_path.empty())
return ABANDONED;
/// Если вспомогательная нода жива, LOCKED.
if (zookeeper.exists(holder_path))
return LOCKED;
/// Если вспомогательной ноды нет, нужно еще раз проверить существование основной ноды,
/// потому что за это время могли успеть вызвать unlock().
/// Заодно уберем оттуда путь к вспомогательной ноде.
if (zookeeper.trySet(path, "") == ZOK)
2014-04-02 07:59:43 +00:00
return ABANDONED;
return UNLOCKED;
}
private:
zkutil::ZooKeeper & zookeeper;
String path_prefix;
String path;
String holder_path;
};
}