Skip to content

Commit cd2b15a

Browse files
Merge pull request ClickHouse#86817 from ClickHouse/backport/25.8/86789
Backport ClickHouse#86789 to 25.8: Improve performance of RemoveRecursive request
2 parents 9d0dc6b + 1b24ee4 commit cd2b15a

File tree

1 file changed

+42
-19
lines changed

1 file changed

+42
-19
lines changed

src/Coordination/KeeperStorage.cpp

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1957,9 +1957,23 @@ class ToDeleteTreeCollector
19571957
uint32_t nodes_observed = 1; /// root node
19581958

19591959
std::list<KeeperStorageBase::Delta> deltas;
1960-
using UncommittedChildren
1960+
using ProcessedUncommittedChildren
19611961
= std::unordered_set<std::string_view, StringHashForHeterogeneousLookup, StringHashForHeterogeneousLookup::transparent_key_equal>;
19621962

1963+
struct PathCmp
1964+
{
1965+
auto operator()(const std::string_view a,
1966+
const std::string_view b) const
1967+
{
1968+
size_t level_a = std::count(a.begin(), a.end(), '/');
1969+
size_t level_b = std::count(b.begin(), b.end(), '/');
1970+
return level_a < level_b || (level_a == level_b && a < b);
1971+
}
1972+
1973+
using is_transparent = void; // required to make find() work with different type than key_type
1974+
};
1975+
1976+
std::map<std::string_view, typename Storage::UncommittedState::UncommittedNode *, PathCmp> uncommitted_children;
19631977
public:
19641978
enum class CollectStatus
19651979
{
@@ -1983,6 +1997,16 @@ class ToDeleteTreeCollector
19831997
if (checkLimits(root_node))
19841998
return CollectStatus::LimitExceeded;
19851999

2000+
/// Collect uncommitted children of root node in a specialized structure so we avoid iterating all uncommitted
2001+
/// nodes for each child node.
2002+
auto & nodes = storage.uncommitted_state.nodes;
2003+
auto root_path_view = root_path.toView();
2004+
for (auto & [node_path, uncommitted_node] : nodes)
2005+
{
2006+
if (Coordination::matchPath(node_path, root_path_view) == Coordination::PathMatchResult::IS_CHILD)
2007+
uncommitted_children[node_path] = &uncommitted_node;
2008+
}
2009+
19862010
addDelta(root_path, root_node.stats, storage.uncommitted_state.getACLs(root_path), std::string{root_node.getData()});
19872011

19882012
for (auto current_delta_it = deltas.rbegin(); current_delta_it != deltas.rend(); ++current_delta_it)
@@ -1993,18 +2017,18 @@ class ToDeleteTreeCollector
19932017
if (!storage.checkACL(current_path, Coordination::ACL::Delete, session_id, /*is_local=*/false))
19942018
return CollectStatus::NoAuth;
19952019

1996-
UncommittedChildren uncommitted_children;
1997-
if (auto status = visitUncommitted(current_path, uncommitted_children); status != CollectStatus::Ok)
2020+
ProcessedUncommittedChildren processed_uncommitted_children;
2021+
if (auto status = visitUncommitted(current_path, processed_uncommitted_children); status != CollectStatus::Ok)
19982022
return status;
19992023

20002024
if constexpr (Storage::use_rocksdb)
20012025
{
2002-
if (auto status = visitRocksDBNode(current_path, uncommitted_children); status != CollectStatus::Ok)
2026+
if (auto status = visitRocksDBNode(current_path, processed_uncommitted_children); status != CollectStatus::Ok)
20032027
return status;
20042028
}
20052029
else
20062030
{
2007-
if (auto status = visitMemNode(current_path, uncommitted_children); status != CollectStatus::Ok)
2031+
if (auto status = visitMemNode(current_path, processed_uncommitted_children); status != CollectStatus::Ok)
20082032
return status;
20092033
}
20102034
}
@@ -2018,7 +2042,7 @@ class ToDeleteTreeCollector
20182042
}
20192043

