Coverage Report

Created: 2026-07-09 12:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/data_dir.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 "storage/data_dir.h"
19
20
#include <fmt/core.h>
21
#include <fmt/format.h>
22
#include <gen_cpp/FrontendService_types.h>
23
#include <gen_cpp/Types_types.h>
24
#include <gen_cpp/olap_file.pb.h>
25
26
#include <atomic>
27
#include <cstdio>
28
// IWYU pragma: no_include <bits/chrono.h>
29
#include <chrono> // IWYU pragma: keep
30
#include <cstddef>
31
#include <filesystem>
32
#include <map>
33
#include <memory>
34
#include <new>
35
#include <optional>
36
#include <roaring/roaring.hh>
37
#include <set>
38
#include <sstream>
39
#include <string>
40
#include <thread>
41
#include <utility>
42
43
#include "common/cast_set.h"
44
#include "common/config.h"
45
#include "common/logging.h"
46
#include "common/metrics/doris_metrics.h"
47
#include "io/fs/file_reader.h"
48
#include "io/fs/file_writer.h"
49
#include "io/fs/local_file_system.h"
50
#include "io/fs/path.h"
51
#include "service/backend_options.h"
52
#include "storage/delete/delete_handler.h"
53
#include "storage/olap_common.h"
54
#include "storage/olap_define.h"
55
#include "storage/olap_meta.h"
56
#include "storage/rowset/beta_rowset.h"
57
#include "storage/rowset/pending_rowset_helper.h"
58
#include "storage/rowset/rowset.h"
59
#include "storage/rowset/rowset_id_generator.h"
60
#include "storage/rowset/rowset_meta.h"
61
#include "storage/rowset/rowset_meta_manager.h"
62
#include "storage/storage_engine.h"
63
#include "storage/storage_policy.h"
64
#include "storage/tablet/tablet.h"
65
#include "storage/tablet/tablet_manager.h"
66
#include "storage/tablet/tablet_meta_manager.h"
67
#include "storage/txn/txn_manager.h"
68
#include "storage/utils.h" // for check_dir_existed
69
#include "util/string_util.h"
70
#include "util/uid_util.h"
71
72
namespace doris {
73
using namespace ErrorCode;
74
75
namespace {
76
77
175
Status read_cluster_id(const std::string& cluster_id_path, int32_t* cluster_id) {
78
175
    bool exists = false;
79
175
    RETURN_IF_ERROR(io::global_local_filesystem()->exists(cluster_id_path, &exists));
80
175
    *cluster_id = -1;
81
175
    if (exists) {
82
6
        io::FileReaderSPtr reader;
83
6
        RETURN_IF_ERROR(io::global_local_filesystem()->open_file(cluster_id_path, &reader));
84
6
        size_t fsize = reader->size();
85
6
        if (fsize > 0) {
86
6
            std::string content;
87
6
            content.resize(fsize, '\0');
88
6
            size_t bytes_read = 0;
89
6
            RETURN_IF_ERROR(reader->read_at(0, {content.data(), fsize}, &bytes_read));
90
6
            DCHECK_EQ(fsize, bytes_read);
91
6
            *cluster_id = std::stoi(content);
92
6
        }
93
6
    }
94
175
    return Status::OK();
95
175
}
96
97
2
Status _write_cluster_id_to_path(const std::string& path, int32_t cluster_id) {
98
2
    bool exists = false;
99
2
    RETURN_IF_ERROR(io::global_local_filesystem()->exists(path, &exists));
100
2
    if (!exists) {
101
2
        io::FileWriterPtr file_writer;
102
2
        RETURN_IF_ERROR(io::global_local_filesystem()->create_file(path, &file_writer));
103
2
        RETURN_IF_ERROR(file_writer->append(std::to_string(cluster_id)));
104
2
        RETURN_IF_ERROR(file_writer->close());
105
2
    }
106
2
    return Status::OK();
107
2
}
108
109
} // namespace
110
111
DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(disks_total_capacity, MetricUnit::BYTES);
112
DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(disks_avail_capacity, MetricUnit::BYTES);
113
DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(disks_local_used_capacity, MetricUnit::BYTES);
114
DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(disks_remote_used_capacity, MetricUnit::BYTES);
115
DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(disks_trash_used_capacity, MetricUnit::BYTES);
116
DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(disks_state, MetricUnit::BYTES);
117
DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(disks_compaction_score, MetricUnit::NOUNIT);
118
DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(disks_compaction_num, MetricUnit::NOUNIT);
119
120
DataDir::DataDir(StorageEngine& engine, const std::string& path, int64_t capacity_bytes,
121
                 TStorageMedium::type storage_medium)
122
346
        : _engine(engine),
123
346
          _path(path),
124
346
          _available_bytes(0),
125
346
          _disk_capacity_bytes(0),
126
346
          _trash_used_bytes(0),
127
346
          _trash_file_num(0),
128
346
          _storage_medium(storage_medium),
129
346
          _is_used(false),
130
346
          _cluster_id(-1),
131
346
          _to_be_deleted(false) {
132
346
    _data_dir_metric_entity = DorisMetrics::instance()->metric_registry()->register_entity(
133
346
            std::string("data_dir.") + path, {{"path", path}});
134
346
    INT_GAUGE_METRIC_REGISTER(_data_dir_metric_entity, disks_total_capacity);
135
346
    INT_GAUGE_METRIC_REGISTER(_data_dir_metric_entity, disks_avail_capacity);
136
346
    INT_GAUGE_METRIC_REGISTER(_data_dir_metric_entity, disks_local_used_capacity);
137
346
    INT_GAUGE_METRIC_REGISTER(_data_dir_metric_entity, disks_remote_used_capacity);
138
346
    INT_GAUGE_METRIC_REGISTER(_data_dir_metric_entity, disks_trash_used_capacity);
139
346
    INT_GAUGE_METRIC_REGISTER(_data_dir_metric_entity, disks_state);
140
346
    INT_GAUGE_METRIC_REGISTER(_data_dir_metric_entity, disks_compaction_score);
141
346
    INT_GAUGE_METRIC_REGISTER(_data_dir_metric_entity, disks_compaction_num);
142
346
}
143
144
341
DataDir::~DataDir() {
145
341
    DorisMetrics::instance()->metric_registry()->deregister_entity(_data_dir_metric_entity);
146
341
    delete _meta;
147
341
}
148
149
175
Status DataDir::init(bool init_meta) {
150
175
    bool exists = false;
151
175
    RETURN_IF_ERROR(io::global_local_filesystem()->exists(_path, &exists));
152
175
    if (!exists) {
153
0
        RETURN_NOT_OK_STATUS_WITH_WARN(Status::IOError("opendir failed, path={}", _path),
154
0
                                       "check file exist failed");
155
0
    }
156
157
175
    RETURN_NOT_OK_STATUS_WITH_WARN(update_capacity(), "update_capacity failed");
158
175
    RETURN_NOT_OK_STATUS_WITH_WARN(_init_cluster_id(), "_init_cluster_id failed");
159
175
    RETURN_NOT_OK_STATUS_WITH_WARN(_init_capacity_and_create_shards(),
160
175
                                   "_init_capacity_and_create_shards failed");
161
175
    if (init_meta) {
162
175
        RETURN_NOT_OK_STATUS_WITH_WARN(_init_meta(), "_init_meta failed");
163
175
    }
164
165
175
    _is_used = true;
166
175
    return Status::OK();
167
175
}
168
169
54
void DataDir::stop_bg_worker() {
170
54
    _stop_bg_worker = true;
171
54
}
172
173
175
Status DataDir::_init_cluster_id() {
174
175
    auto cluster_id_path = fmt::format("{}/{}", _path, CLUSTER_ID_PREFIX);
175
175
    RETURN_IF_ERROR(read_cluster_id(cluster_id_path, &_cluster_id));
176
175
    if (_cluster_id == -1) {
177
169
        _cluster_id_incomplete = true;
178
169
    }
179
175
    return Status::OK();
180
175
}
181
182
175
Status DataDir::_init_capacity_and_create_shards() {
183
175
    RETURN_IF_ERROR(io::global_local_filesystem()->get_space_info(_path, &_disk_capacity_bytes,
184
175
                                                                  &_available_bytes));
185
175
    auto data_path = fmt::format("{}/{}", _path, DATA_PREFIX);
186
175
    bool exists = false;
187
175
    RETURN_IF_ERROR(io::global_local_filesystem()->exists(data_path, &exists));
188
175
    if (!exists) {
189
169
        RETURN_IF_ERROR(io::global_local_filesystem()->create_directory(data_path));
190
169
    }
191
179k
    for (int i = 0; i < MAX_SHARD_NUM; ++i) {
192
179k
        auto shard_path = fmt::format("{}/{}", data_path, i);
193
179k
        RETURN_IF_ERROR(io::global_local_filesystem()->exists(shard_path, &exists));
194
179k
        if (!exists) {
195
173k
            RETURN_IF_ERROR(io::global_local_filesystem()->create_directory(shard_path));
196
173k
        }
197
179k
    }
198
199
175
    return Status::OK();
200
175
}
201
202
182
Status DataDir::_init_meta() {
203
    // init path hash
204
182
    _path_hash = hash_of_path(BackendOptions::get_localhost(), _path);
205
182
    LOG(INFO) << "path: " << _path << ", hash: " << _path_hash;
206
207
    // init meta
208
182
    _meta = new (std::nothrow) OlapMeta(_path);
209
182
    if (_meta == nullptr) {
210
0
        RETURN_NOT_OK_STATUS_WITH_WARN(
211
0
                Status::MemoryAllocFailed("allocate memory for OlapMeta failed"),
212
0
                "new OlapMeta failed");
213
0
    }
214
182
    Status res = _meta->init();
215
182
    if (!res.ok()) {
216
0
        RETURN_NOT_OK_STATUS_WITH_WARN(Status::IOError("open rocksdb failed, path={}", _path),
217
0
                                       "init OlapMeta failed");
218
0
    }
219
182
    return Status::OK();
220
182
}
221
222
2
Status DataDir::set_cluster_id(int32_t cluster_id) {
223
2
    if (_cluster_id != -1 && _cluster_id != cluster_id) {
224
0
        LOG(ERROR) << "going to set cluster id to already assigned store, cluster_id="
225
0
                   << _cluster_id << ", new_cluster_id=" << cluster_id;
226
0
        return Status::InternalError("going to set cluster id to already assigned store");
227
0
    }
228
2
    if (!_cluster_id_incomplete) {
229
0
        return Status::OK();
230
0
    }
231
2
    auto cluster_id_path = fmt::format("{}/{}", _path, CLUSTER_ID_PREFIX);
232
2
    return _write_cluster_id_to_path(cluster_id_path, cluster_id);
233
2
}
234
235
2.99k
void DataDir::health_check() {
236
    // check disk
237
2.99k
    if (_is_used) {
238
2.99k
        Status res = _read_and_write_test_file();
239
2.99k
        if (!res && res.is<IO_ERROR>()) {
240
0
            LOG(WARNING) << "store read/write test file occur IO Error. path=" << _path
241
0
                         << ", err: " << res;
242
0
            _engine.add_broken_path(_path);
243
0
            _is_used = !res.is<IO_ERROR>();
244
0
        }
245
2.99k
    }
246
2.99k
    disks_state->set_value(_is_used ? 1 : 0);
247
2.99k
}
248
249
2.99k
Status DataDir::_read_and_write_test_file() {
250
2.99k
    auto test_file = fmt::format("{}/{}", _path, kTestFilePath);
251
2.99k
    return read_write_test_file(test_file);
252
2.99k
}
253
254
286k
void DataDir::register_tablet(Tablet* tablet) {
255
286k
    TabletInfo tablet_info(tablet->tablet_id(), tablet->tablet_uid());
256
257
286k
    std::lock_guard<std::mutex> l(_mutex);
258
286k
    _tablet_set.emplace(std::move(tablet_info));
259
286k
}
260
261
5.94k
void DataDir::deregister_tablet(Tablet* tablet) {
262
5.94k
    TabletInfo tablet_info(tablet->tablet_id(), tablet->tablet_uid());
263
264
5.94k
    std::lock_guard<std::mutex> l(_mutex);
265
5.94k
    _tablet_set.erase(tablet_info);
266
5.94k
}
267
268
0
void DataDir::clear_tablets(std::vector<TabletInfo>* tablet_infos) {
269
0
    std::lock_guard<std::mutex> l(_mutex);
270
271
0
    tablet_infos->insert(tablet_infos->end(), _tablet_set.begin(), _tablet_set.end());
272
0
    _tablet_set.clear();
273
0
}
274
275
0
std::string DataDir::get_absolute_shard_path(int64_t shard_id) {
276
0
    return fmt::format("{}/{}/{}", _path, DATA_PREFIX, shard_id);
277
0
}
278
279
std::string DataDir::get_absolute_tablet_path(int64_t shard_id, int64_t tablet_id,
280
0
                                              int32_t schema_hash) {
281
0
    return fmt::format("{}/{}/{}", get_absolute_shard_path(shard_id), tablet_id, schema_hash);
282
0
}
283
284
0
void DataDir::find_tablet_in_trash(int64_t tablet_id, std::vector<std::string>* paths) {
285
    // path: /root_path/trash/time_label/tablet_id/schema_hash
286
0
    auto trash_path = fmt::format("{}/{}", _path, TRASH_PREFIX);
287
0
    bool exists = true;
288
0
    std::vector<io::FileInfo> sub_dirs;
289
0
    Status st = io::global_local_filesystem()->list(trash_path, false, &sub_dirs, &exists);
290
0
    if (!st) {
291
0
        return;
292
0
    }
293
294
0
    for (auto& sub_dir : sub_dirs) {
295
        // sub dir is time_label
296
0
        if (sub_dir.is_file) {
297
0
            continue;
298
0
        }
299
0
        auto sub_path = fmt::format("{}/{}", trash_path, sub_dir.file_name);
300
0
        auto tablet_path = fmt::format("{}/{}", sub_path, tablet_id);
301
0
        st = io::global_local_filesystem()->exists(tablet_path, &exists);
302
0
        if (st && exists) {
303
0
            paths->emplace_back(std::move(tablet_path));
304
0
        }
305
0
    }
306
0
}
307
308
std::string DataDir::get_root_path_from_schema_hash_path_in_trash(
309
0
        const std::string& schema_hash_dir_in_trash) {
310
0
    return io::Path(schema_hash_dir_in_trash)
311
0
            .parent_path()
312
0
            .parent_path()
313
0
            .parent_path()
314
0
            .parent_path()
315
0
            .string();
316
0
}
317
318
59
Status DataDir::_check_incompatible_old_format_tablet() {
319
59
    auto check_incompatible_old_func = [](int64_t tablet_id, int32_t schema_hash,
320
59
                                          std::string_view value) -> bool {
321
        // if strict check incompatible old format, then log fatal
322
0
        if (config::storage_strict_check_incompatible_old_format) {
323
0
            throw Exception(Status::FatalError(
324
0
                    "There are incompatible old format metas, current version does not support and "
325
0
                    "it may lead to data missing!!! tablet_id = {} schema_hash = {}",
326
0
                    tablet_id, schema_hash));
327
0
        } else {
328
0
            LOG(WARNING)
329
0
                    << "There are incompatible old format metas, current version does not support "
330
0
                    << "and it may lead to data missing!!! "
331
0
                    << "tablet_id = " << tablet_id << " schema_hash = " << schema_hash;
332
0
        }
333
0
        return false;
334
0
    };
335
336
    // seek old header prefix. when check_incompatible_old_func is called, it has old format in olap_meta
337
59
    Status check_incompatible_old_status = TabletMetaManager::traverse_headers(
338
59
            _meta, check_incompatible_old_func, OLD_HEADER_PREFIX);
339
59
    if (!check_incompatible_old_status) {
340
0
        LOG(WARNING) << "check incompatible old format meta fails, it may lead to data missing!!! "
341
0
                     << _path;
342
59
    } else {
343
59
        LOG(INFO) << "successfully check incompatible old format meta " << _path;
344
59
    }
345
59
    return check_incompatible_old_status;
346
59
}
347
348
// TODO(ygl): deal with rowsets and tablets when load failed
349
59
Status DataDir::load() {
350
59
    LOG(INFO) << "start to load tablets from " << _path;
351
352
    // load rowset meta from meta env and create rowset
353
    // COMMITTED: add to txn manager
354
    // VISIBLE: add to tablet
355
    // if one rowset load failed, then the total data dir will not be loaded
356
357
    // necessarily check incompatible old format. when there are old metas, it may load to data missing
358
59
    RETURN_IF_ERROR(_check_incompatible_old_format_tablet());
359
360
59
    std::vector<RowsetMetaSharedPtr> dir_rowset_metas;
361
59
    LOG(INFO) << "begin loading rowset from meta";
362
59
    auto load_rowset_func = [&dir_rowset_metas, this](TabletUid tablet_uid, RowsetId rowset_id,
363
1.08k
                                                      std::string_view meta_str) -> bool {
364
1.08k
        RowsetMetaSharedPtr rowset_meta(new RowsetMeta());
365
1.08k
        bool parsed = rowset_meta->init(meta_str);
366
1.08k
        if (!parsed) {
367
0
            LOG(WARNING) << "parse rowset meta string failed for rowset_id:" << rowset_id;
368
            // return false will break meta iterator, return true to skip this error
369
0
            return true;
370
0
        }
371
372
1.08k
        if (rowset_meta->has_delete_predicate()) {
373
            // copy the delete sub pred v1 to check then
374
29
            auto orig_delete_sub_pred = rowset_meta->delete_predicate().sub_predicates();
375
29
            auto* delete_pred = rowset_meta->mutable_delete_pred_pb();
376
377
29
            if ((!delete_pred->sub_predicates().empty() &&
378
29
                 delete_pred->sub_predicates_v2().empty()) ||
379
29
                (!delete_pred->in_predicates().empty() &&
380
29
                 delete_pred->in_predicates()[0].has_column_unique_id())) {
381
                // convert pred and write only when delete sub pred v2 is not set or there is in list pred to be set column uid
382
0
                RETURN_IF_ERROR(DeleteHandler::convert_to_sub_pred_v2(
383
0
                        delete_pred, rowset_meta->tablet_schema()));
384
0
                LOG(INFO) << fmt::format(
385
0
                        "convert rowset with old delete pred: rowset_id={}, tablet_id={}",
386
0
                        rowset_id.to_string(), tablet_uid.to_string());
387
0
                CHECK_EQ(orig_delete_sub_pred.size(), delete_pred->sub_predicates().size())
388
0
                        << "inconsistent sub predicate v1 after conversion";
389
0
                for (int i = 0; i < orig_delete_sub_pred.size(); ++i) {
390
0
                    CHECK_STREQ(orig_delete_sub_pred.Get(i).c_str(),
391
0
                                delete_pred->sub_predicates().Get(i).c_str())
392
0
                            << "inconsistent sub predicate v1 after conversion";
393
0
                }
394
0
                std::string result;
395
0
                rowset_meta->serialize(&result);
396
0
                std::string key =
397
0
                        ROWSET_PREFIX + tablet_uid.to_string() + "_" + rowset_id.to_string();
398
0
                RETURN_IF_ERROR(_meta->put(META_COLUMN_FAMILY_INDEX, key, result));
399
0
            }
400
29
        }
401
402
1.08k
        if (rowset_meta->partition_id() == 0) {
403
0
            LOG(WARNING) << "rs tablet=" << rowset_meta->tablet_id() << " rowset_id=" << rowset_id
404
0
                         << " load from meta but partition id eq 0";
405
0
        }
406
407
1.08k
        dir_rowset_metas.push_back(rowset_meta);
408
1.08k
        return true;
409
1.08k
    };
410
59
    MonotonicStopWatch rs_timer;
411
59
    rs_timer.start();
412
59
    Status load_rowset_status = RowsetMetaManager::traverse_rowset_metas(_meta, load_rowset_func);
413
59
    rs_timer.stop();
414
59
    if (!load_rowset_status) {
415
0
        LOG(WARNING) << "errors when load rowset meta from meta env, skip this data dir:" << _path;
416
59
    } else {
417
59
        LOG(INFO) << "load rowset from meta finished, cost: "
418
59
                  << rs_timer.elapsed_time_milliseconds() << " ms, data dir: " << _path;
419
59
    }
420
421
    // load tablet
422
    // create tablet from tablet meta and add it to tablet mgr
423
59
    LOG(INFO) << "begin loading tablet from meta";
424
59
    std::set<int64_t> tablet_ids;
425
59
    std::set<int64_t> failed_tablet_ids;
426
59
    auto load_tablet_func = [this, &tablet_ids, &failed_tablet_ids](
427
59
                                    int64_t tablet_id, int32_t schema_hash,
428
278k
                                    std::string_view value) -> bool {
429
278k
        Status status = _engine.tablet_manager()->load_tablet_from_meta(
430
278k
                this, tablet_id, schema_hash, value, false, false, false, false);
431
278k
        if (!status.ok() && !status.is<TABLE_ALREADY_DELETED_ERROR>() &&
432
278k
            !status.is<ENGINE_INSERT_OLD_TABLET>()) {
433
            // load_tablet_from_meta() may return Status::Error<TABLE_ALREADY_DELETED_ERROR>()
434
            // which means the tablet status is DELETED
435
            // This may happen when the tablet was just deleted before the BE restarted,
436
            // but it has not been cleared from rocksdb. At this time, restarting the BE
437
            // will read the tablet in the DELETE state from rocksdb. These tablets have been
438
            // added to the garbage collection queue and will be automatically deleted afterwards.
439
            // Therefore, we believe that this situation is not a failure.
440
441
            // Besides, load_tablet_from_meta() may return Status::Error<ENGINE_INSERT_OLD_TABLET>()
442
            // when BE is restarting and the older tablet have been added to the
443
            // garbage collection queue but not deleted yet.
444
            // In this case, since the data_dirs are parallel loaded, a later loaded tablet
445
            // may be older than previously loaded one, which should not be acknowledged as a
446
            // failure.
447
0
            LOG(WARNING) << "load tablet from header failed. status:" << status
448
0
                         << ", tablet=" << tablet_id << "." << schema_hash;
449
0
            failed_tablet_ids.insert(tablet_id);
450
278k
        } else {
451
278k
            tablet_ids.insert(tablet_id);
452
278k
        }
453
278k
        return true;
454
278k
    };
455
59
    MonotonicStopWatch tablet_timer;
456
59
    tablet_timer.start();
457
59
    Status load_tablet_status = TabletMetaManager::traverse_headers(_meta, load_tablet_func);
458
59
    tablet_timer.stop();
459
59
    if (!failed_tablet_ids.empty()) {
460
0
        LOG(WARNING) << "load tablets from header failed"
461
0
                     << ", loaded tablet: " << tablet_ids.size()
462
0
                     << ", error tablet: " << failed_tablet_ids.size() << ", path: " << _path;
463
0
        if (!config::ignore_load_tablet_failure) {
464
0
            throw Exception(Status::FatalError(
465
0
                    "load tablets encounter failure. stop BE process. path: {}", _path));
466
0
        }
467
0
    }
468
59
    if (!load_tablet_status) {
469
0
        LOG(WARNING) << "there is failure when loading tablet headers"
470
0
                     << ", loaded tablet: " << tablet_ids.size()
471
0
                     << ", error tablet: " << failed_tablet_ids.size() << ", path: " << _path;
472
59
    } else {
473
59
        LOG(INFO) << "load tablet from meta finished"
474
59
                  << ", loaded tablet: " << tablet_ids.size()
475
59
                  << ", error tablet: " << failed_tablet_ids.size()
476
59
                  << ", cost: " << tablet_timer.elapsed_time_milliseconds()
477
59
                  << " ms, path: " << _path;
478
59
    }
479
480
278k
    for (int64_t tablet_id : tablet_ids) {
481
278k
        TabletSharedPtr tablet = _engine.tablet_manager()->get_tablet(tablet_id);
482
278k
        if (tablet && tablet->set_tablet_schema_into_rowset_meta()) {
483
0
            RETURN_IF_ERROR(TabletMetaManager::save(this, tablet->tablet_id(),
484
0
                                                    tablet->schema_hash(), tablet->tablet_meta()));
485
0
        }
486
278k
    }
487
488
59
    auto load_pending_publish_info_func = [&engine = _engine](int64_t tablet_id,
489
59
                                                              int64_t publish_version,
490
59
                                                              std::string_view info) {
491
0
        PendingPublishInfoPB pending_publish_info_pb;
492
0
        bool parsed =
493
0
                pending_publish_info_pb.ParseFromArray(info.data(), cast_set<int>(info.size()));
494
0
        if (!parsed) {
495
0
            LOG(WARNING) << "parse pending publish info failed, tablet_id: " << tablet_id
496
0
                         << " publish_version: " << publish_version;
497
0
        }
498
0
        engine.add_async_publish_task(pending_publish_info_pb.partition_id(), tablet_id,
499
0
                                      publish_version, pending_publish_info_pb.transaction_id(),
500
0
                                      true, pending_publish_info_pb.commit_tso());
501
0
        return true;
502
0
    };
503
59
    MonotonicStopWatch pending_publish_timer;
504
59
    pending_publish_timer.start();
505
59
    RETURN_IF_ERROR(
506
59
            TabletMetaManager::traverse_pending_publish(_meta, load_pending_publish_info_func));
507
59
    pending_publish_timer.stop();
508
59
    LOG(INFO) << "load pending publish task from meta finished, cost: "
509
59
              << pending_publish_timer.elapsed_time_milliseconds() << " ms, data dir: " << _path;
510
511
59
    int64_t rowset_partition_id_eq_0_num = 0;
512
1.08k
    for (auto rowset_meta : dir_rowset_metas) {
513
1.08k
        if (rowset_meta->partition_id() == 0) {
514
0
            ++rowset_partition_id_eq_0_num;
515
0
        }
516
1.08k
    }
517
59
    if (rowset_partition_id_eq_0_num > config::ignore_invalid_partition_id_rowset_num) {
518
0
        throw Exception(Status::FatalError(
519
0
                "rowset partition id eq 0 is {} bigger than config {}, be exit, plz check be.INFO",
520
0
                rowset_partition_id_eq_0_num, config::ignore_invalid_partition_id_rowset_num));
521
0
    }
522
523
59
    std::map<RowsetId, std::vector<RowsetMetaSharedPtr>> rowset_id_to_row_binlog_metas;
524
59
    int64_t row_binlog_cnt {0};
525
59
    int64_t invalid_row_binlog_cnt {0};
526
59
    auto load_row_binlog_meta_func =
527
59
            [&rowset_id_to_row_binlog_metas, &row_binlog_cnt, &invalid_row_binlog_cnt](
528
59
                    const TabletUid& tablet_uid, const RowsetId& rowset_id,
529
59
                    const RowsetId& row_binlog_rowset_id, const std::string& val) -> bool {
530
2
        RowsetMetaSharedPtr rowset_meta(new RowsetMeta());
531
2
        bool parsed = rowset_meta->init(val);
532
2
        if (!parsed) {
533
0
            LOG(WARNING) << "parse binlog<row> meta string failed, tablet_uid=" << tablet_uid
534
0
                         << ", rowset_id=" << rowset_id
535
0
                         << ", row_binlog_rowset_id=" << row_binlog_rowset_id;
536
0
            ++invalid_row_binlog_cnt;
537
0
            return true;
538
0
        }
539
2
        DCHECK(rowset_meta->is_row_binlog());
540
2
        DCHECK_EQ(rowset_meta->tablet_uid(), tablet_uid);
541
2
        rowset_id_to_row_binlog_metas[rowset_id].emplace_back(std::move(rowset_meta));
542
2
        ++row_binlog_cnt;
543
2
        return true;
544
2
    };
545
546
59
    MonotonicStopWatch rb_timer;
547
59
    rb_timer.start();
548
59
    RETURN_IF_ERROR(RowsetMetaManager::traverse_row_binlog_metas(_meta, load_row_binlog_meta_func));
549
59
    rb_timer.stop();
550
59
    LOG(INFO) << "load binlog<row> meta finished, cost: " << rb_timer.elapsed_time_milliseconds()
551
59
              << " ms, data dir: " << _path << ", count: " << row_binlog_cnt
552
59
              << ", invalid: " << invalid_row_binlog_cnt;
553
554
    // traverse rowset
555
    // 1. add committed rowset to txn map
556
    // 2. add visible rowset to tablet
557
    // ignore any errors when load tablet or rowset, because fe will repair them after report
558
59
    int64_t invalid_rowset_counter = 0;
559
1.08k
    for (auto&& rowset_meta : dir_rowset_metas) {
560
1.08k
        TabletSharedPtr tablet = _engine.tablet_manager()->get_tablet(rowset_meta->tablet_id());
561
        // tablet maybe dropped, but not drop related rowset meta
562
1.08k
        if (tablet == nullptr) {
563
0
            VLOG_NOTICE << "could not find tablet id: " << rowset_meta->tablet_id()
564
0
                        << ", schema hash: " << rowset_meta->tablet_schema_hash()
565
0
                        << ", for rowset: " << rowset_meta->rowset_id() << ", skip this rowset";
566
0
            ++invalid_rowset_counter;
567
0
            continue;
568
0
        }
569
570
1.08k
        if (rowset_meta->partition_id() == 0) {
571
0
            LOG(WARNING) << "skip tablet_id=" << tablet->tablet_id()
572
0
                         << " rowset: " << rowset_meta->rowset_id()
573
0
                         << " txn: " << rowset_meta->txn_id();
574
0
            continue;
575
0
        }
576
577
1.08k
        std::vector<RowsetSharedPtr> attach_rowsets;
578
1.08k
        std::map<RowsetId, RowsetMetaPB> attach_rowset_map;
579
1.08k
        bool invalid_attach_rowset = false;
580
1.08k
        if (auto it = rowset_id_to_row_binlog_metas.find(rowset_meta->rowset_id());
581
1.08k
            it != rowset_id_to_row_binlog_metas.end()) {
582
2
            for (auto& attach_rowset_meta : it->second) {
583
2
                DCHECK_EQ(attach_rowset_meta->rowset_state(), rowset_meta->rowset_state());
584
2
                if (!attach_rowset_meta->tablet_schema()) {
585
0
                    attach_rowset_meta->set_tablet_schema(tablet->row_binlog_tablet_schema());
586
0
                }
587
588
2
                RowsetSharedPtr attach_rowset;
589
2
                Status attach_create_status =
590
2
                        tablet->create_rowset(attach_rowset_meta, &attach_rowset);
591
2
                if (!attach_create_status.ok()) {
592
0
                    LOG(WARNING) << "could not create rowset from binlog<row> rowset meta: "
593
0
                                 << " rowset_id: " << attach_rowset_meta->rowset_id()
594
0
                                 << " rowset_type: " << attach_rowset_meta->rowset_type()
595
0
                                 << " rowset_state: " << attach_rowset_meta->rowset_state();
596
0
                    invalid_attach_rowset = true;
597
0
                    break;
598
0
                }
599
2
                attach_rowset_map.emplace(attach_rowset_meta->rowset_id(),
600
2
                                          attach_rowset_meta->get_rowset_pb());
601
2
                attach_rowsets.emplace_back(std::move(attach_rowset));
602
2
            }
603
2
        }
604
1.08k
        if (invalid_attach_rowset) {
605
0
            continue;
606
0
        }
607
608
1.08k
        RowsetSharedPtr rowset;
609
1.08k
        Status create_status = tablet->create_rowset(rowset_meta, &rowset);
610
1.08k
        if (!create_status) {
611
0
            LOG(WARNING) << "could not create rowset from rowsetmeta: "
612
0
                         << " rowset_id: " << rowset_meta->rowset_id()
613
0
                         << " rowset_type: " << rowset_meta->rowset_type()
614
0
                         << " rowset_state: " << rowset_meta->rowset_state();
615
0
            continue;
616
0
        }
617
618
1.08k
        std::optional<BinlogFormatPB> binlog_format = std::nullopt;
619
1.08k
        const std::map<RowsetId, RowsetMetaPB>* attach_rowset_map_ptr = nullptr;
620
1.08k
        if (!attach_rowset_map.empty()) {
621
2
            binlog_format = BinlogFormatPB::ROW;
622
2
            attach_rowset_map_ptr = &attach_rowset_map;
623
2
        }
624
625
1.08k
        if (rowset_meta->rowset_state() == RowsetStatePB::COMMITTED &&
626
1.08k
            rowset_meta->tablet_uid() == tablet->tablet_uid()) {
627
131
            if (!rowset_meta->tablet_schema()) {
628
0
                rowset_meta->set_tablet_schema(tablet->tablet_schema());
629
0
                RETURN_IF_ERROR(RowsetMetaManager::save(
630
0
                        _meta, rowset_meta->tablet_uid(), rowset_meta->rowset_id(),
631
0
                        rowset_meta->get_rowset_pb(), binlog_format, attach_rowset_map_ptr));
632
0
            }
633
131
            std::vector<RowsetId> rowset_ids {rowset_meta->rowset_id()};
634
131
            for (const auto& attach_rowset : attach_rowsets) {
635
0
                if (attach_rowset != nullptr) {
636
0
                    rowset_ids.emplace_back(attach_rowset->rowset_id());
637
0
                }
638
0
            }
639
131
            Status commit_txn_status = _engine.txn_manager()->commit_txn(
640
131
                    _meta, rowset_meta->partition_id(), rowset_meta->txn_id(),
641
131
                    rowset_meta->tablet_id(), rowset_meta->tablet_uid(), rowset_meta->load_id(),
642
131
                    rowset, _engine.pending_local_rowsets().add(rowset_ids), true, nullptr,
643
131
                    attach_rowsets.empty() ? nullptr : &attach_rowsets);
644
131
            if (commit_txn_status || commit_txn_status.is<PUSH_TRANSACTION_ALREADY_EXIST>()) {
645
131
                LOG(INFO) << "successfully to add committed rowset: " << rowset_meta->rowset_id()
646
131
                          << " to tablet: " << rowset_meta->tablet_id()
647
131
                          << " schema hash: " << rowset_meta->tablet_schema_hash()
648
131
                          << " for txn: " << rowset_meta->txn_id() << ", binlog<row> rowset: "
649
131
                          << (attach_rowsets.empty() ? "0"
650
131
                                                     : attach_rowsets[0]->rowset_id().to_string());
651
652
131
            } else if (commit_txn_status.is<ErrorCode::INTERNAL_ERROR>()) {
653
0
                LOG(WARNING) << "failed to add committed rowset: " << rowset_meta->rowset_id()
654
0
                             << " to tablet: " << rowset_meta->tablet_id()
655
0
                             << " for txn: " << rowset_meta->txn_id()
656
0
                             << " error: " << commit_txn_status << ", binlog<row> rowset: "
657
0
                             << (attach_rowsets.empty()
658
0
                                         ? "0"
659
0
                                         : attach_rowsets[0]->rowset_id().to_string());
660
0
                return commit_txn_status;
661
0
            } else {
662
0
                LOG(WARNING) << "failed to add committed rowset: " << rowset_meta->rowset_id()
663
0
                             << " to tablet: " << rowset_meta->tablet_id()
664
0
                             << " for txn: " << rowset_meta->txn_id()
665
0
                             << " error: " << commit_txn_status << ", binlog<row> rowset: "
666
0
                             << (attach_rowsets.empty()
667
0
                                         ? "0"
668
0
                                         : attach_rowsets[0]->rowset_id().to_string());
669
0
            }
670
958
        } else if (rowset_meta->rowset_state() == RowsetStatePB::VISIBLE &&
671
958
                   rowset_meta->tablet_uid() == tablet->tablet_uid()) {
672
958
            if (!rowset_meta->tablet_schema()) {
673
0
                rowset_meta->set_tablet_schema(tablet->tablet_schema());
674
0
                RETURN_IF_ERROR(RowsetMetaManager::save(
675
0
                        _meta, rowset_meta->tablet_uid(), rowset_meta->rowset_id(),
676
0
                        rowset_meta->get_rowset_pb(), binlog_format, attach_rowset_map_ptr));
677
0
            }
678
958
            DCHECK_LE(attach_rowsets.size(), 1);
679
958
            Status publish_status = tablet->add_rowset(
680
958
                    rowset, attach_rowsets.empty() ? nullptr : attach_rowsets[0]);
681
958
            if (!publish_status && !publish_status.is<PUSH_VERSION_ALREADY_EXIST>()) {
682
0
                LOG(WARNING) << "add visible rowset to tablet failed rowset_id:"
683
0
                             << rowset->rowset_id() << " tablet id: " << rowset_meta->tablet_id()
684
0
                             << " txn id:" << rowset_meta->txn_id()
685
0
                             << " start_version: " << rowset_meta->version().first
686
0
                             << " end_version: " << rowset_meta->version().second
687
0
                             << ", binlog<row> rowset: "
688
0
                             << (attach_rowsets.empty()
689
0
                                         ? "0"
690
0
                                         : attach_rowsets[0]->rowset_id().to_string());
691
0
            }
692
958
        } else {
693
0
            LOG(WARNING) << "find invalid rowset: " << rowset_meta->rowset_id()
694
0
                         << " with tablet id: " << rowset_meta->tablet_id()
695
0
                         << " tablet uid: " << rowset_meta->tablet_uid()
696
0
                         << " schema hash: " << rowset_meta->tablet_schema_hash()
697
0
                         << " txn: " << rowset_meta->txn_id()
698
0
                         << " current valid tablet uid: " << tablet->tablet_uid()
699
0
                         << ", binlog<row> rowset: "
700
0
                         << (attach_rowsets.empty() ? "0"
701
0
                                                    : attach_rowsets[0]->rowset_id().to_string());
702
0
            ++invalid_rowset_counter;
703
0
        }
704
1.08k
    }
705
706
59
    int64_t dbm_cnt {0};
707
59
    int64_t unknown_dbm_cnt {0};
708
59
    auto load_delete_bitmap_func = [this, &dbm_cnt, &unknown_dbm_cnt](int64_t tablet_id,
709
59
                                                                      int64_t version,
710
59
                                                                      std::string_view val) {
711
26
        TabletSharedPtr tablet = _engine.tablet_manager()->get_tablet(tablet_id);
712
26
        if (!tablet) {
713
0
            return true;
714
0
        }
715
26
        const auto& all_data_rowsets = tablet->tablet_meta()->all_rs_metas();
716
26
        const auto& all_row_binlogs = tablet->tablet_meta()->all_row_binlog_rs_metas();
717
26
        RowsetIdUnorderedSet rowset_ids;
718
102
        for (const auto& [_, rowset_meta] : all_data_rowsets) {
719
102
            rowset_ids.insert(rowset_meta->rowset_id());
720
102
        }
721
26
        for (auto& [_, rowset_meta] : all_row_binlogs) {
722
12
            rowset_ids.insert(rowset_meta->rowset_id());
723
12
        }
724
725
26
        DeleteBitmapPB delete_bitmap_pb;
726
26
        delete_bitmap_pb.ParseFromArray(val.data(), cast_set<int>(val.size()));
727
26
        int rst_ids_size = delete_bitmap_pb.rowset_ids_size();
728
26
        int seg_ids_size = delete_bitmap_pb.segment_ids_size();
729
26
        int seg_maps_size = delete_bitmap_pb.segment_delete_bitmaps_size();
730
26
        int binlog_mark_size = delete_bitmap_pb.is_binlog_delvec_size();
731
26
        CHECK(rst_ids_size == seg_ids_size && seg_ids_size == seg_maps_size);
732
26
        CHECK(binlog_mark_size == 0 || binlog_mark_size == rst_ids_size);
733
734
90
        for (int i = 0; i < rst_ids_size; ++i) {
735
64
            RowsetId rst_id;
736
64
            rst_id.init(delete_bitmap_pb.rowset_ids(i));
737
            // only process the rowset in _rs_metas and _row_binlog_rs_metas
738
64
            if (rowset_ids.find(rst_id) == rowset_ids.end()) {
739
0
                ++unknown_dbm_cnt;
740
0
                continue;
741
0
            }
742
64
            ++dbm_cnt;
743
64
            auto seg_id = delete_bitmap_pb.segment_ids(i);
744
64
            auto iter = tablet->tablet_meta()->delete_bitmap().delete_bitmap.find(
745
64
                    {rst_id, seg_id, version});
746
            // This version of delete bitmap already exists
747
64
            if (iter != tablet->tablet_meta()->delete_bitmap().delete_bitmap.end()) {
748
0
                continue;
749
0
            }
750
64
            auto bitmap = delete_bitmap_pb.segment_delete_bitmaps(i).data();
751
752
64
            bool from_binlog = delete_bitmap_pb.is_binlog_delvec_size() > 0
753
64
                                       ? delete_bitmap_pb.is_binlog_delvec(i)
754
64
                                       : false;
755
64
            if (!from_binlog) {
756
63
                tablet->tablet_meta()->delete_bitmap().delete_bitmap[{rst_id, seg_id, version}] =
757
63
                        roaring::Roaring::read(bitmap);
758
63
            } else {
759
1
                tablet->tablet_meta()->binlog_delvec().delete_bitmap[{rst_id, seg_id, version}] =
760
1
                        roaring::Roaring::read(bitmap);
761
1
            }
762
64
            VLOG_ROW << "successfully to add delete_bitmap, tablet_id=" << tablet->tablet_id()
763
0
                     << ", rowset_id=" << rst_id << ", seg_id=" << seg_id << ", version=" << version
764
0
                     << ", from_binlog=" << from_binlog;
765
64
        }
766
26
        return true;
767
26
    };
768
59
    MonotonicStopWatch dbm_timer;
769
59
    dbm_timer.start();
770
59
    RETURN_IF_ERROR(TabletMetaManager::traverse_delete_bitmap(_meta, load_delete_bitmap_func));
771
59
    dbm_timer.stop();
772
773
59
    LOG(INFO) << "load delete bitmap from meta finished, cost: "
774
59
              << dbm_timer.elapsed_time_milliseconds() << " ms, data dir: " << _path;
775
776
    // At startup, we only count these invalid rowset, but do not actually delete it.
777
    // The actual delete operation is in StorageEngine::_clean_unused_rowset_metas,
778
    // which is cleaned up uniformly by the background cleanup thread.
779
59
    LOG(INFO) << "finish to load tablets from " << _path
780
59
              << ", total rowset meta: " << dir_rowset_metas.size()
781
59
              << ", invalid rowset num: " << invalid_rowset_counter
782
59
              << ", visible/stale rowsets' delete bitmap count: " << dbm_cnt
783
59
              << ", invalid rowsets' delete bitmap count: " << unknown_dbm_cnt;
784
785
59
    return Status::OK();
786
59
}
787
788
// gc unused local tablet dir
789
287k
void DataDir::_perform_tablet_gc(const std::string& tablet_schema_hash_path, int16_t shard_id) {
790
287k
    if (_stop_bg_worker) {
791
0
        return;
792
0
    }
793
794
287k
    TTabletId tablet_id = -1;
795
287k
    TSchemaHash schema_hash = -1;
796
287k
    bool is_valid = TabletManager::get_tablet_id_and_schema_hash_from_path(
797
287k
            tablet_schema_hash_path, &tablet_id, &schema_hash);
798
287k
    if (!is_valid || tablet_id < 1 || schema_hash < 1) [[unlikely]] {
799
0
        LOG(WARNING) << "[path gc] unknown path: " << tablet_schema_hash_path;
800
0
        return;
801
0
    }
802
803
287k
    auto tablet = _engine.tablet_manager()->get_tablet(tablet_id);
804
287k
    if (!tablet || tablet->data_dir() != this) {
805
726
        if (tablet) {
806
0
            LOG(INFO) << "The tablet in path " << tablet_schema_hash_path
807
0
                      << " is not same with the running one: " << tablet->tablet_path()
808
0
                      << ", might be the old tablet after migration, try to move it to trash";
809
0
        }
810
726
        _engine.tablet_manager()->try_delete_unused_tablet_path(this, tablet_id, schema_hash,
811
726
                                                                tablet_schema_hash_path, shard_id);
812
726
        return;
813
726
    }
814
815
286k
    _perform_rowset_gc(tablet_schema_hash_path);
816
286k
}
817
818
// gc unused local rowsets under tablet dir
819
286k
void DataDir::_perform_rowset_gc(const std::string& tablet_schema_hash_path) {
820
286k
    if (_stop_bg_worker) {
821
0
        return;
822
0
    }
823
824
286k
    TTabletId tablet_id = -1;
825
286k
    TSchemaHash schema_hash = -1;
826
286k
    bool is_valid = doris::TabletManager::get_tablet_id_and_schema_hash_from_path(
827
286k
            tablet_schema_hash_path, &tablet_id, &schema_hash);
828
286k
    if (!is_valid || tablet_id < 1 || schema_hash < 1) [[unlikely]] {
829
0
        LOG(WARNING) << "[path gc] unknown path: " << tablet_schema_hash_path;
830
0
        return;
831
0
    }
832
833
286k
    auto tablet = _engine.tablet_manager()->get_tablet(tablet_id);
834
286k
    if (!tablet) {
835
        // Could not found the tablet, maybe it's a dropped tablet, will be reclaimed
836
        // in the next time `_perform_path_gc_by_tablet`
837
0
        return;
838
0
    }
839
840
286k
    if (tablet->data_dir() != this) {
841
        // Current running tablet is not in same data_dir, maybe it's a tablet after migration,
842
        // will be reclaimed in the next time `_perform_path_gc_by_tablet`
843
0
        return;
844
0
    }
845
846
286k
    bool exists;
847
286k
    std::vector<io::FileInfo> files;
848
286k
    auto st = io::global_local_filesystem()->list(tablet_schema_hash_path, true, &files, &exists);
849
286k
    if (!st.ok()) [[unlikely]] {
850
0
        LOG(WARNING) << "[path gc] fail to list tablet path " << tablet_schema_hash_path << " : "
851
0
                     << st;
852
0
        return;
853
0
    }
854
855
    // Rowset files excluding pending rowsets
856
286k
    std::vector<std::pair<RowsetId, std::string /* filename */>> rowsets_not_pending;
857
286k
    for (auto&& file : files) {
858
81.6k
        auto rowset_id = extract_rowset_id(file.file_name);
859
81.6k
        if (rowset_id.hi == 0) {
860
356
            continue; // Not a rowset
861
356
        }
862
863
81.2k
        if (_engine.pending_local_rowsets().contains(rowset_id)) {
864
122
            continue; // Pending rowset file
865
122
        }
866
867
81.1k
        rowsets_not_pending.emplace_back(rowset_id, std::move(file.file_name));
868
81.1k
    }
869
870
286k
    RowsetIdUnorderedSet rowsets_in_version_map;
871
286k
    tablet->traverse_rowsets(
872
505k
            [&rowsets_in_version_map](auto& rs) { rowsets_in_version_map.insert(rs->rowset_id()); },
873
286k
            true);
874
286k
    for (const auto& [_, rb_meta] : tablet->tablet_meta()->all_row_binlog_rs_metas()) {
875
67
        rowsets_in_version_map.insert(rb_meta->rowset_id());
876
67
    }
877
878
286k
    DBUG_EXECUTE_IF("DataDir::_perform_rowset_gc.simulation.slow", {
879
286k
        auto target_tablet_id = dp->param<int64_t>("tablet_id", -1);
880
286k
        if (target_tablet_id == tablet_id) {
881
286k
            LOG(INFO) << "debug point wait tablet to remove rsmgr tabletId=" << tablet_id;
882
286k
            DBUG_BLOCK;
883
286k
        }
884
286k
    });
885
886
286k
    auto reclaim_rowset_file = [](const std::string& path) {
887
182
        auto st = io::global_local_filesystem()->delete_file(path);
888
182
        if (!st.ok()) [[unlikely]] {
889
0
            LOG(WARNING) << "[path gc] failed to delete garbage rowset file: " << st;
890
0
            return;
891
0
        }
892
182
        LOG(INFO) << "[path gc] delete garbage path: " << path; // Audit log
893
182
    };
894
895
286k
    auto should_reclaim = [&, this](const RowsetId& rowset_id) {
896
74.6k
        return !rowsets_in_version_map.contains(rowset_id) &&
897
74.6k
               !_engine.check_rowset_id_in_unused_rowsets(rowset_id) &&
898
74.6k
               RowsetMetaManager::exists(get_meta(), tablet->tablet_uid(), rowset_id)
899
215
                       .is<META_KEY_NOT_FOUND>() &&
900
74.6k
               !RowsetMetaManager::row_binlog_meta_exists(get_meta(), tablet->tablet_uid(),
901
182
                                                          rowset_id);
902
74.6k
    };
903
904
    // rowset_id -> is_garbage
905
286k
    std::unordered_map<RowsetId, bool> checked_rowsets;
906
286k
    for (auto&& [rowset_id, filename] : rowsets_not_pending) {
907
81.1k
        if (_stop_bg_worker) {
908
0
            return;
909
0
        }
910
911
81.1k
        if (auto it = checked_rowsets.find(rowset_id); it != checked_rowsets.end()) {
912
6.35k
            if (it->second) { // Is checked garbage rowset
913
60
                reclaim_rowset_file(tablet_schema_hash_path + '/' + filename);
914
60
            }
915
6.35k
            continue;
916
6.35k
        }
917
918
74.7k
        if (should_reclaim(rowset_id)) {
919
202
            if (config::path_gc_check_step > 0 &&
920
202
                ++_path_gc_step % config::path_gc_check_step == 0) {
921
0
                std::this_thread::sleep_for(
922
0
                        std::chrono::milliseconds(config::path_gc_check_step_interval_ms));
923
0
            }
924
202
            reclaim_rowset_file(tablet_schema_hash_path + '/' + filename);
925
202
            checked_rowsets.emplace(rowset_id, true);
926
74.5k
        } else {
927
74.5k
            checked_rowsets.emplace(rowset_id, false);
928
74.5k
        }
929
74.7k
    }
930
286k
}
931
932
15
void DataDir::perform_path_gc() {
933
15
    if (_stop_bg_worker) {
934
0
        return;
935
0
    }
936
937
15
    LOG(INFO) << "start to gc data dir " << _path;
938
15
    auto data_path = fmt::format("{}/{}", _path, DATA_PREFIX);
939
15
    std::vector<io::FileInfo> shards;
940
15
    bool exists = true;
941
15
    const auto& fs = io::global_local_filesystem();
942
15
    auto st = fs->list(data_path, false, &shards, &exists);
943
15
    if (!st.ok()) [[unlikely]] {
944
0
        LOG(WARNING) << "failed to scan data dir: " << st;
945
0
        return;
946
0
    }
947
948
14.3k
    for (const auto& shard : shards) {
949
14.3k
        if (_stop_bg_worker) {
950
0
            break;
951
0
        }
952
953
14.3k
        if (shard.is_file) {
954
0
            continue;
955
0
        }
956
957
14.3k
        auto shard_path = fmt::format("{}/{}", data_path, shard.file_name);
958
14.3k
        std::vector<io::FileInfo> tablet_ids;
959
14.3k
        st = io::global_local_filesystem()->list(shard_path, false, &tablet_ids, &exists);
960
14.3k
        if (!st.ok()) [[unlikely]] {
961
0
            LOG(WARNING) << "fail to walk dir, shard_path=" << shard_path << " : " << st;
962
0
            continue;
963
0
        }
964
965
287k
        for (const auto& tablet_id : tablet_ids) {
966
287k
            if (_stop_bg_worker) {
967
0
                break;
968
0
            }
969
970
287k
            if (tablet_id.is_file) {
971
0
                continue;
972
0
            }
973
974
287k
            auto tablet_id_path = fmt::format("{}/{}", shard_path, tablet_id.file_name);
975
287k
            std::vector<io::FileInfo> schema_hashes;
976
287k
            st = fs->list(tablet_id_path, false, &schema_hashes, &exists);
977
287k
            if (!st.ok()) [[unlikely]] {
978
0
                LOG(WARNING) << "fail to walk dir, tablet_id_path=" << tablet_id_path << " : "
979
0
                             << st;
980
0
                continue;
981
0
            }
982
983
287k
            for (auto&& schema_hash : schema_hashes) {
984
287k
                if (schema_hash.is_file) {
985
0
                    continue;
986
0
                }
987
988
287k
                if (config::path_gc_check_step > 0 &&
989
287k
                    ++_path_gc_step % config::path_gc_check_step == 0) {
990
285
                    std::this_thread::sleep_for(
991
285
                            std::chrono::milliseconds(config::path_gc_check_step_interval_ms));
992
285
                }
993
287k
                int16_t shard_id = -1;
994
287k
                try {
995
287k
                    shard_id = cast_set<int16_t>(std::stoi(shard.file_name));
996
287k
                } catch (const std::exception&) {
997
0
                    LOG(WARNING) << "failed to stoi shard_id, shard name=" << shard.file_name;
998
0
                    continue;
999
0
                }
1000
287k
                _perform_tablet_gc(tablet_id_path + '/' + schema_hash.file_name, shard_id);
1001
287k
            }
1002
287k
        }
1003
14.3k
    }
1004
1005
15
    LOG(INFO) << "gc data dir path: " << _path << " finished";
1006
15
}
1007
1008
858
Status DataDir::update_capacity() {
1009
858
    RETURN_IF_ERROR(io::global_local_filesystem()->get_space_info(_path, &_disk_capacity_bytes,
1010
858
                                                                  &_available_bytes));
1011
847
    disks_total_capacity->set_value(_disk_capacity_bytes);
1012
847
    disks_avail_capacity->set_value(_available_bytes);
1013
847
    LOG(INFO) << "path: " << _path << " total capacity: " << _disk_capacity_bytes
1014
847
              << ", available capacity: " << _available_bytes << ", usage: " << get_usage(0)
1015
847
              << ", in_use: " << is_used();
1016
1017
847
    return Status::OK();
1018
858
}
1019
1020
94
void DataDir::update_trash_capacity() {
1021
94
    auto trash_path = fmt::format("{}/{}", _path, TRASH_PREFIX);
1022
94
    try {
1023
94
        _trash_used_bytes = _engine.get_file_or_directory_size(trash_path);
1024
94
        _trash_file_num = 0;
1025
94
        if (std::filesystem::exists(trash_path)) {
1026
7
            for (const auto& entry : std::filesystem::directory_iterator(trash_path)) {
1027
7
                if (entry.is_directory()) {
1028
6
                    _trash_file_num++;
1029
6
                }
1030
7
            }
1031
5
        }
1032
94
    } catch (const std::filesystem::filesystem_error& e) {
1033
0
        LOG(WARNING) << "update trash capacity failed, path: " << _path << ", err: " << e.what();
1034
0
        return;
1035
0
    }
1036
94
    disks_trash_used_capacity->set_value(_trash_used_bytes);
1037
94
    LOG(INFO) << "path: " << _path << " trash capacity: " << _trash_used_bytes
1038
94
              << ", trash file num: " << _trash_file_num;
1039
94
}
1040
1041
589
void DataDir::update_local_data_size(int64_t size) {
1042
589
    disks_local_used_capacity->set_value(size);
1043
589
}
1044
1045
589
void DataDir::update_remote_data_size(int64_t size) {
1046
589
    disks_remote_used_capacity->set_value(size);
1047
589
}
1048
1049
0
size_t DataDir::tablet_size() const {
1050
0
    std::lock_guard<std::mutex> l(_mutex);
1051
0
    return _tablet_set.size();
1052
0
}
1053
1054
258k
bool DataDir::reach_capacity_limit(int64_t incoming_data_size) {
1055
258k
    double used_pct = get_usage(incoming_data_size);
1056
258k
    int64_t left_bytes = _available_bytes - incoming_data_size;
1057
258k
    if (used_pct >= config::storage_flood_stage_usage_percent / 100.0 &&
1058
258k
        left_bytes <= config::storage_flood_stage_left_capacity_bytes) {
1059
0
        LOG(WARNING) << "reach capacity limit. used pct: " << used_pct
1060
0
                     << ", left bytes: " << left_bytes << ", path: " << _path;
1061
0
        return true;
1062
0
    }
1063
258k
    return false;
1064
258k
}
1065
1066
8.38k
void DataDir::disks_compaction_score_increment(int64_t delta) {
1067
8.38k
    disks_compaction_score->increment(delta);
1068
8.38k
}
1069
1070
8.39k
void DataDir::disks_compaction_num_increment(int64_t delta) {
1071
8.39k
    disks_compaction_num->increment(delta);
1072
8.39k
}
1073
1074
5.74k
Status DataDir::move_to_trash(const std::string& tablet_path) {
1075
5.74k
    if (config::trash_file_expire_time_sec <= 0) {
1076
5.74k
        LOG(INFO) << "delete tablet dir " << tablet_path
1077
5.74k
                  << " directly due to trash_file_expire_time_sec is 0";
1078
5.74k
        RETURN_IF_ERROR(io::global_local_filesystem()->delete_directory(tablet_path));
1079
5.74k
        return delete_tablet_parent_path_if_empty(tablet_path);
1080
5.74k
    }
1081
1082
0
    Status res = Status::OK();
1083
    // 1. get timestamp string
1084
0
    std::string time_str;
1085
0
    if ((res = gen_timestamp_string(&time_str)) != Status::OK()) {
1086
0
        LOG(WARNING) << "failed to generate time_string when move file to trash.err code=" << res;
1087
0
        return res;
1088
0
    }
1089
1090
    // 2. generate new file path
1091
    // a global counter to avoid file name duplication.
1092
0
    static std::atomic<uint64_t> delete_counter(0);
1093
0
    auto trash_root_path =
1094
0
            fmt::format("{}/{}/{}.{}", _path, TRASH_PREFIX, time_str, delete_counter++);
1095
0
    auto fs_tablet_path = io::Path(tablet_path);
1096
0
    auto trash_tablet_path = trash_root_path /
1097
0
                             fs_tablet_path.parent_path().filename() /* tablet_id */ /
1098
0
                             fs_tablet_path.filename() /* schema_hash */;
1099
1100
    // 3. create target dir, or the rename() function will fail.
1101
0
    auto trash_tablet_parent = trash_tablet_path.parent_path();
1102
    // create dir if not exists
1103
0
    bool exists = true;
1104
0
    RETURN_IF_ERROR(io::global_local_filesystem()->exists(trash_tablet_parent, &exists));
1105
0
    if (!exists) {
1106
0
        RETURN_IF_ERROR(io::global_local_filesystem()->create_directory(trash_tablet_parent));
1107
0
    }
1108
1109
    // 4. move tablet to trash
1110
0
    VLOG_NOTICE << "move file to trash. " << tablet_path << " -> " << trash_tablet_path;
1111
0
    if (rename(tablet_path.c_str(), trash_tablet_path.c_str()) < 0) {
1112
0
        return Status::Error<OS_ERROR>("move file to trash failed. file={}, target={}, err={}",
1113
0
                                       tablet_path, trash_tablet_path.native(), Errno::str());
1114
0
    }
1115
1116
    // 5. check parent dir of source file, delete it when empty
1117
0
    RETURN_IF_ERROR(delete_tablet_parent_path_if_empty(tablet_path));
1118
1119
0
    return Status::OK();
1120
0
}
1121
1122
5.74k
Status DataDir::delete_tablet_parent_path_if_empty(const std::string& tablet_path) {
1123
5.74k
    auto fs_tablet_path = io::Path(tablet_path);
1124
5.74k
    std::string source_parent_dir = fs_tablet_path.parent_path(); // tablet_id level
1125
5.74k
    std::vector<io::FileInfo> sub_files;
1126
5.74k
    bool exists = true;
1127
5.74k
    RETURN_IF_ERROR(
1128
5.74k
            io::global_local_filesystem()->list(source_parent_dir, false, &sub_files, &exists));
1129
5.74k
    if (sub_files.empty()) {
1130
5.74k
        LOG(INFO) << "remove empty dir " << source_parent_dir;
1131
        // no need to exam return status
1132
5.74k
        RETURN_IF_ERROR(io::global_local_filesystem()->delete_directory(source_parent_dir));
1133
5.74k
    }
1134
5.74k
    return Status::OK();
1135
5.74k
}
1136
1137
88
void DataDir::perform_remote_rowset_gc() {
1138
88
    std::vector<std::pair<std::string, std::string>> gc_kvs;
1139
88
    auto traverse_remote_rowset_func = [&gc_kvs](std::string_view key,
1140
88
                                                 std::string_view value) -> bool {
1141
0
        gc_kvs.emplace_back(key, value);
1142
0
        return true;
1143
0
    };
1144
88
    static_cast<void>(_meta->iterate(META_COLUMN_FAMILY_INDEX, REMOTE_ROWSET_GC_PREFIX,
1145
88
                                     traverse_remote_rowset_func));
1146
88
    std::vector<std::string> deleted_keys;
1147
88
    for (auto& [key, val] : gc_kvs) {
1148
0
        auto rowset_id = key.substr(REMOTE_ROWSET_GC_PREFIX.size());
1149
0
        RemoteRowsetGcPB gc_pb;
1150
0
        if (!gc_pb.ParseFromString(val)) {
1151
0
            LOG(WARNING) << "malformed RemoteRowsetGcPB. rowset_id=" << rowset_id;
1152
0
            deleted_keys.push_back(std::move(key));
1153
0
            continue;
1154
0
        }
1155
1156
0
        auto storage_resource = get_storage_resource(gc_pb.resource_id());
1157
0
        if (!storage_resource) {
1158
0
            LOG(WARNING) << "Cannot get file system: " << gc_pb.resource_id();
1159
0
            continue;
1160
0
        }
1161
1162
0
        std::vector<io::Path> seg_paths;
1163
0
        seg_paths.reserve(gc_pb.num_segments());
1164
0
        for (int i = 0; i < gc_pb.num_segments(); ++i) {
1165
0
            seg_paths.emplace_back(
1166
0
                    storage_resource->first.remote_segment_path(gc_pb.tablet_id(), rowset_id, i));
1167
0
        }
1168
1169
0
        auto& fs = storage_resource->first.fs;
1170
0
        LOG(INFO) << "delete remote rowset. root_path=" << fs->root_path()
1171
0
                  << ", rowset_id=" << rowset_id;
1172
0
        auto st = fs->batch_delete(seg_paths);
1173
0
        if (st.ok()) {
1174
0
            deleted_keys.push_back(std::move(key));
1175
0
            unused_remote_rowset_num << -1;
1176
0
        } else {
1177
0
            LOG(WARNING) << "failed to delete remote rowset. err=" << st;
1178
0
        }
1179
0
    }
1180
88
    for (const auto& key : deleted_keys) {
1181
0
        static_cast<void>(_meta->remove(META_COLUMN_FAMILY_INDEX, key));
1182
0
    }
1183
88
}
1184
1185
88
void DataDir::perform_remote_tablet_gc() {
1186
88
    std::vector<std::pair<std::string, std::string>> tablet_gc_kvs;
1187
88
    auto traverse_remote_tablet_func = [&tablet_gc_kvs](std::string_view key,
1188
88
                                                        std::string_view value) -> bool {
1189
0
        tablet_gc_kvs.emplace_back(key, value);
1190
0
        return true;
1191
0
    };
1192
88
    static_cast<void>(_meta->iterate(META_COLUMN_FAMILY_INDEX, REMOTE_TABLET_GC_PREFIX,
1193
88
                                     traverse_remote_tablet_func));
1194
88
    std::vector<std::string> deleted_keys;
1195
88
    for (auto& [key, val] : tablet_gc_kvs) {
1196
0
        auto tablet_id = key.substr(REMOTE_TABLET_GC_PREFIX.size());
1197
0
        RemoteTabletGcPB gc_pb;
1198
0
        if (!gc_pb.ParseFromString(val)) {
1199
0
            LOG(WARNING) << "malformed RemoteTabletGcPB. tablet_id=" << tablet_id;
1200
0
            deleted_keys.push_back(std::move(key));
1201
0
            continue;
1202
0
        }
1203
0
        bool success = true;
1204
0
        for (auto& resource_id : gc_pb.resource_ids()) {
1205
0
            auto fs = get_filesystem(resource_id);
1206
0
            if (!fs) {
1207
0
                LOG(WARNING) << "could not get file system. resource_id=" << resource_id;
1208
0
                success = false;
1209
0
                continue;
1210
0
            }
1211
0
            LOG(INFO) << "delete remote rowsets of tablet. root_path=" << fs->root_path()
1212
0
                      << ", tablet_id=" << tablet_id;
1213
0
            auto st = fs->delete_directory(DATA_PREFIX + '/' + tablet_id);
1214
0
            if (!st.ok()) {
1215
0
                LOG(WARNING) << "failed to delete all remote rowset in tablet. err=" << st;
1216
0
                success = false;
1217
0
            }
1218
0
        }
1219
0
        if (success) {
1220
0
            deleted_keys.push_back(std::move(key));
1221
0
        }
1222
0
    }
1223
88
    for (const auto& key : deleted_keys) {
1224
0
        static_cast<void>(_meta->remove(META_COLUMN_FAMILY_INDEX, key));
1225
0
    }
1226
88
}
1227
} // namespace doris