be/src/load/group_commit/wal/wal_manager.cpp
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | #include "load/group_commit/wal/wal_manager.h" |
19 | | |
20 | | #include <absl/strings/str_split.h> |
21 | | #include <bvar/bvar.h> |
22 | | #include <glog/logging.h> |
23 | | |
24 | | #include <chrono> |
25 | | #include <filesystem> |
26 | | #include <shared_mutex> |
27 | | #include <thread> |
28 | | #include <unordered_map> |
29 | | #include <vector> |
30 | | |
31 | | #include "common/config.h" |
32 | | #include "common/status.h" |
33 | | #include "io/fs/local_file_system.h" |
34 | | #include "load/group_commit/wal/wal_dirs_info.h" |
35 | | #include "load/group_commit/wal/wal_reader.h" |
36 | | #include "runtime/cluster_info.h" |
37 | | #include "runtime/exec_env.h" |
38 | | #include "runtime/fragment_mgr.h" |
39 | | #include "storage/options.h" |
40 | | #include "util/parse_util.h" |
41 | | |
42 | | namespace doris { |
43 | | |
44 | | bvar::Status<size_t> g_wal_total_count("wal_total_count", 0); |
45 | | bvar::Status<size_t> g_wal_max_count_per_table("wal_max_count_per_table", 0); |
46 | | |
47 | | WalManager::WalManager(ExecEnv* exec_env, const std::string& wal_dir_list) |
48 | 3 | : _exec_env(exec_env), |
49 | 3 | _stop(false), |
50 | 3 | _stop_background_threads_latch(1), |
51 | 3 | _first_replay(true) { |
52 | 3 | _wal_dirs = absl::StrSplit(wal_dir_list, ";", absl::SkipWhitespace()); |
53 | 3 | static_cast<void>(ThreadPoolBuilder("GroupCommitReplayWalThreadPool") |
54 | 3 | .set_min_threads(1) |
55 | 3 | .set_max_threads(config::group_commit_relay_wal_threads) |
56 | 3 | .build(&_thread_pool)); |
57 | 3 | _wal_dirs_info = WalDirsInfo::create_unique(); |
58 | 3 | } |
59 | | |
60 | 3 | WalManager::~WalManager() { |
61 | 3 | LOG(INFO) << "WalManager is destoried"; |
62 | 3 | } |
63 | | |
64 | 0 | bool WalManager::is_running() { |
65 | 0 | return !_stop.load(); |
66 | 0 | } |
67 | | |
68 | 2 | void WalManager::stop() { |
69 | 2 | if (!this->_stop.load()) { |
70 | 2 | this->_stop.store(true); |
71 | 2 | _stop_relay_wal(); |
72 | 2 | _stop_background_threads_latch.count_down(); |
73 | 2 | if (_replay_thread) { |
74 | 0 | _replay_thread->join(); |
75 | 0 | } |
76 | 2 | if (_update_wal_dirs_info_thread) { |
77 | 0 | _update_wal_dirs_info_thread->join(); |
78 | 0 | } |
79 | 2 | _thread_pool->shutdown(); |
80 | 2 | LOG(INFO) << "WalManager is stopped"; |
81 | 2 | } |
82 | 2 | } |
83 | | |
84 | 0 | Status WalManager::init() { |
85 | 0 | RETURN_IF_ERROR(_init_wal_dirs_conf()); |
86 | 0 | RETURN_IF_ERROR(_init_wal_dirs()); |
87 | 0 | RETURN_IF_ERROR(_init_wal_dirs_info()); |
88 | 0 | return Thread::create( |
89 | 0 | "WalMgr", "replay_wal", [this]() { static_cast<void>(this->_replay_background()); }, |
90 | 0 | &_replay_thread); |
91 | 0 | } |
92 | | |
93 | 0 | Status WalManager::_init_wal_dirs_conf() { |
94 | 0 | std::vector<std::string> tmp_dirs; |
95 | 0 | if (_wal_dirs.empty()) { |
96 | | // default case. |
97 | 0 | for (const StorePath& path : ExecEnv::GetInstance()->store_paths()) { |
98 | 0 | tmp_dirs.emplace_back(path.path + "/wal"); |
99 | 0 | } |
100 | 0 | } else { |
101 | | // user config must be absolute path. |
102 | 0 | for (const std::string& wal_dir : _wal_dirs) { |
103 | 0 | if (std::filesystem::path(wal_dir).is_absolute()) { |
104 | 0 | tmp_dirs.emplace_back(wal_dir); |
105 | 0 | } else { |
106 | 0 | return Status::InternalError( |
107 | 0 | "BE config group_commit_replay_wal_dir has to be absolute path!"); |
108 | 0 | } |
109 | 0 | } |
110 | 0 | } |
111 | 0 | _wal_dirs = tmp_dirs; |
112 | 0 | return Status::OK(); |
113 | 0 | } |
114 | | |
115 | 0 | Status WalManager::_init_wal_dirs() { |
116 | 0 | bool exists = false; |
117 | 0 | for (auto wal_dir : _wal_dirs) { |
118 | 0 | std::string tmp_dir = wal_dir + "/" + _tmp; |
119 | 0 | LOG(INFO) << "wal_dir:" << wal_dir << ", tmp_dir:" << tmp_dir; |
120 | 0 | RETURN_IF_ERROR(io::global_local_filesystem()->exists(wal_dir, &exists)); |
121 | 0 | if (!exists) { |
122 | 0 | RETURN_IF_ERROR(io::global_local_filesystem()->create_directory(wal_dir)); |
123 | 0 | } |
124 | 0 | RETURN_IF_ERROR(io::global_local_filesystem()->exists(tmp_dir, &exists)); |
125 | 0 | if (!exists) { |
126 | 0 | RETURN_IF_ERROR(io::global_local_filesystem()->create_directory(tmp_dir)); |
127 | 0 | } |
128 | 0 | } |
129 | 0 | return Status::OK(); |
130 | 0 | } |
131 | | |
132 | 16 | Status WalManager::_init_wal_dirs_info() { |
133 | 16 | for (const std::string& wal_dir : _wal_dirs) { |
134 | 16 | size_t available_bytes; |
135 | | #ifndef BE_TEST |
136 | | size_t disk_capacity_bytes; |
137 | | RETURN_IF_ERROR(io::global_local_filesystem()->get_space_info(wal_dir, &disk_capacity_bytes, |
138 | | &available_bytes)); |
139 | | #else |
140 | 16 | available_bytes = wal_limit_test_bytes; |
141 | 16 | #endif |
142 | 16 | bool is_percent = true; |
143 | 16 | int64_t wal_disk_limit = ParseUtil::parse_mem_spec(config::group_commit_wal_max_disk_limit, |
144 | 16 | -1, available_bytes, &is_percent); |
145 | 16 | if (wal_disk_limit < 0) { |
146 | 3 | return Status::InternalError( |
147 | 3 | "group_commit_wal_max_disk_limit config is wrong, please check your config!"); |
148 | 3 | } |
149 | | // if there are some wal files in wal dir, we need to add it to wal disk limit. |
150 | 13 | size_t wal_dir_size = 0; |
151 | | #ifndef BE_TEST |
152 | | RETURN_IF_ERROR(io::global_local_filesystem()->directory_size(wal_dir, &wal_dir_size)); |
153 | | #endif |
154 | 13 | if (is_percent) { |
155 | 4 | wal_disk_limit += wal_dir_size; |
156 | 4 | } |
157 | 13 | RETURN_IF_ERROR(_wal_dirs_info->add(wal_dir, wal_disk_limit, wal_dir_size, 0)); |
158 | | |
159 | 13 | #ifdef BE_TEST |
160 | 13 | wal_limit_test_bytes = wal_disk_limit; |
161 | 13 | #endif |
162 | 13 | } |
163 | | #ifndef BE_TEST |
164 | | return Thread::create( |
165 | | "WalMgr", "update_wal_dir_info", |
166 | | [this]() { static_cast<void>(this->_update_wal_dir_info_thread()); }, |
167 | | &_update_wal_dirs_info_thread); |
168 | | #else |
169 | 13 | return Status::OK(); |
170 | 16 | #endif |
171 | 16 | } |
172 | | |
173 | 0 | void WalManager::add_wal_queue(int64_t table_id, int64_t wal_id) { |
174 | 0 | std::lock_guard<std::shared_mutex> wrlock(_wal_queue_lock); |
175 | 0 | LOG(INFO) << "add wal to queue, table_id: " << table_id << ", wal_id: " << wal_id; |
176 | 0 | auto it = _wal_queues.find(table_id); |
177 | 0 | if (it == _wal_queues.end()) { |
178 | 0 | std::set<int64_t> tmp_set; |
179 | 0 | tmp_set.insert(wal_id); |
180 | 0 | _wal_queues.emplace(table_id, tmp_set); |
181 | 0 | } else { |
182 | 0 | it->second.insert(wal_id); |
183 | 0 | } |
184 | 0 | } |
185 | | |
186 | 1 | void WalManager::erase_wal_queue(int64_t table_id, int64_t wal_id) { |
187 | 1 | std::lock_guard<std::shared_mutex> wrlock(_wal_queue_lock); |
188 | 1 | auto it = _wal_queues.find(table_id); |
189 | 1 | if (it != _wal_queues.end()) { |
190 | 0 | LOG(INFO) << "remove wal from queue, table_id: " << table_id << ", wal_id: " << wal_id; |
191 | 0 | it->second.erase(wal_id); |
192 | 0 | if (it->second.empty()) { |
193 | 0 | _wal_queues.erase(table_id); |
194 | 0 | } |
195 | 0 | } |
196 | 1 | } |
197 | | |
198 | 0 | size_t WalManager::get_wal_queue_size(int64_t table_id) { |
199 | 0 | std::shared_lock rdlock(_wal_queue_lock); |
200 | 0 | size_t count = 0; |
201 | 0 | if (table_id > 0) { |
202 | 0 | auto it = _wal_queues.find(table_id); |
203 | 0 | if (it != _wal_queues.end()) { |
204 | 0 | return it->second.size(); |
205 | 0 | } else { |
206 | 0 | return 0; |
207 | 0 | } |
208 | 0 | } else { |
209 | | // table_id is -1 meaning get all table wal size |
210 | 0 | size_t max_count_per_table = 0; |
211 | 0 | for (const auto& [_, table_wals] : _wal_queues) { |
212 | 0 | size_t table_wal_count = table_wals.size(); |
213 | 0 | count += table_wal_count; |
214 | 0 | if (table_wal_count > max_count_per_table) { |
215 | 0 | max_count_per_table = table_wal_count; |
216 | 0 | } |
217 | 0 | } |
218 | 0 | g_wal_max_count_per_table.set_value(max_count_per_table); |
219 | 0 | } |
220 | 0 | return count; |
221 | 0 | } |
222 | | |
223 | 0 | std::string WalManager::get_last_replay_wal_failed_reason(int64_t table_id) { |
224 | 0 | std::shared_lock rdlock(_table_lock); |
225 | 0 | auto it = _table_map.find(table_id); |
226 | 0 | if (it != _table_map.end()) { |
227 | 0 | return it->second->get_last_replay_wal_failed_reason(); |
228 | 0 | } |
229 | 0 | return ""; |
230 | 0 | } |
231 | | |
232 | | Status WalManager::create_wal_path(int64_t db_id, int64_t table_id, int64_t wal_id, |
233 | | const std::string& label, std::string& base_path, |
234 | 4 | uint32_t wal_version) { |
235 | 4 | base_path = _wal_dirs_info->get_available_random_wal_dir(); |
236 | 4 | std::stringstream ss; |
237 | 4 | ss << base_path << "/" << std::to_string(db_id) << "/" << std::to_string(table_id) << "/" |
238 | 4 | << std::to_string(wal_version) << "_" << _exec_env->cluster_info()->backend_id << "_" |
239 | 4 | << std::to_string(wal_id) << "_" << label; |
240 | 4 | { |
241 | 4 | std::lock_guard<std::shared_mutex> wrlock(_wal_path_lock); |
242 | 4 | auto it = _wal_path_map.find(wal_id); |
243 | 4 | if (it != _wal_path_map.end()) { |
244 | 0 | return Status::InternalError("wal_id {} already in wal_path_map", wal_id); |
245 | 0 | } |
246 | 4 | _wal_path_map.emplace(wal_id, ss.str()); |
247 | 4 | } |
248 | 0 | return Status::OK(); |
249 | 4 | } |
250 | | |
251 | 0 | Status WalManager::get_wal_path(int64_t wal_id, std::string& wal_path) { |
252 | 0 | std::shared_lock rdlock(_wal_path_lock); |
253 | 0 | auto it = _wal_path_map.find(wal_id); |
254 | 0 | if (it != _wal_path_map.end()) { |
255 | 0 | wal_path = _wal_path_map[wal_id]; |
256 | 0 | } else { |
257 | 0 | return Status::InternalError("can not find wal_id {} in wal_path_map", wal_id); |
258 | 0 | } |
259 | 0 | return Status::OK(); |
260 | 0 | } |
261 | | |
262 | | Status WalManager::parse_wal_path(const std::string& file_name, int64_t& version, |
263 | 2 | int64_t& backend_id, int64_t& wal_id, std::string& label) { |
264 | 2 | try { |
265 | | // find version |
266 | 2 | auto pos = file_name.find("_"); |
267 | 2 | version = std::strtoll(file_name.substr(0, pos).c_str(), NULL, 10); |
268 | | // find be id |
269 | 2 | auto substring1 = file_name.substr(pos + 1); |
270 | 2 | pos = substring1.find("_"); |
271 | 2 | backend_id = std::strtoll(substring1.substr(0, pos).c_str(), NULL, 10); |
272 | | // find wal id |
273 | 2 | auto substring2 = substring1.substr(pos + 1); |
274 | 2 | pos = substring2.find("_"); |
275 | 2 | wal_id = std::strtoll(substring2.substr(0, pos).c_str(), NULL, 10); |
276 | | // find label |
277 | 2 | label = substring2.substr(pos + 1); |
278 | 2 | VLOG_DEBUG << "version:" << version << "backend_id:" << backend_id << ",wal_id:" << wal_id |
279 | 0 | << ",label:" << label; |
280 | 2 | } catch (const std::invalid_argument& e) { |
281 | 0 | return Status::InvalidArgument("Invalid format, {}", e.what()); |
282 | 0 | } |
283 | 2 | return Status::OK(); |
284 | 2 | } |
285 | | |
286 | 0 | Status WalManager::_load_wals() { |
287 | 0 | std::vector<ScanWalInfo> wals; |
288 | 0 | for (auto wal_dir : _wal_dirs) { |
289 | 0 | WARN_IF_ERROR(_scan_wals(wal_dir, wals), fmt::format("fail to scan wal dir={}", wal_dir)); |
290 | 0 | } |
291 | 0 | for (const auto& wal : wals) { |
292 | 0 | bool exists = false; |
293 | 0 | WARN_IF_ERROR(io::global_local_filesystem()->exists(wal.wal_path, &exists), |
294 | 0 | fmt::format("fail to check exist on wal file={}", wal.wal_path)); |
295 | 0 | if (!exists) { |
296 | 0 | continue; |
297 | 0 | } |
298 | 0 | LOG(INFO) << "find wal: " << wal.wal_path; |
299 | 0 | { |
300 | 0 | std::lock_guard<std::shared_mutex> wrlock(_wal_path_lock); |
301 | 0 | auto it = _wal_path_map.find(wal.wal_id); |
302 | 0 | if (it != _wal_path_map.end()) { |
303 | 0 | LOG(INFO) << "wal_id " << wal.wal_id << " already in wal_path_map, skip it"; |
304 | 0 | continue; |
305 | 0 | } |
306 | 0 | _wal_path_map.emplace(wal.wal_id, wal.wal_path); |
307 | 0 | } |
308 | | // this config is use for test p0 case in pipeline |
309 | 0 | if (config::group_commit_wait_replay_wal_finish) { |
310 | 0 | auto lock = std::make_shared<std::mutex>(); |
311 | 0 | auto cv = std::make_shared<std::condition_variable>(); |
312 | 0 | auto add_st = add_wal_cv_map(wal.wal_id, lock, cv); |
313 | 0 | if (!add_st.ok()) { |
314 | 0 | LOG(WARNING) << "fail to add wal_id " << wal.wal_id << " to wal_cv_map"; |
315 | 0 | continue; |
316 | 0 | } |
317 | 0 | } |
318 | 0 | _exec_env->wal_mgr()->add_wal_queue(wal.tb_id, wal.wal_id); |
319 | 0 | WARN_IF_ERROR(add_recover_wal(wal.db_id, wal.tb_id, wal.wal_id, wal.wal_path), |
320 | 0 | fmt::format("Failed to add recover wal={}", wal.wal_path)); |
321 | 0 | } |
322 | 0 | return Status::OK(); |
323 | 0 | } |
324 | | |
325 | 0 | Status WalManager::_scan_wals(const std::string& wal_path, std::vector<ScanWalInfo>& res) { |
326 | 0 | bool exists = false; |
327 | 0 | auto last_total_size = res.size(); |
328 | 0 | std::vector<io::FileInfo> dbs; |
329 | 0 | Status st = io::global_local_filesystem()->list(wal_path, false, &dbs, &exists); |
330 | 0 | if (!st.ok()) { |
331 | 0 | LOG(WARNING) << "failed list files for wal_dir=" << wal_path << ", st=" << st.to_string(); |
332 | 0 | return st; |
333 | 0 | } |
334 | 0 | for (const auto& database_id : dbs) { |
335 | 0 | if (database_id.is_file || database_id.file_name == _tmp) { |
336 | 0 | continue; |
337 | 0 | } |
338 | 0 | std::vector<io::FileInfo> tables; |
339 | 0 | auto db_path = wal_path + "/" + database_id.file_name; |
340 | 0 | st = io::global_local_filesystem()->list(db_path, false, &tables, &exists); |
341 | 0 | if (!st.ok()) { |
342 | 0 | LOG(WARNING) << "failed list files for wal_dir=" << db_path |
343 | 0 | << ", st=" << st.to_string(); |
344 | 0 | return st; |
345 | 0 | } |
346 | 0 | for (const auto& table_id : tables) { |
347 | 0 | if (table_id.is_file) { |
348 | 0 | continue; |
349 | 0 | } |
350 | 0 | std::vector<io::FileInfo> wals; |
351 | 0 | auto table_path = db_path + "/" + table_id.file_name; |
352 | 0 | st = io::global_local_filesystem()->list(table_path, false, &wals, &exists); |
353 | 0 | if (!st.ok()) { |
354 | 0 | LOG(WARNING) << "failed list files for wal_dir=" << table_path |
355 | 0 | << ", st=" << st.to_string(); |
356 | 0 | return st; |
357 | 0 | } |
358 | 0 | if (wals.empty()) { |
359 | 0 | continue; |
360 | 0 | } |
361 | 0 | int64_t db_id = -1; |
362 | 0 | int64_t tb_id = -1; |
363 | 0 | try { |
364 | 0 | db_id = std::strtoll(database_id.file_name.c_str(), NULL, 10); |
365 | 0 | tb_id = std::strtoll(table_id.file_name.c_str(), NULL, 10); |
366 | 0 | } catch (const std::invalid_argument& e) { |
367 | 0 | return Status::InvalidArgument("Invalid format, {}", e.what()); |
368 | 0 | } |
369 | 0 | for (const auto& wal : wals) { |
370 | 0 | int64_t version = -1; |
371 | 0 | int64_t backend_id = -1; |
372 | 0 | int64_t wal_id = -1; |
373 | 0 | std::string label = ""; |
374 | 0 | auto parse_st = parse_wal_path(wal.file_name, version, backend_id, wal_id, label); |
375 | 0 | if (!parse_st.ok()) { |
376 | 0 | LOG(WARNING) << "fail to parse file=" << wal.file_name |
377 | 0 | << ",st=" << parse_st.to_string(); |
378 | 0 | continue; |
379 | 0 | } |
380 | 0 | auto wal_file = table_path + "/" + wal.file_name; |
381 | 0 | struct ScanWalInfo scan_wal_info; |
382 | 0 | scan_wal_info.wal_path = wal_file; |
383 | 0 | scan_wal_info.db_id = db_id; |
384 | 0 | scan_wal_info.tb_id = tb_id; |
385 | 0 | scan_wal_info.wal_id = wal_id; |
386 | 0 | scan_wal_info.be_id = backend_id; |
387 | 0 | res.emplace_back(scan_wal_info); |
388 | 0 | } |
389 | 0 | } |
390 | 0 | } |
391 | 0 | LOG(INFO) << "Finish list wal_dir=" << wal_path |
392 | 0 | << ", wal count=" << std::to_string(res.size() - last_total_size); |
393 | 0 | return Status::OK(); |
394 | 0 | } |
395 | | |
396 | 0 | Status WalManager::_replay_background() { |
397 | 0 | do { |
398 | 0 | if (_stop.load()) { |
399 | 0 | break; |
400 | 0 | } |
401 | | // port == 0 means not received heartbeat yet |
402 | 0 | if (_exec_env->cluster_info() != nullptr && |
403 | 0 | _exec_env->cluster_info()->master_fe_addr.port == 0) { |
404 | 0 | continue; |
405 | 0 | } |
406 | | // replay residual wal,only replay once |
407 | 0 | bool expected = true; |
408 | 0 | if (_first_replay.compare_exchange_strong(expected, false)) { |
409 | 0 | RETURN_IF_ERROR(_load_wals()); |
410 | 0 | } |
411 | 0 | g_wal_total_count.set_value(get_wal_queue_size(-1)); |
412 | | // replay wal of current process |
413 | 0 | std::vector<int64_t> replay_tables; |
414 | 0 | { |
415 | 0 | std::lock_guard<std::shared_mutex> wrlock(_table_lock); |
416 | 0 | auto it = _table_map.begin(); |
417 | 0 | while (it != _table_map.end()) { |
418 | 0 | if (it->second->size() > 0) { |
419 | 0 | replay_tables.push_back(it->first); |
420 | 0 | } |
421 | 0 | it++; |
422 | 0 | } |
423 | 0 | } |
424 | 0 | for (const auto& table_id : replay_tables) { |
425 | 0 | RETURN_IF_ERROR(_thread_pool->submit_func([table_id, this] { |
426 | 0 | auto st = this->_table_map[table_id]->replay_wals(); |
427 | 0 | if (!st.ok()) { |
428 | 0 | LOG(WARNING) << "failed to submit replay wal for table=" << table_id; |
429 | 0 | } |
430 | 0 | })); |
431 | 0 | } |
432 | 0 | } while (!_stop_background_threads_latch.wait_for( |
433 | 0 | std::chrono::seconds(config::group_commit_replay_wal_retry_interval_seconds))); |
434 | 0 | return Status::OK(); |
435 | 0 | } |
436 | | |
437 | | Status WalManager::add_recover_wal(int64_t db_id, int64_t table_id, int64_t wal_id, |
438 | 0 | std::string wal) { |
439 | 0 | std::lock_guard<std::shared_mutex> wrlock(_table_lock); |
440 | 0 | std::shared_ptr<WalTable> table_ptr; |
441 | 0 | auto it = _table_map.find(table_id); |
442 | 0 | if (it == _table_map.end()) { |
443 | 0 | table_ptr = std::make_shared<WalTable>(_exec_env, db_id, table_id); |
444 | 0 | _table_map.emplace(table_id, table_ptr); |
445 | 0 | } else { |
446 | 0 | table_ptr = it->second; |
447 | 0 | } |
448 | 0 | table_ptr->add_wal(wal_id, wal); |
449 | | #ifndef BE_TEST |
450 | | WARN_IF_ERROR(update_wal_dir_limit(get_base_wal_path(wal)), |
451 | | "Failed to update wal dir limit while add recover wal!"); |
452 | | WARN_IF_ERROR(update_wal_dir_used(get_base_wal_path(wal)), |
453 | | "Failed to update wal dir used while add recove wal!"); |
454 | | #endif |
455 | 0 | return Status::OK(); |
456 | 0 | } |
457 | | |
458 | 0 | size_t WalManager::get_wal_table_size(int64_t table_id) { |
459 | 0 | std::shared_lock rdlock(_table_lock); |
460 | 0 | auto it = _table_map.find(table_id); |
461 | 0 | if (it != _table_map.end()) { |
462 | 0 | return it->second->size(); |
463 | 0 | } else { |
464 | 0 | return 0; |
465 | 0 | } |
466 | 0 | } |
467 | | |
468 | 2 | void WalManager::_stop_relay_wal() { |
469 | 2 | std::lock_guard<std::shared_mutex> wrlock(_table_lock); |
470 | 2 | for (auto& [_, wal_table] : _table_map) { |
471 | 0 | wal_table->stop(); |
472 | 0 | } |
473 | 2 | } |
474 | | |
475 | 2 | size_t WalManager::get_max_available_size() { |
476 | 2 | return _wal_dirs_info->get_max_available_size(); |
477 | 2 | } |
478 | | |
479 | 0 | std::string WalManager::get_wal_dirs_info_string() { |
480 | 0 | return _wal_dirs_info->get_wal_dirs_info_string(); |
481 | 0 | } |
482 | | |
483 | 0 | Status WalManager::update_wal_dir_limit(const std::string& wal_dir, size_t limit) { |
484 | 0 | return _wal_dirs_info->update_wal_dir_limit(wal_dir, limit); |
485 | 0 | } |
486 | | |
487 | 0 | Status WalManager::update_wal_dir_used(const std::string& wal_dir, size_t used) { |
488 | 0 | return _wal_dirs_info->update_wal_dir_used(wal_dir, used); |
489 | 0 | } |
490 | | |
491 | | Status WalManager::update_wal_dir_estimated_wal_bytes(const std::string& wal_dir, |
492 | | size_t increase_estimated_wal_bytes, |
493 | 0 | size_t decrease_estimated_wal_bytes) { |
494 | 0 | return _wal_dirs_info->update_wal_dir_estimated_wal_bytes(wal_dir, increase_estimated_wal_bytes, |
495 | 0 | decrease_estimated_wal_bytes); |
496 | 0 | } |
497 | | |
498 | 0 | Status WalManager::_update_wal_dir_info_thread() { |
499 | 0 | while (!_stop.load()) { |
500 | 0 | if (!ExecEnv::ready()) { |
501 | 0 | VLOG_DEBUG << "Sleep 1s to wait for storage engine init."; |
502 | 0 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); |
503 | 0 | continue; |
504 | 0 | } |
505 | 0 | static_cast<void>(_wal_dirs_info->update_all_wal_dir_limit()); |
506 | 0 | static_cast<void>(_wal_dirs_info->update_all_wal_dir_used()); |
507 | 0 | LOG_EVERY_N(INFO, 100) << "Scheduled(every 10s) WAL info: " << get_wal_dirs_info_string(); |
508 | 0 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); |
509 | 0 | } |
510 | 0 | return Status::OK(); |
511 | 0 | } |
512 | | |
513 | 0 | Status WalManager::get_wal_dir_available_size(const std::string& wal_dir, size_t* available_bytes) { |
514 | 0 | return _wal_dirs_info->get_wal_dir_available_size(wal_dir, available_bytes); |
515 | 0 | } |
516 | | |
517 | 0 | std::string WalManager::get_base_wal_path(const std::string& wal_path_str) { |
518 | 0 | io::Path wal_path = wal_path_str; |
519 | 0 | for (int i = 0; i < 3; ++i) { |
520 | 0 | if (!wal_path.has_parent_path()) { |
521 | 0 | return ""; |
522 | 0 | } |
523 | 0 | wal_path = wal_path.parent_path(); |
524 | 0 | } |
525 | 0 | return wal_path.string(); |
526 | 0 | } |
527 | | |
528 | | Status WalManager::add_wal_cv_map(int64_t wal_id, std::shared_ptr<std::mutex> lock, |
529 | 0 | std::shared_ptr<std::condition_variable> cv) { |
530 | 0 | std::lock_guard<std::shared_mutex> wrlock(_wal_cv_lock); |
531 | 0 | auto it = _wal_cv_map.find(wal_id); |
532 | 0 | if (it != _wal_cv_map.end()) { |
533 | 0 | return Status::InternalError("wal {} is already in _wal_cv_map ", wal_id); |
534 | 0 | } |
535 | 0 | auto pair = std::make_pair(lock, cv); |
536 | 0 | _wal_cv_map.emplace(wal_id, pair); |
537 | 0 | LOG(INFO) << "add " << wal_id << " to _wal_cv_map"; |
538 | 0 | return Status::OK(); |
539 | 0 | } |
540 | | |
541 | 0 | Status WalManager::erase_wal_cv_map(int64_t wal_id) { |
542 | 0 | std::lock_guard<std::shared_mutex> wrlock(_wal_cv_lock); |
543 | 0 | if (_wal_cv_map.erase(wal_id)) { |
544 | 0 | LOG(INFO) << "erase " << wal_id << " from _wal_cv_map"; |
545 | 0 | } else { |
546 | 0 | return Status::InternalError("fail to erase wal {} from wal_cv_map", wal_id); |
547 | 0 | } |
548 | 0 | return Status::OK(); |
549 | 0 | } |
550 | | |
551 | 0 | Status WalManager::wait_replay_wal_finish(int64_t wal_id) { |
552 | 0 | std::shared_ptr<std::mutex> lock = nullptr; |
553 | 0 | std::shared_ptr<std::condition_variable> cv = nullptr; |
554 | 0 | auto st = get_lock_and_cv(wal_id, lock, cv); |
555 | 0 | if (st.ok()) { |
556 | 0 | std::unique_lock l(*(lock)); |
557 | 0 | LOG(INFO) << "start wait " << wal_id; |
558 | 0 | if (cv->wait_for(l, std::chrono::seconds(180)) == std::cv_status::timeout) { |
559 | 0 | LOG(WARNING) << "wait for " << wal_id << " is time out"; |
560 | 0 | } |
561 | 0 | LOG(INFO) << "get wal " << wal_id << ",finish wait"; |
562 | 0 | RETURN_IF_ERROR(erase_wal_cv_map(wal_id)); |
563 | 0 | LOG(INFO) << "erase wal " << wal_id; |
564 | 0 | } |
565 | 0 | return Status::OK(); |
566 | 0 | } |
567 | | |
568 | 0 | Status WalManager::notify_relay_wal(int64_t wal_id) { |
569 | 0 | std::shared_ptr<std::mutex> lock = nullptr; |
570 | 0 | std::shared_ptr<std::condition_variable> cv = nullptr; |
571 | 0 | auto st = get_lock_and_cv(wal_id, lock, cv); |
572 | 0 | if (st.ok()) { |
573 | 0 | std::unique_lock l(*(lock)); |
574 | 0 | cv->notify_all(); |
575 | 0 | LOG(INFO) << "get wal " << wal_id << ",notify all"; |
576 | 0 | } |
577 | 0 | return Status::OK(); |
578 | 0 | } |
579 | | |
580 | | Status WalManager::get_lock_and_cv(int64_t wal_id, std::shared_ptr<std::mutex>& lock, |
581 | 0 | std::shared_ptr<std::condition_variable>& cv) { |
582 | 0 | std::lock_guard<std::shared_mutex> wrlock(_wal_cv_lock); |
583 | 0 | auto it = _wal_cv_map.find(wal_id); |
584 | 0 | if (it == _wal_cv_map.end()) { |
585 | 0 | return Status::InternalError("cannot find txn {} in _wal_cv_map", wal_id); |
586 | 0 | } |
587 | 0 | lock = it->second.first; |
588 | 0 | cv = it->second.second; |
589 | 0 | return Status::OK(); |
590 | 0 | } |
591 | | |
592 | 1 | Status WalManager::delete_wal(int64_t table_id, int64_t wal_id) { |
593 | 1 | std::string wal_path; |
594 | 1 | { |
595 | 1 | std::lock_guard<std::shared_mutex> wrlock(_wal_path_lock); |
596 | 1 | auto it = _wal_path_map.find(wal_id); |
597 | 1 | if (it != _wal_path_map.end()) { |
598 | 0 | wal_path = it->second; |
599 | 0 | auto st = io::global_local_filesystem()->delete_file(wal_path); |
600 | 0 | if (st.ok()) { |
601 | 0 | LOG(INFO) << "delete wal=" << wal_path; |
602 | 0 | } else { |
603 | 0 | LOG(WARNING) << "failed to delete wal=" << wal_path << ", st=" << st.to_string(); |
604 | 0 | } |
605 | 0 | _wal_path_map.erase(wal_id); |
606 | 0 | } |
607 | 1 | } |
608 | 1 | erase_wal_queue(table_id, wal_id); |
609 | 1 | return Status::OK(); |
610 | 1 | } |
611 | | |
612 | 0 | Status WalManager::rename_to_tmp_path(const std::string wal, int64_t table_id, int64_t wal_id) { |
613 | 0 | io::Path wal_path = wal; |
614 | 0 | std::list<std::string> path_element; |
615 | 0 | for (int i = 0; i < 3; ++i) { |
616 | 0 | if (!wal_path.has_parent_path()) { |
617 | 0 | return Status::InternalError("parent path is not enough when rename " + wal); |
618 | 0 | } |
619 | 0 | path_element.push_front(wal_path.filename().string()); |
620 | 0 | wal_path = wal_path.parent_path(); |
621 | 0 | } |
622 | 0 | wal_path.append(_tmp); |
623 | 0 | for (auto path : path_element) { |
624 | 0 | wal_path.append(path); |
625 | 0 | } |
626 | 0 | bool exists = false; |
627 | 0 | RETURN_IF_ERROR(io::global_local_filesystem()->exists(wal_path.parent_path(), &exists)); |
628 | 0 | if (!exists) { |
629 | 0 | RETURN_IF_ERROR(io::global_local_filesystem()->create_directory(wal_path.parent_path())); |
630 | 0 | } |
631 | 0 | auto res = std::rename(wal.c_str(), wal_path.string().c_str()); |
632 | 0 | if (res < 0) { |
633 | 0 | LOG(INFO) << "failed to rename wal from " << wal << " to " << wal_path.string(); |
634 | 0 | return Status::InternalError("rename fail on path " + wal); |
635 | 0 | } |
636 | 0 | LOG(INFO) << "rename wal from " << wal << " to " << wal_path.string(); |
637 | 0 | { |
638 | 0 | std::lock_guard<std::shared_mutex> wrlock(_wal_path_lock); |
639 | 0 | auto it = _wal_path_map.find(wal_id); |
640 | 0 | if (it != _wal_path_map.end()) { |
641 | 0 | _wal_path_map.erase(wal_id); |
642 | 0 | } else { |
643 | | LOG(WARNING) << "can't find " << wal_id << " in _wal_path_map when trying to rename"; |
644 | 0 | } |
645 | 0 | } |
646 | 0 | erase_wal_queue(table_id, wal_id); |
647 | 0 | return Status::OK(); |
648 | 0 | } |
649 | | |
650 | | } // namespace doris |