2014-04-02 07:59:43 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <zkutil/ZooKeeper.h>
|
|
|
|
|
#include <DB/Core/Exception.h>
|
2014-09-12 00:32:27 +00:00
|
|
|
|
#include <DB/IO/ReadHelpers.h>
|
2014-04-02 07:59:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/** Примитив синхронизации. Работает следующим образом:
|
|
|
|
|
* При создании создает неэфемерную инкрементную ноду и помечает ее как заблокированную (LOCKED).
|
|
|
|
|
* unlock() разблокирует ее (UNLOCKED).
|
|
|
|
|
* При вызове деструктора или завершении сессии в ZooKeeper, переходит в состояние ABANDONED.
|
2014-09-12 00:32:27 +00:00
|
|
|
|
* (В том числе при падении программы).
|
2014-04-02 07:59:43 +00:00
|
|
|
|
*/
|
2014-08-07 09:23:55 +00:00
|
|
|
|
class AbandonableLockInZooKeeper : private boost::noncopyable
|
2014-04-02 07:59:43 +00:00
|
|
|
|
{
|
|
|
|
|
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);
|
2014-09-12 00:32:27 +00:00
|
|
|
|
|
|
|
|
|
if (path.size() <= path_prefix.size())
|
|
|
|
|
throw Exception("Logical error: name of sequential node is shorter than prefix.", ErrorCodes::LOGICAL_ERROR);
|
2014-04-02 07:59:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-07 09:23:55 +00:00
|
|
|
|
AbandonableLockInZooKeeper(AbandonableLockInZooKeeper && rhs)
|
|
|
|
|
: zookeeper(rhs.zookeeper)
|
|
|
|
|
{
|
|
|
|
|
std::swap(path_prefix, rhs.path_prefix);
|
|
|
|
|
std::swap(path, rhs.path);
|
|
|
|
|
std::swap(holder_path, rhs.holder_path);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-12 00:32:27 +00:00
|
|
|
|
String getPath() const
|
2014-04-02 07:59:43 +00:00
|
|
|
|
{
|
|
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Распарсить число в конце пути.
|
2014-09-12 00:32:27 +00:00
|
|
|
|
UInt64 getNumber() const
|
2014-04-02 07:59:43 +00:00
|
|
|
|
{
|
2014-09-12 00:32:27 +00:00
|
|
|
|
return parse<UInt64>(path.c_str() + path_prefix.size(), path.size() - path_prefix.size());
|
2014-04-02 07:59:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void unlock()
|
|
|
|
|
{
|
|
|
|
|
zookeeper.remove(path);
|
|
|
|
|
zookeeper.remove(holder_path);
|
2014-08-07 09:23:55 +00:00
|
|
|
|
holder_path = "";
|
2014-04-02 07:59:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
2014-08-07 09:23:55 +00:00
|
|
|
|
if (holder_path.empty())
|
|
|
|
|
return;
|
|
|
|
|
|
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().
|
|
|
|
|
/// Заодно уберем оттуда путь к вспомогательной ноде.
|
2014-06-04 13:49:22 +00:00
|
|
|
|
if (zookeeper.trySet(path, "") == ZOK)
|
2014-04-02 07:59:43 +00:00
|
|
|
|
return ABANDONED;
|
|
|
|
|
|
|
|
|
|
return UNLOCKED;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-25 16:32:02 +00:00
|
|
|
|
static void createAbandonedIfNotExists(const String & path, zkutil::ZooKeeper & zookeeper)
|
|
|
|
|
{
|
|
|
|
|
zookeeper.createIfNotExists(path, "");
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-02 07:59:43 +00:00
|
|
|
|
private:
|
|
|
|
|
zkutil::ZooKeeper & zookeeper;
|
|
|
|
|
String path_prefix;
|
|
|
|
|
String path;
|
|
|
|
|
String holder_path;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|