ClickHouse/src/Storages/MergeTree/ReplicatedMergeTreeCleanupThread.cpp

522 lines
20 KiB
C++
Raw Normal View History

#include <Storages/MergeTree/ReplicatedMergeTreeCleanupThread.h>
#include <Storages/StorageReplicatedMergeTree.h>
#include <Poco/Timestamp.h>
#include <Interpreters/Context.h>
2014-10-15 01:22:06 +00:00
#include <random>
2020-06-15 02:12:06 +00:00
#include <pcg_random.hpp>
2018-08-28 00:10:05 +00:00
#include <unordered_set>
2014-10-15 01:22:06 +00:00
namespace DB
{
namespace ErrorCodes
{
extern const int NOT_FOUND_NODE;
extern const int ALL_REPLICAS_LOST;
2018-08-23 13:55:59 +00:00
extern const int REPLICA_STATUS_CHANGED;
}
2014-10-15 01:22:06 +00:00
ReplicatedMergeTreeCleanupThread::ReplicatedMergeTreeCleanupThread(StorageReplicatedMergeTree & storage_)
: storage(storage_)
2019-12-03 16:25:32 +00:00
, log_name(storage.getStorageID().getFullTableName() + " (ReplicatedMergeTreeCleanupThread)")
2020-05-30 21:57:37 +00:00
, log(&Poco::Logger::get(log_name))
{
task = storage.getContext()->getSchedulePool().createTask(log_name, [this]{ run(); });
}
2014-10-15 01:22:06 +00:00
void ReplicatedMergeTreeCleanupThread::run()
{
2019-08-26 14:24:29 +00:00
auto storage_settings = storage.getSettings();
const auto sleep_ms = storage_settings->cleanup_delay_period * 1000
2019-08-13 10:29:31 +00:00
+ std::uniform_int_distribution<UInt64>(0, storage_settings->cleanup_delay_period_random_add * 1000)(rng);
2014-10-15 01:22:06 +00:00
try
{
iterate();
}
catch (const Coordination::Exception & e)
{
tryLogCurrentException(log, __PRETTY_FUNCTION__);
if (e.code == Coordination::Error::ZSESSIONEXPIRED)
return;
}
catch (...)
{
tryLogCurrentException(log, __PRETTY_FUNCTION__);
}
2014-10-15 01:22:06 +00:00
task->scheduleAfter(sleep_ms);
2014-10-15 01:22:06 +00:00
}
2014-10-15 01:22:06 +00:00
void ReplicatedMergeTreeCleanupThread::iterate()
{
storage.clearOldPartsAndRemoveFromZK();
{
2020-06-18 16:10:47 +00:00
auto lock = storage.lockForShare(RWLockImpl::NO_QUERY, storage.getSettings()->lock_acquire_timeout_for_background_operations);
/// Both use relative_data_path which changes during rename, so we
/// do it under share lock
storage.clearOldWriteAheadLogs();
storage.clearOldTemporaryDirectories(storage.merger_mutator, storage.getSettings()->temporary_directories_lifetime.totalSeconds());
}
2017-11-19 21:17:58 +00:00
/// This is loose condition: no problem if we actually had lost leadership at this moment
/// and two replicas will try to do cleanup simultaneously.
if (storage.is_leader)
{
clearOldLogs();
clearOldBlocks();
clearOldMutations();
storage.clearEmptyParts();
}
2014-10-15 01:22:06 +00:00
}
void ReplicatedMergeTreeCleanupThread::clearOldLogs()
{
auto zookeeper = storage.getZooKeeper();
2019-08-26 14:24:29 +00:00
auto storage_settings = storage.getSettings();
Coordination::Stat stat;
if (!zookeeper->exists(storage.zookeeper_path + "/log", &stat))
throw Exception(storage.zookeeper_path + "/log doesn't exist", ErrorCodes::NOT_FOUND_NODE);
int children_count = stat.numChildren;
2020-06-15 02:12:06 +00:00
/// We will wait for 1.05 to 1.15 times more records to accumulate than necessary.
/// Randomization is needed to spread the time when multiple replicas come here.
/// Numbers are arbitrary.
std::uniform_real_distribution<double> distr(1.05, 1.15);
double ratio = distr(rng);
size_t min_replicated_logs_to_keep = storage_settings->min_replicated_logs_to_keep * ratio;
if (static_cast<double>(children_count) < min_replicated_logs_to_keep)
return;
Strings replicas = zookeeper->getChildren(storage.zookeeper_path + "/replicas", &stat);
2018-08-28 00:10:05 +00:00
/// We will keep logs after and including this threshold.
2018-08-23 13:55:59 +00:00
UInt64 min_saved_log_pointer = std::numeric_limits<UInt64>::max();
2018-08-28 00:10:05 +00:00
2018-08-27 16:22:39 +00:00
UInt64 min_log_pointer_lost_candidate = std::numeric_limits<UInt64>::max();
Strings entries = zookeeper->getChildren(storage.zookeeper_path + "/log");
if (entries.empty())
return;
std::sort(entries.begin(), entries.end());
2018-08-28 00:17:27 +00:00
String min_saved_record_log_str = entries[
2019-08-13 10:29:31 +00:00
entries.size() > storage_settings->max_replicated_logs_to_keep
? entries.size() - storage_settings->max_replicated_logs_to_keep
2018-08-28 00:17:27 +00:00
: 0];
2018-08-28 00:10:05 +00:00
/// Replicas that were marked is_lost but are active.
std::unordered_set<String> recovering_replicas;
2018-08-28 00:10:05 +00:00
/// Lost replica -> a version of 'host' node.
2018-08-27 15:44:51 +00:00
std::unordered_map<String, UInt32> host_versions_lost_replicas;
2018-08-28 00:10:05 +00:00
2018-08-28 00:17:27 +00:00
/// Replica -> log pointer.
2018-08-27 15:44:51 +00:00
std::unordered_map<String, String> log_pointers_candidate_lost_replicas;
2018-08-23 15:58:29 +00:00
2018-08-27 15:44:51 +00:00
size_t num_replicas_were_marked_is_lost = 0;
for (const String & replica : replicas)
{
2018-08-27 13:51:22 +00:00
Coordination::Stat host_stat;
2018-08-20 13:31:24 +00:00
zookeeper->get(storage.zookeeper_path + "/replicas/" + replica + "/host", &host_stat);
String pointer = zookeeper->get(storage.zookeeper_path + "/replicas/" + replica + "/log_pointer");
2019-01-09 15:44:20 +00:00
UInt64 log_pointer = 0;
2018-08-27 15:44:51 +00:00
if (!pointer.empty())
log_pointer = parse<UInt64>(pointer);
2018-08-27 19:06:32 +00:00
/// Check status of replica (active or not).
2018-08-28 00:10:05 +00:00
/// If replica was not active, we could check when its log_pointer locates.
2018-08-28 00:22:32 +00:00
/// There can be three possibilities for "is_lost" node:
/// It doesn't exist: in old version of ClickHouse.
/// It exists and value is 0.
/// It exists and value is 1.
2018-08-27 15:44:51 +00:00
String is_lost_str;
2018-08-28 00:10:05 +00:00
bool has_is_lost_node = zookeeper->tryGet(storage.zookeeper_path + "/replicas/" + replica + "/is_lost", is_lost_str);
2018-08-23 13:55:59 +00:00
if (zookeeper->exists(storage.zookeeper_path + "/replicas/" + replica + "/is_active"))
2018-08-27 15:44:51 +00:00
{
2018-08-28 00:10:05 +00:00
if (has_is_lost_node && is_lost_str == "1")
2018-08-27 15:44:51 +00:00
{
2018-08-28 00:17:27 +00:00
/// Lost and active: recovering.
recovering_replicas.insert(replica);
2018-08-27 15:44:51 +00:00
++num_replicas_were_marked_is_lost;
}
else
2018-08-28 00:17:27 +00:00
{
/// Not lost and active: usual case.
min_saved_log_pointer = std::min(min_saved_log_pointer, log_pointer);
2018-08-28 00:17:27 +00:00
}
2018-08-27 15:44:51 +00:00
}
else
{
2018-08-28 00:10:05 +00:00
if (!has_is_lost_node)
{
/// Only to support old versions CH.
/// If replica did not have "/is_lost" we must save it's log_pointer.
/// Because old version CH can not work with recovering.
2018-08-23 13:55:59 +00:00
min_saved_log_pointer = std::min(min_saved_log_pointer, log_pointer);
}
2018-08-23 13:55:59 +00:00
else
2018-08-27 15:44:51 +00:00
{
if (is_lost_str == "0")
{
2018-08-28 00:17:27 +00:00
/// Not active and not lost: a candidate to be marked as lost.
2018-08-23 13:55:59 +00:00
String log_pointer_str = "log-" + padIndex(log_pointer);
if (log_pointer_str >= min_saved_record_log_str)
2018-08-28 00:17:27 +00:00
{
/// Its log pointer is fresh enough.
2018-08-23 13:55:59 +00:00
min_saved_log_pointer = std::min(min_saved_log_pointer, log_pointer);
2018-08-28 00:17:27 +00:00
}
2018-08-23 13:55:59 +00:00
else
{
2018-08-28 00:17:27 +00:00
/// Its log pointer is stale: will mark replica as lost.
2018-08-27 15:44:51 +00:00
host_versions_lost_replicas[replica] = host_stat.version;
log_pointers_candidate_lost_replicas[replica] = log_pointer_str;
2018-08-27 16:22:39 +00:00
min_log_pointer_lost_candidate = std::min(min_log_pointer_lost_candidate, log_pointer);
2018-08-23 13:55:59 +00:00
}
}
2018-08-23 13:55:59 +00:00
else
{
2018-08-27 15:44:51 +00:00
++num_replicas_were_marked_is_lost;
host_versions_lost_replicas[replica] = host_stat.version;
}
2018-08-27 15:44:51 +00:00
}
}
}
2018-08-27 15:44:51 +00:00
/// We must check log_pointer of recovering replicas at the end.
/// Because log pointer of recovering replicas can move backward.
for (const String & replica : recovering_replicas)
{
String pointer = zookeeper->get(storage.zookeeper_path + "/replicas/" + replica + "/log_pointer");
2019-01-09 15:44:20 +00:00
UInt64 log_pointer = 0;
2018-08-27 15:44:51 +00:00
if (!pointer.empty())
log_pointer = parse<UInt64>(pointer);
min_saved_log_pointer = std::min(min_saved_log_pointer, log_pointer);
}
2018-08-27 13:51:22 +00:00
if (!recovering_replicas.empty())
2018-08-27 16:22:39 +00:00
min_saved_log_pointer = std::min(min_saved_log_pointer, min_log_pointer_lost_candidate);
/// We will not touch the last `min_replicated_logs_to_keep` records.
2019-08-13 10:29:31 +00:00
entries.erase(entries.end() - std::min<UInt64>(entries.size(), storage_settings->min_replicated_logs_to_keep), entries.end());
2018-08-28 00:01:03 +00:00
/// We will not touch records that are no less than `min_saved_log_pointer`.
2018-08-23 13:55:59 +00:00
entries.erase(std::lower_bound(entries.begin(), entries.end(), "log-" + padIndex(min_saved_log_pointer)), entries.end());
if (entries.empty())
return;
2020-06-15 02:12:06 +00:00
markLostReplicas(
host_versions_lost_replicas,
log_pointers_candidate_lost_replicas,
replicas.size() - num_replicas_were_marked_is_lost,
zookeeper);
2018-08-27 19:06:32 +00:00
Coordination::Requests ops;
2020-06-15 02:12:06 +00:00
size_t i = 0;
for (; i < entries.size(); ++i)
{
ops.emplace_back(zkutil::makeRemoveRequest(storage.zookeeper_path + "/log/" + entries[i], -1));
if (ops.size() > 4 * zkutil::MULTI_BATCH_SIZE || i + 1 == entries.size())
{
2018-08-27 15:44:51 +00:00
/// We need to check this because the replica that was restored from one of the marked replicas does not copy a non-valid log_pointer.
2018-08-27 23:59:49 +00:00
for (const auto & host_version : host_versions_lost_replicas)
2018-08-27 15:44:51 +00:00
ops.emplace_back(zkutil::makeCheckRequest(storage.zookeeper_path + "/replicas/" + host_version.first + "/host", host_version.second));
2018-08-23 15:58:29 +00:00
/// Simultaneously with clearing the log, we check to see if replica was added since we received replicas list.
ops.emplace_back(zkutil::makeCheckRequest(storage.zookeeper_path + "/replicas", stat.version));
2020-06-15 02:12:06 +00:00
try
{
zookeeper->multi(ops);
}
catch (const zkutil::KeeperMultiException & e)
{
/// Another replica already deleted the same node concurrently.
if (e.code == Coordination::Error::ZNONODE)
break;
throw;
}
ops.clear();
}
}
2020-06-15 02:12:06 +00:00
if (i != 0)
LOG_DEBUG(log, "Removed {} old log entries: {} - {}", i, entries[0], entries[i - 1]);
2014-10-15 01:22:06 +00:00
}
2018-08-27 15:44:51 +00:00
void ReplicatedMergeTreeCleanupThread::markLostReplicas(const std::unordered_map<String, UInt32> & host_versions_lost_replicas,
const std::unordered_map<String, String> & log_pointers_candidate_lost_replicas,
2018-08-23 15:58:29 +00:00
size_t replicas_count, const zkutil::ZooKeeperPtr & zookeeper)
{
2018-08-27 15:54:07 +00:00
Strings candidate_lost_replicas;
2018-08-27 13:51:22 +00:00
std::vector<Coordination::Requests> requests;
2018-08-27 16:22:39 +00:00
for (const auto & pair : log_pointers_candidate_lost_replicas)
{
String replica = pair.first;
2020-10-06 20:05:28 +00:00
LOG_WARNING(log, "Will mark replica {} as lost, because it has stale log pointer: {}", replica, pair.second);
2018-08-27 13:51:22 +00:00
Coordination::Requests ops;
2018-08-23 15:58:29 +00:00
/// If host changed version we can not mark replicas, because replica started to be active.
2020-06-15 02:12:06 +00:00
ops.emplace_back(zkutil::makeCheckRequest(
storage.zookeeper_path + "/replicas/" + replica + "/host", host_versions_lost_replicas.at(replica)));
ops.emplace_back(zkutil::makeSetRequest(
storage.zookeeper_path + "/replicas/" + replica + "/is_lost", "1", -1));
2018-08-27 15:44:51 +00:00
candidate_lost_replicas.push_back(replica);
requests.push_back(ops);
}
2018-08-27 19:06:32 +00:00
2018-08-27 15:44:51 +00:00
if (candidate_lost_replicas.size() == replicas_count)
2018-08-28 00:21:02 +00:00
throw Exception("All replicas are stale: we won't mark any replica as lost", ErrorCodes::ALL_REPLICAS_LOST);
2018-08-27 19:06:32 +00:00
2018-08-28 00:17:27 +00:00
std::vector<zkutil::ZooKeeper::FutureMulti> futures;
2018-08-27 15:54:07 +00:00
for (size_t i = 0; i < candidate_lost_replicas.size(); ++i)
futures.emplace_back(zookeeper->asyncTryMultiNoThrow(requests[i]));
2018-08-27 15:54:07 +00:00
for (size_t i = 0; i < candidate_lost_replicas.size(); ++i)
2018-08-20 13:31:24 +00:00
{
2018-08-28 00:23:38 +00:00
auto multi_responses = futures[i].get();
2018-08-27 13:51:22 +00:00
if (multi_responses.responses[0]->error == Coordination::Error::ZBADVERSION)
2018-08-28 00:23:52 +00:00
throw Exception(candidate_lost_replicas[i] + " became active when we marked lost replicas.", DB::ErrorCodes::REPLICA_STATUS_CHANGED);
2018-08-27 15:44:51 +00:00
zkutil::KeeperMultiException::check(multi_responses.error, requests[i], multi_responses.responses);
2018-08-20 13:31:24 +00:00
}
}
struct ReplicatedMergeTreeCleanupThread::NodeWithStat
{
String node;
Int64 ctime = 0;
2021-04-19 09:52:37 +00:00
Int32 version = 0;
2021-04-19 09:52:37 +00:00
NodeWithStat(String node_, Int64 ctime_, Int32 version_) : node(std::move(node_)), ctime(ctime_), version(version_) {}
static bool greaterByTime(const NodeWithStat & lhs, const NodeWithStat & rhs)
{
return std::forward_as_tuple(lhs.ctime, lhs.node) > std::forward_as_tuple(rhs.ctime, rhs.node);
}
};
2014-10-15 01:22:06 +00:00
void ReplicatedMergeTreeCleanupThread::clearOldBlocks()
{
auto zookeeper = storage.getZooKeeper();
2019-08-26 14:24:29 +00:00
auto storage_settings = storage.getSettings();
2014-12-12 20:50:32 +00:00
std::vector<NodeWithStat> timed_blocks;
getBlocksSortedByTime(*zookeeper, timed_blocks);
if (timed_blocks.empty())
return;
/// Use ZooKeeper's first node (last according to time) timestamp as "current" time.
Int64 current_time = timed_blocks.front().ctime;
2020-06-15 02:12:06 +00:00
Int64 time_threshold = std::max(
static_cast<Int64>(0),
current_time - static_cast<Int64>(1000 * storage_settings->replicated_deduplication_window_seconds));
/// Virtual node, all nodes that are "greater" than this one will be deleted
2021-04-19 09:52:37 +00:00
NodeWithStat block_threshold{{}, time_threshold, 0};
2019-08-13 10:29:31 +00:00
size_t current_deduplication_window = std::min<size_t>(timed_blocks.size(), storage_settings->replicated_deduplication_window);
auto first_outdated_block_fixed_threshold = timed_blocks.begin() + current_deduplication_window;
2020-06-15 02:12:06 +00:00
auto first_outdated_block_time_threshold = std::upper_bound(
timed_blocks.begin(), timed_blocks.end(), block_threshold, NodeWithStat::greaterByTime);
auto first_outdated_block = std::min(first_outdated_block_fixed_threshold, first_outdated_block_time_threshold);
auto num_nodes_to_delete = timed_blocks.end() - first_outdated_block;
if (!num_nodes_to_delete)
return;
auto last_outdated_block = timed_blocks.end() - 1;
LOG_TRACE(log, "Will clear {} old blocks from {} (ctime {}) to {} (ctime {})", num_nodes_to_delete,
first_outdated_block->node, first_outdated_block->ctime,
last_outdated_block->node, last_outdated_block->ctime);
zkutil::AsyncResponses<Coordination::RemoveResponse> try_remove_futures;
for (auto it = first_outdated_block; it != timed_blocks.end(); ++it)
{
String path = storage.zookeeper_path + "/blocks/" + it->node;
2021-04-19 09:52:37 +00:00
try_remove_futures.emplace_back(path, zookeeper->asyncTryRemove(path, it->version));
}
for (auto & pair : try_remove_futures)
{
const String & path = pair.first;
Coordination::Error rc = pair.second.get().error;
if (rc == Coordination::Error::ZNOTEMPTY)
{
2017-12-21 17:43:32 +00:00
/// Can happen if there are leftover block nodes with children created by previous server versions.
zookeeper->removeRecursive(path);
2019-06-06 15:28:02 +00:00
cached_block_stats.erase(first_outdated_block->node);
}
else if (rc == Coordination::Error::ZOK || rc == Coordination::Error::ZNONODE || rc == Coordination::Error::ZBADVERSION)
2019-06-06 15:28:02 +00:00
{
2020-06-15 02:12:06 +00:00
/// No node is Ok. Another replica is removing nodes concurrently.
2019-06-06 15:28:02 +00:00
/// Successfully removed blocks have to be removed from cache
cached_block_stats.erase(first_outdated_block->node);
}
2020-06-15 02:12:06 +00:00
else
{
LOG_WARNING(log, "Error while deleting ZooKeeper path `{}`: {}, ignoring.", path, Coordination::errorMessage(rc));
}
2019-06-06 15:28:02 +00:00
first_outdated_block++;
}
LOG_TRACE(log, "Cleared {} old blocks from ZooKeeper", num_nodes_to_delete);
}
void ReplicatedMergeTreeCleanupThread::getBlocksSortedByTime(zkutil::ZooKeeper & zookeeper, std::vector<NodeWithStat> & timed_blocks)
{
timed_blocks.clear();
Strings blocks;
Coordination::Stat stat;
if (Coordination::Error::ZOK != zookeeper.tryGetChildren(storage.zookeeper_path + "/blocks", blocks, &stat))
throw Exception(storage.zookeeper_path + "/blocks doesn't exist", ErrorCodes::NOT_FOUND_NODE);
2014-10-15 01:22:06 +00:00
2019-06-06 15:28:02 +00:00
/// Seems like this code is obsolete, because we delete blocks from cache
/// when they are deleted from zookeeper. But we don't know about all (maybe future) places in code
/// where they can be removed, so just to be sure that cache would not leak we check it here.
{
NameSet blocks_set(blocks.begin(), blocks.end());
for (auto it = cached_block_stats.begin(); it != cached_block_stats.end();)
{
if (!blocks_set.count(it->first))
it = cached_block_stats.erase(it);
else
++it;
}
}
2014-10-15 01:22:06 +00:00
auto not_cached_blocks = stat.numChildren - cached_block_stats.size();
2017-09-26 15:17:31 +00:00
if (not_cached_blocks)
{
2020-05-23 22:24:01 +00:00
LOG_TRACE(log, "Checking {} blocks ({} are not cached){}", stat.numChildren, not_cached_blocks, " to clear old ones from ZooKeeper.");
2017-09-26 15:17:31 +00:00
}
2014-10-15 01:22:06 +00:00
zkutil::AsyncResponses<Coordination::ExistsResponse> exists_futures;
for (const String & block : blocks)
{
auto it = cached_block_stats.find(block);
if (it == cached_block_stats.end())
{
/// New block. Fetch its stat asynchronously.
exists_futures.emplace_back(block, zookeeper.asyncExists(storage.zookeeper_path + "/blocks/" + block));
}
else
{
/// Cached block
2021-04-19 09:52:37 +00:00
const auto & ctime_and_version = it->second;
timed_blocks.emplace_back(block, ctime_and_version.first, ctime_and_version.second);
}
}
2014-10-15 01:22:06 +00:00
/// Put fetched stats into the cache
for (auto & elem : exists_futures)
{
auto status = elem.second.get();
if (status.error != Coordination::Error::ZNONODE)
{
2021-04-19 09:52:37 +00:00
cached_block_stats.emplace(elem.first, std::make_pair(status.stat.ctime, status.stat.version));
timed_blocks.emplace_back(elem.first, status.stat.ctime, status.stat.version);
}
}
std::sort(timed_blocks.begin(), timed_blocks.end(), NodeWithStat::greaterByTime);
}
void ReplicatedMergeTreeCleanupThread::clearOldMutations()
{
2019-08-26 14:24:29 +00:00
auto storage_settings = storage.getSettings();
2019-08-13 10:29:31 +00:00
if (!storage_settings->finished_mutations_to_keep)
return;
2019-08-13 10:29:31 +00:00
if (storage.queue.countFinishedMutations() <= storage_settings->finished_mutations_to_keep)
{
/// Not strictly necessary, but helps to avoid unnecessary ZooKeeper requests.
/// If even this replica hasn't finished enough mutations yet, then we don't need to clean anything.
return;
}
auto zookeeper = storage.getZooKeeper();
Coordination::Stat replicas_stat;
Strings replicas = zookeeper->getChildren(storage.zookeeper_path + "/replicas", &replicas_stat);
UInt64 min_pointer = std::numeric_limits<UInt64>::max();
for (const String & replica : replicas)
{
String pointer;
zookeeper->tryGet(storage.zookeeper_path + "/replicas/" + replica + "/mutation_pointer", pointer);
if (pointer.empty())
return; /// One replica hasn't done anything yet so we can't delete any mutations.
min_pointer = std::min(parse<UInt64>(pointer), min_pointer);
}
Strings entries = zookeeper->getChildren(storage.zookeeper_path + "/mutations");
std::sort(entries.begin(), entries.end());
/// Do not remove entries that are greater than `min_pointer` (they are not done yet).
entries.erase(std::upper_bound(entries.begin(), entries.end(), padIndex(min_pointer)), entries.end());
2019-08-13 10:29:31 +00:00
/// Do not remove last `storage_settings->finished_mutations_to_keep` entries.
if (entries.size() <= storage_settings->finished_mutations_to_keep)
return;
2019-08-13 10:29:31 +00:00
entries.erase(entries.end() - storage_settings->finished_mutations_to_keep, entries.end());
if (entries.empty())
return;
Coordination::Requests ops;
size_t batch_start_i = 0;
for (size_t i = 0; i < entries.size(); ++i)
{
ops.emplace_back(zkutil::makeRemoveRequest(storage.zookeeper_path + "/mutations/" + entries[i], -1));
if (ops.size() > 4 * zkutil::MULTI_BATCH_SIZE || i + 1 == entries.size())
{
/// Simultaneously with clearing the log, we check to see if replica was added since we received replicas list.
ops.emplace_back(zkutil::makeCheckRequest(storage.zookeeper_path + "/replicas", replicas_stat.version));
2020-06-15 02:12:06 +00:00
try
{
zookeeper->multi(ops);
}
catch (const zkutil::KeeperMultiException & e)
{
/// Another replica already deleted the same node concurrently.
if (e.code == Coordination::Error::ZNONODE)
break;
throw;
}
LOG_DEBUG(log, "Removed {} old mutation entries: {} - {}",
i + 1 - batch_start_i, entries[batch_start_i], entries[i]);
batch_start_i = i + 1;
ops.clear();
}
}
}
2014-10-15 01:22:06 +00:00
}