20202044
private:
2021-
CollectStatus visitRocksDBNode(StringRef current_path, const UncommittedChildren & uncommitted_children) requires Storage::use_rocksdb
2045+
CollectStatus visitRocksDBNode(StringRef current_path, const ProcessedUncommittedChildren & processed_uncommitted_children) requires Storage::use_rocksdb
20222046
{
20232047
std::filesystem::path current_path_fs(current_path.toString());
20242048

@@ -2032,7 +2056,7 @@ class ToDeleteTreeCollector
20322056
{
20332057
auto child_path = (current_path_fs / child_name).generic_string();
20342058

2035-
if (uncommitted_children.contains(child_path))
2059+
if (processed_uncommitted_children.contains(child_path))
20362060
continue;
20372061

20382062
if (checkLimits(child_node))
@@ -2044,7 +2068,7 @@ class ToDeleteTreeCollector
20442068
return CollectStatus::Ok;
20452069
}
20462070

2047-
CollectStatus visitMemNode(StringRef current_path, const UncommittedChildren & uncommitted_children) requires (!Storage::use_rocksdb)
2071+
CollectStatus visitMemNode(StringRef current_path, const ProcessedUncommittedChildren & processed_uncommitted_children) requires (!Storage::use_rocksdb)
20482072
{
20492073
std::lock_guard lock(storage.storage_mutex);
20502074
auto node_it = storage.container.find(current_path);
@@ -2058,7 +2082,7 @@ class ToDeleteTreeCollector
20582082
{
20592083
auto child_path = (current_path_fs / child_name.toView()).generic_string();
20602084

2061-
if (uncommitted_children.contains(child_path))
2085+
if (processed_uncommitted_children.contains(child_path))
20622086
continue;
20632087

20642088
auto child_it = storage.container.find(child_path);
@@ -2074,27 +2098,26 @@ class ToDeleteTreeCollector
20742098
return CollectStatus::Ok;
20752099
}
20762100

2077-
CollectStatus visitUncommitted(const std::string & path, UncommittedChildren & uncommitted_children)
2101+
CollectStatus visitUncommitted(const std::string & path, ProcessedUncommittedChildren & processed_uncommitted_children)
20782102
{
2079-
auto & nodes = storage.uncommitted_state.nodes;
2080-
2081-
for (auto & [node_path, uncommitted_node] : nodes)
2103+
for (auto nodes_it = uncommitted_children.upper_bound(path + "/");
2104+
nodes_it != uncommitted_children.end() && Coordination::parentNodePath(StringRef{nodes_it->first}) == path;
2105+
++nodes_it)
20822106
{
2083-
if (Coordination::parentNodePath(node_path) != path)
2084-
continue;
2107+
const auto & [node_path, uncommitted_node] = *nodes_it;
20852108

2086-
uncommitted_children.insert(node_path);
2109+
processed_uncommitted_children.insert(node_path);
20872110

2088-
const auto node_ptr = uncommitted_node.node;
2111+
const auto node_ptr = uncommitted_node->node;
20892112

20902113
if (node_ptr == nullptr) /// node was deleted in previous step of multi transaction
20912114
continue;
20922115

20932116
if (checkLimits(*node_ptr))
20942117
return CollectStatus::LimitExceeded;
20952118

2096-
uncommitted_node.materializeACL(storage.acl_map);
2097-
addDelta(node_path, node_ptr->stats, *uncommitted_node.acls, std::string{node_ptr->getData()});
2119+
uncommitted_node->materializeACL(storage.acl_map);
2120+
addDelta(std::string{node_path}, node_ptr->stats, *uncommitted_node->acls, std::string{node_ptr->getData()});
20982121
}
20992122

21002123
return CollectStatus::Ok;

0 commit comments

Comments
 (0)