Пример #1
0
RWLock::RWLock(GetZooKeeper get_zookeeper_, const std::string & path_)
    : get_zookeeper{get_zookeeper_}, path{path_}
{
    if (!get_zookeeper)
        throw DB::Exception{"No ZooKeeper accessor specified", DB::ErrorCodes::LOGICAL_ERROR};

    int32_t code = get_zookeeper()->tryCreate(path, "", CreateMode::Persistent);
    if ((code != ZOK) && (code != ZNODEEXISTS))
    {
        if (code == ZNONODE)
            throw DB::Exception{"No such lock", DB::ErrorCodes::RWLOCK_NO_SUCH_LOCK};
        else
            throw KeeperException{code};
    }
}
Пример #2
0
void RWLock::release()
{
    __sync_synchronize();

    if (!*this)
    {
        owns_lock = false;
        return;
    }

    if (key.empty())
        throw DB::Exception{"RWLock: no lock is held", DB::ErrorCodes::LOGICAL_ERROR};

    get_zookeeper()->tryRemoveEphemeralNodeWithRetries(path + "/" + key);
    key.clear();
    owns_lock = false;
}
Пример #3
0
std::optional<std::string> ZooKeeperNodeCache::get(const std::string & path)
{
    zkutil::ZooKeeperPtr zookeeper;
    std::unordered_set<std::string> invalidated_paths;
    {
        std::lock_guard<std::mutex> lock(context->mutex);

        if (!context->zookeeper)
        {
            /// Possibly, there was a previous session and it has expired. Clear the cache.
            nonexistent_nodes.clear();
            node_cache.clear();

            context->zookeeper = get_zookeeper();
        }
        zookeeper = context->zookeeper;

        invalidated_paths.swap(context->invalidated_paths);
    }

    if (!zookeeper)
        throw DB::Exception("Could not get znode: `" + path + "'. ZooKeeper not configured.", DB::ErrorCodes::NO_ZOOKEEPER);

    for (const auto & path : invalidated_paths)
    {
        nonexistent_nodes.erase(path);
        node_cache.erase(path);
    }

    if (nonexistent_nodes.count(path))
        return std::nullopt;

    auto watch_callback = [context=context](const ZooKeeperImpl::ZooKeeper::WatchResponse & response)
    {
        if (!(response.type != ZooKeeperImpl::ZooKeeper::SESSION || response.state == ZooKeeperImpl::ZooKeeper::EXPIRED_SESSION))
            return;

        bool changed = false;
        {
            std::lock_guard<std::mutex> lock(context->mutex);

            if (response.type != ZooKeeperImpl::ZooKeeper::SESSION)
                changed = context->invalidated_paths.emplace(response.path).second;
            else if (response.state == ZooKeeperImpl::ZooKeeper::EXPIRED_SESSION)
            {
                context->zookeeper = nullptr;
                context->invalidated_paths.clear();
                changed = true;
            }
        }
        if (changed)
            context->changed_event.set();
    };

    std::string contents;

    auto cache_it = node_cache.find(path);
    if (cache_it != node_cache.end())
    {
        return cache_it->second;
    }

    if (zookeeper->tryGetWatch(path, contents, /* stat = */nullptr, watch_callback))
    {
        node_cache.emplace(path, contents);
        return contents;
    }

    /// Node doesn't exist. Create a watch on node creation.
    nonexistent_nodes.insert(path);

    if (!zookeeper->existsWatch(path, /* stat = */nullptr, watch_callback))
        return std::nullopt;

    /// Node was created between the two previous calls, try again. Watch is already set.
    if (zookeeper->tryGet(path, contents))
    {
        nonexistent_nodes.erase(path);
        node_cache.emplace(path, contents);
        return contents;
    }

    return std::nullopt;
}
Пример #4
0
void RWLock::acquireImpl(Mode mode)
{
    static_assert((lock_type == RWLock::Read) || (lock_type == RWLock::Write), "Invalid RWLock type");

    __sync_synchronize();

    if (!*this)
    {
        owns_lock = true;
        return;
    }

    if (!key.empty())
        throw DB::Exception{"RWLock: lock already held", DB::ErrorCodes::RWLOCK_ALREADY_HELD};

    try
    {
        /// Enqueue a new request for a lock.
        int32_t code = get_zookeeper()->tryCreate(path + "/" + Prefix<lock_type>::name,
            "", CreateMode::EphemeralSequential, key);
        if (code == ZNONODE)
            throw DB::Exception{"No such lock", DB::ErrorCodes::RWLOCK_NO_SUCH_LOCK};
        else if (code != ZOK)
            throw KeeperException(code);

        key = key.substr(path.length() + 1);

        while (true)
        {
            auto zookeeper = get_zookeeper();

            std::vector<std::string> children;
            int32_t code = zookeeper->tryGetChildren(path, children);
            if (code == ZNONODE)
                throw DB::Exception{"No such lock", DB::ErrorCodes::RWLOCK_NO_SUCH_LOCK};
            else if (code != ZOK)
                throw KeeperException{code};

            std::sort(children.begin(), children.end(), nodeQueueCmp);
            auto it = std::lower_bound(children.cbegin(), children.cend(), key, nodeQueueCmp);

            /// This should never happen.
            if ((it == children.cend()) || !nodeQueueEquals(*it, key))
                throw DB::Exception{"RWLock: corrupted lock request queue. Own request not found.",
                    DB::ErrorCodes::LOGICAL_ERROR};

            const std::string * observed_key = nullptr;

            if (lock_type == RWLock::Read)
            {
                /// Look for the first write lock request that is older than us.
                auto it2 = std::find_if(
                    std::make_reverse_iterator(it),
                    children.crend(),
                    [](const std::string & child)
                    {
                        return startsWith(child, Prefix<RWLock::Write>::name);
                    });
                if (it2 != children.crend())
                    observed_key = &*it2;
            }
            else if (lock_type == RWLock::Write)
            {
                if (it != children.cbegin())
                {
                    /// Take the next lock request that is older than us.
                    auto it2 = std::prev(it);
                    observed_key = &*it2;
                }
            }

            if (observed_key == nullptr)
            {
                /// We hold the lock.
                owns_lock = true;
                break;
            }

            if (mode == NonBlocking)
            {
                int32_t code = zookeeper->tryRemoveEphemeralNodeWithRetries(path + "/" + key);
                if (code == ZNONODE)
                    throw DB::Exception{"No such lock", DB::ErrorCodes::RWLOCK_NO_SUCH_LOCK};
                else if (code != ZOK)
                    throw KeeperException{code};

                key.clear();
                break;
            }

            abortIfRequested();

            /// Wait for our turn to come.
            if (zookeeper->exists(path + "/" + *observed_key, nullptr, event))
            {
                do
                {
                    abortIfRequested();
                }
                while (!event->tryWait(wait_duration));
            }
        }
    }
    catch (...)
    {
        try
        {
            if (!key.empty())
                get_zookeeper()->tryRemoveEphemeralNodeWithRetries(path + "/" + key);
        }
        catch (...)
        {
            DB::tryLogCurrentException(__PRETTY_FUNCTION__);
        }

        throw;
    }
}
Пример #5
0
ZooKeeperNodeCache::ZNode ZooKeeperNodeCache::get(const std::string & path, Coordination::WatchCallback caller_watch_callback)
{
    std::unordered_set<std::string> invalidated_paths;
    {
        std::lock_guard lock(context->mutex);

        if (context->all_paths_invalidated)
        {
            /// Possibly, there was a previous session and it has expired. Clear the cache.
            path_to_cached_znode.clear();
            context->all_paths_invalidated = false;
        }

        invalidated_paths.swap(context->invalidated_paths);
    }

    zkutil::ZooKeeperPtr zookeeper = get_zookeeper();
    if (!zookeeper)
        throw DB::Exception("Could not get znode: `" + path + "'. ZooKeeper not configured.", DB::ErrorCodes::NO_ZOOKEEPER);

    for (const auto & invalidated_path : invalidated_paths)
        path_to_cached_znode.erase(invalidated_path);

    auto cache_it = path_to_cached_znode.find(path);
    if (cache_it != path_to_cached_znode.end())
        return cache_it->second;

    std::weak_ptr<Context> weak_context(context);
    auto watch_callback = [weak_context, caller_watch_callback](const Coordination::WatchResponse & response)
    {
        if (!(response.type != Coordination::SESSION || response.state == Coordination::EXPIRED_SESSION))
            return;

        auto owned_context = weak_context.lock();
        if (!owned_context)
            return;

        bool changed = false;
        {
            std::lock_guard lock(owned_context->mutex);

            if (response.type != Coordination::SESSION)
                changed = owned_context->invalidated_paths.emplace(response.path).second;
            else if (response.state == Coordination::EXPIRED_SESSION)
            {
                owned_context->invalidated_paths.clear();
                owned_context->all_paths_invalidated = true;
                changed = true;
            }
        }
        if (changed && caller_watch_callback)
            caller_watch_callback(response);
    };

    ZNode result;

    result.exists = zookeeper->tryGetWatch(path, result.contents, &result.stat, watch_callback);
    if (result.exists)
    {
        path_to_cached_znode.emplace(path, result);
        return result;
    }

    /// Node doesn't exist. We must set a watch on node creation (because it wasn't set by tryGetWatch).

    result.exists = zookeeper->existsWatch(path, &result.stat, watch_callback);
    if (!result.exists)
    {
        path_to_cached_znode.emplace(path, result);
        return result;
    }

    /// Node was created between the two previous calls, try again. Watch is already set.

    result.exists = zookeeper->tryGet(path, result.contents, &result.stat);
    path_to_cached_znode.emplace(path, result);
    return result;
}