Coverage Report

Created: 2026-08-06 13:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
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