Coverage Report

Created: 2026-07-23 16:24

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