Coverage Report

Created: 2026-03-16 19:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/io/cache/cache_lru_dumper.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 "io/cache/cache_lru_dumper.h"
19
20
#include <crc32c/crc32c.h>
21
22
#include "exec/common/endian.h"
23
#include "io/cache/block_file_cache.h"
24
#include "io/cache/lru_queue_recorder.h"
25
#include "util/coding.h"
26
27
namespace doris::io {
28
29
14
std::string CacheLRUDumper::Footer::serialize_as_string() const {
30
14
    std::string result;
31
14
    result.reserve(sizeof(Footer));
32
33
    // Serialize meta_offset (convert to little-endian)
34
14
    uint64_t meta_offset_le;
35
14
    encode_fixed64_le(reinterpret_cast<uint8_t*>(&meta_offset_le), meta_offset);
36
14
    result.append(reinterpret_cast<const char*>(&meta_offset_le), sizeof(meta_offset_le));
37
38
    // Serialize checksum (convert to little-endian)
39
14
    uint32_t checksum_le;
40
14
    encode_fixed32_le(reinterpret_cast<uint8_t*>(&checksum_le), checksum);
41
14
    result.append(reinterpret_cast<const char*>(&checksum_le), sizeof(checksum_le));
42
43
14
    result.append(reinterpret_cast<const char*>(&version), sizeof(version));
44
45
    // Serialize magic
46
14
    result.append(magic, sizeof(magic));
47
48
14
    return result;
49
14
}
50
51
12
bool CacheLRUDumper::Footer::deserialize_from_string(const std::string& data) {
52
12
    DCHECK(data.size() == sizeof(Footer));
53
54
12
    const char* ptr = data.data();
55
56
    // Deserialize meta_offset (convert from little-endian)
57
12
    uint64_t meta_offset_le;
58
12
    std::memcpy(&meta_offset_le, ptr, sizeof(meta_offset_le));
59
12
    meta_offset = decode_fixed64_le(reinterpret_cast<uint8_t*>(&meta_offset_le));
60
12
    ptr += sizeof(meta_offset_le);
61
62
    // Deserialize checksum (convert from little-endian)
63
12
    uint32_t checksum_le;
64
12
    std::memcpy(&checksum_le, ptr, sizeof(checksum_le));
65
12
    checksum = decode_fixed32_le(reinterpret_cast<uint8_t*>(&checksum_le));
66
12
    ptr += sizeof(checksum_le);
67
68
12
    version = *((uint8_t*)ptr);
69
12
    ptr += sizeof(version);
70
71
    // Deserialize magic
72
12
    std::memcpy(magic, ptr, sizeof(magic));
73
74
12
    return true;
75
12
}
76
77
39
Status CacheLRUDumper::check_ofstream_status(std::ofstream& out, std::string& filename) {
78
39
    if (!out.good()) {
79
0
        std::ios::iostate state = out.rdstate();
80
0
        std::stringstream err_msg;
81
0
        if (state & std::ios::eofbit) {
82
0
            err_msg << "End of file reached.";
83
0
        }
84
0
        if (state & std::ios::failbit) {
85
0
            err_msg << "Input/output operation failed, err_code: " << strerror(errno);
86
0
        }
87
0
        if (state & std::ios::badbit) {
88
0
            err_msg << "Serious I/O error occurred, err_code: " << strerror(errno);
89
0
        }
90
0
        out.close();
91
0
        std::string warn_msg = fmt::format("dump lru writing failed, file={}, {}", filename,
92
0
                                           err_msg.str().c_str());
93
0
        LOG(WARNING) << warn_msg;
94
0
        return Status::InternalError<false>(warn_msg);
95
0
    }
96
97
39
    return Status::OK();
98
39
}
99
100
33
Status CacheLRUDumper::check_ifstream_status(std::ifstream& in, std::string& filename) {
101
33
    if (!in.good()) {
102
0
        std::ios::iostate state = in.rdstate();
103
0
        std::stringstream err_msg;
104
0
        if (state & std::ios::eofbit) {
105
0
            err_msg << "End of file reached.";
106
0
        }
107
0
        if (state & std::ios::failbit) {
108
0
            err_msg << "Input/output operation failed, err_code: " << strerror(errno);
109
0
        }
110
0
        if (state & std::ios::badbit) {
111
0
            err_msg << "Serious I/O error occurred, err_code: " << strerror(errno);
112
0
        }
113
0
        in.close();
114
0
        std::string warn_msg = std::string(
115
0
                fmt::format("dump lru reading failed, file={}, {}", filename, err_msg.str()));
116
0
        LOG(WARNING) << warn_msg;
117
0
        return Status::InternalError<false>(warn_msg);
118
0
    }
119
120
33
    return Status::OK();
121
33
}
122
123
Status CacheLRUDumper::dump_one_lru_entry(std::ofstream& out, std::string& filename,
124
34
                                          const UInt128Wrapper& hash, size_t offset, size_t size) {
125
    // Dump file format description:
126
    // +-----------------------------------------------+
127
    // | LRUDumpEntryGroupPb_1                         |
128
    // +-----------------------------------------------+
129
    // | LRUDumpEntryGroupPb_2                         |
130
    // +-----------------------------------------------+
131
    // | LRUDumpEntryGroupPb_3                         |
132
    // +-----------------------------------------------+
133
    // | ...                                           |
134
    // +-----------------------------------------------+
135
    // | LRUDumpEntryGroupPb_n                         |
136
    // +-----------------------------------------------+
137
    // | LRUDumpMetaPb (List<offset,size,crc>)         |
138
    // +-----------------------------------------------+
139
    // | FOOTER_OFFSET (8Bytes)                        |
140
    // +-----------------------------------------------+
141
    // | CHECKSUM (4Bytes)|VERSION (1Byte)|MAGIC (3B)|
142
    // +-----------------------------------------------+
143
    //
144
    // why we are not using protobuf as a whole?
145
    // AFAIK, current protobuf version dose not support streaming mode,
146
    // so that we need to store all the message in memory which will
147
    // consume loads of RAMs.
148
    // Instead, we use protobuf serialize each of the single entry
149
    // and provide the version field in the footer for upgrade
150
151
34
    ::doris::io::cache::LRUDumpEntryPb* entry = _current_dump_group.add_entries();
152
34
    ::doris::io::cache::UInt128WrapperPb* hash_pb = entry->mutable_hash();
153
34
    hash_pb->set_high(hash.high());
154
34
    hash_pb->set_low(hash.low());
155
34
    entry->set_offset(offset);
156
34
    entry->set_size(size);
157
158
34
    _current_dump_group_count++;
159
34
    if (_current_dump_group_count >= 10000) {
160
0
        RETURN_IF_ERROR(flush_current_group(out, filename));
161
0
    }
162
34
    return Status::OK();
163
34
}
164
165
11
Status CacheLRUDumper::flush_current_group(std::ofstream& out, std::string& filename) {
166
11
    if (_current_dump_group_count == 0) {
167
0
        return Status::OK();
168
0
    }
169
170
    // Record current position as group start offset
171
11
    size_t group_start = out.tellp();
172
173
    // Serialize and write the group
174
11
    std::string serialized;
175
11
    VLOG_DEBUG << "Serialized size: " << serialized.size()
176
0
               << " Before serialization: " << _current_dump_group.DebugString();
177
11
    if (!_current_dump_group.SerializeToString(&serialized)) {
178
0
        std::string warn_msg = fmt::format("Failed to serialize LRUDumpEntryGroupPb");
179
0
        LOG(WARNING) << warn_msg;
180
0
        return Status::InternalError<false>(warn_msg);
181
0
    }
182
183
11
    out.write(serialized.data(), serialized.size());
184
11
    RETURN_IF_ERROR(check_ofstream_status(out, filename));
185
186
    // Record group metadata
187
11
    ::doris::io::cache::EntryGroupOffsetSizePb* group_info = _dump_meta.add_group_offset_size();
188
11
    group_info->set_offset(group_start);
189
11
    group_info->set_size(serialized.size());
190
11
    uint32_t checksum = crc32c::Crc32c(serialized.data(), serialized.size());
191
11
    group_info->set_checksum(checksum);
192
193
    // Reset for next group
194
11
    _current_dump_group.Clear();
195
11
    _current_dump_group_count = 0;
196
11
    return Status::OK();
197
11
}
198
199
Status CacheLRUDumper::finalize_dump(std::ofstream& out, size_t entry_num,
200
                                     std::string& tmp_filename, std::string& final_filename,
201
14
                                     size_t& file_size) {
202
    // Flush any remaining entries
203
14
    if (_current_dump_group_count > 0) {
204
11
        RETURN_IF_ERROR(flush_current_group(out, tmp_filename));
205
11
    }
206
207
    // Write meta information
208
14
    _dump_meta.set_entry_num(entry_num);
209
14
    size_t meta_offset = out.tellp();
210
14
    LOG(INFO) << "dump meta: " << _dump_meta.DebugString();
211
14
    std::string meta_serialized;
212
14
    if (!_dump_meta.SerializeToString(&meta_serialized)) {
213
0
        std::string warn_msg =
214
0
                fmt::format("Failed to serialize LRUDumpMetaPb, file={}", tmp_filename);
215
0
        LOG(WARNING) << warn_msg;
216
0
        return Status::InternalError<false>(warn_msg);
217
0
    }
218
14
    out.write(meta_serialized.data(), meta_serialized.size());
219
14
    RETURN_IF_ERROR(check_ofstream_status(out, tmp_filename));
220
221
    // Write footer
222
14
    Footer footer;
223
14
    footer.meta_offset = meta_offset;
224
14
    footer.checksum = 0;
225
14
    footer.version = 1;
226
14
    std::memcpy(footer.magic, "DOR", 3);
227
228
14
    std::string footer_str = footer.serialize_as_string();
229
14
    out.write(footer_str.data(), footer_str.size());
230
14
    RETURN_IF_ERROR(check_ofstream_status(out, tmp_filename));
231
232
14
    out.close();
233
234
14
    if (_is_first_dump) [[unlikely]] {
235
        // we back up two dumps (one for last before be restart, one for first after be restart)
236
        // for later debug the restore process
237
12
        try {
238
12
            if (std::filesystem::exists(final_filename)) {
239
0
                std::string backup_filename = final_filename + "_" + _start_time + "_last";
240
0
                std::rename(final_filename.c_str(), backup_filename.c_str());
241
0
            }
242
12
            std::string timestamped_filename = final_filename + "_" + _start_time;
243
12
            std::filesystem::copy_file(tmp_filename, timestamped_filename);
244
245
12
            std::filesystem::path dir = std::filesystem::path(final_filename).parent_path();
246
12
            std::string prefix = std::filesystem::path(final_filename).filename().string();
247
12
            uint64_t total_size = 0;
248
12
            std::vector<std::pair<std::filesystem::path, std::filesystem::file_time_type>> files;
249
102
            for (const auto& entry : std::filesystem::directory_iterator(dir)) {
250
102
                if (entry.path().filename().string().find(prefix) == 0) {
251
24
                    total_size += entry.file_size();
252
24
                    files.emplace_back(entry.path(), entry.last_write_time());
253
24
                }
254
102
            }
255
12
            if (total_size > 5ULL * 1024 * 1024 * 1024) {
256
                // delete oldest two files
257
0
                std::sort(files.begin(), files.end(),
258
0
                          [](const auto& a, const auto& b) { return a.second < b.second; });
259
0
                if (!files.empty()) {
260
0
                    auto remove_file = [](const std::filesystem::path& file_path) {
261
0
                        std::error_code ec;
262
0
                        bool removed = std::filesystem::remove(file_path, ec);
263
0
                        LOG(INFO) << "Remove " << (removed ? "succeeded" : "failed")
264
0
                                  << " for file: " << file_path
265
0
                                  << (ec ? ", error: " + ec.message() : "");
266
0
                        return removed;
267
0
                    };
268
269
0
                    remove_file(files[0].first);
270
0
                    if (files.size() > 1) {
271
0
                        remove_file(files[1].first);
272
0
                    }
273
0
                }
274
0
            }
275
12
        } catch (const std::filesystem::filesystem_error& e) {
276
0
            LOG(WARNING) << "failed to handle first dump case: " << e.what();
277
0
        }
278
12
    }
279
280
    // Rename tmp to formal file
281
14
    try {
282
14
        std::rename(tmp_filename.c_str(), final_filename.c_str());
283
14
        file_size = std::filesystem::file_size(final_filename);
284
14
    } catch (const std::filesystem::filesystem_error& e) {
285
0
        LOG(WARNING) << "failed to rename " << tmp_filename << " to " << final_filename
286
0
                     << " err: " << e.what();
287
0
    }
288
289
14
    _dump_meta.Clear();
290
14
    _current_dump_group.Clear();
291
14
    _current_dump_group_count = 0;
292
293
14
    return Status::OK();
294
14
}
295
296
6.34k
void CacheLRUDumper::dump_queue(const std::string& queue_name, bool force) {
297
6.34k
    FileCacheType type = string_to_cache_type(queue_name);
298
6.34k
    if (force || _recorder->get_lru_queue_update_cnt_from_last_dump(type) >
299
6.34k
                         config::file_cache_background_lru_dump_update_cnt_threshold) {
300
12
        LRUQueue& queue = _recorder->get_shadow_queue(type);
301
12
        do_dump_queue(queue, queue_name);
302
12
        _recorder->reset_lru_queue_update_cnt_from_last_dump(type);
303
12
    }
304
6.34k
}
305
306
13
void CacheLRUDumper::do_dump_queue(LRUQueue& queue, const std::string& queue_name) {
307
13
    Status st;
308
13
    std::vector<std::tuple<UInt128Wrapper, size_t, size_t>> elements;
309
13
    elements.reserve(config::file_cache_background_lru_dump_tail_record_num);
310
311
13
    {
312
13
        std::lock_guard<std::mutex> lru_log_lock(_recorder->_mutex_lru_log);
313
13
        size_t count = 0;
314
37
        for (const auto& [hash, offset, size] : queue) {
315
37
            if (count++ >= config::file_cache_background_lru_dump_tail_record_num) break;
316
34
            elements.emplace_back(hash, offset, size);
317
34
        }
318
13
    }
319
320
    // Write to disk
321
13
    int64_t duration_ns = 0;
322
13
    std::uintmax_t file_size = 0;
323
13
    {
324
13
        SCOPED_RAW_TIMER(&duration_ns);
325
13
        std::string tmp_filename =
326
13
                fmt::format("{}/lru_dump_{}.tail.tmp", _mgr->_cache_base_path, queue_name);
327
13
        std::string final_filename =
328
13
                fmt::format("{}/lru_dump_{}.tail", _mgr->_cache_base_path, queue_name);
329
13
        std::ofstream out(tmp_filename, std::ios::binary);
330
13
        if (out) {
331
13
            LOG(INFO) << "begin dump " << queue_name << " with " << elements.size() << " elements";
332
34
            for (const auto& [hash, offset, size] : elements) {
333
34
                RETURN_IF_STATUS_ERROR(st,
334
34
                                       dump_one_lru_entry(out, tmp_filename, hash, offset, size));
335
34
            }
336
13
            RETURN_IF_STATUS_ERROR(st, finalize_dump(out, elements.size(), tmp_filename,
337
13
                                                     final_filename, file_size));
338
13
        } else {
339
0
            LOG(WARNING) << "open lru dump file failed, reason: " << tmp_filename
340
0
                         << " failed to create";
341
0
        }
342
13
    }
343
13
    *(_mgr->_lru_dump_latency_us) << (duration_ns / 1000);
344
13
    LOG(INFO) << fmt::format("lru dump for {} size={} element={} time={}us", queue_name, file_size,
345
13
                             elements.size(), duration_ns / 1000);
346
13
};
347
348
Status CacheLRUDumper::parse_dump_footer(std::ifstream& in, std::string& filename,
349
12
                                         size_t& entry_num) {
350
12
    size_t file_size = std::filesystem::file_size(filename);
351
352
    // Read footer
353
12
    Footer footer;
354
12
    size_t footer_size = sizeof(footer);
355
12
    if (file_size < footer_size) {
356
0
        std::string warn_msg = std::string(fmt::format(
357
0
                "LRU dump file too small to contain footer, file={}, skip restore", filename));
358
0
        LOG(WARNING) << warn_msg;
359
0
        return Status::InternalError<false>(warn_msg);
360
0
    }
361
362
12
    in.seekg(-footer_size, std::ios::end);
363
12
    std::string footer_str(footer_size, '\0');
364
12
    in.read(&footer_str[0], footer_size);
365
12
    RETURN_IF_ERROR(check_ifstream_status(in, filename));
366
367
12
    if (!footer.deserialize_from_string(footer_str)) {
368
0
        std::string warn_msg = std::string(
369
0
                fmt::format("Failed to deserialize footer, file={}, skip restore", filename));
370
0
        LOG(WARNING) << warn_msg;
371
0
        return Status::InternalError<false>(warn_msg);
372
0
    }
373
374
    // Validate footer
375
12
    if (footer.version != 1 || std::string(footer.magic, 3) != "DOR") {
376
0
        std::string warn_msg = std::string(fmt::format(
377
0
                "LRU dump file invalid footer format, file={}, skip restore", filename));
378
0
        LOG(WARNING) << warn_msg;
379
0
        return Status::InternalError<false>(warn_msg);
380
0
    }
381
382
    // Read meta
383
12
    in.seekg(footer.meta_offset, std::ios::beg);
384
12
    size_t meta_size = file_size - footer.meta_offset - footer_size;
385
12
    if (meta_size <= 0) {
386
0
        std::string warn_msg = std::string(
387
0
                fmt::format("LRU dump file invalid meta size, file={}, skip restore", filename));
388
0
        LOG(WARNING) << warn_msg;
389
0
        return Status::InternalError<false>(warn_msg);
390
0
    }
391
12
    std::string meta_serialized(meta_size, '\0');
392
12
    in.read(&meta_serialized[0], meta_serialized.size());
393
12
    RETURN_IF_ERROR(check_ifstream_status(in, filename));
394
12
    _parse_meta.Clear();
395
12
    _current_parse_group.Clear();
396
12
    if (!_parse_meta.ParseFromString(meta_serialized)) {
397
0
        std::string warn_msg = std::string(
398
0
                fmt::format("LRU dump file meta parse failed, file={}, skip restore", filename));
399
0
        LOG(WARNING) << warn_msg;
400
0
        return Status::InternalError<false>(warn_msg);
401
0
    }
402
12
    VLOG_DEBUG << "parse meta: " << _parse_meta.DebugString();
403
404
12
    entry_num = _parse_meta.entry_num();
405
12
    return Status::OK();
406
12
}
407
408
Status CacheLRUDumper::parse_one_lru_entry(std::ifstream& in, std::string& filename,
409
32
                                           UInt128Wrapper& hash, size_t& offset, size_t& size) {
410
    // Read next group if current is empty
411
32
    if (_current_parse_group.entries_size() == 0) {
412
9
        if (_parse_meta.group_offset_size_size() == 0) {
413
0
            return Status::EndOfFile("No more entries");
414
0
        }
415
416
9
        auto group_info = _parse_meta.group_offset_size(0);
417
9
        in.seekg(group_info.offset(), std::ios::beg);
418
9
        std::string group_serialized(group_info.size(), '\0');
419
9
        in.read(&group_serialized[0], group_serialized.size());
420
9
        RETURN_IF_ERROR(check_ifstream_status(in, filename));
421
9
        uint32_t checksum = crc32c::Crc32c(group_serialized.data(), group_serialized.size());
422
9
        if (checksum != group_info.checksum()) {
423
0
            std::string warn_msg =
424
0
                    fmt::format("restore lru failed as checksum not match, file={}", filename);
425
0
            LOG(WARNING) << warn_msg;
426
0
            return Status::InternalError(warn_msg);
427
0
        }
428
9
        if (!_current_parse_group.ParseFromString(group_serialized)) {
429
0
            std::string warn_msg =
430
0
                    fmt::format("restore lru failed to parse group, file={}", filename);
431
0
            LOG(WARNING) << warn_msg;
432
0
            return Status::InternalError(warn_msg);
433
0
        }
434
435
        // Remove processed group info
436
9
        _parse_meta.mutable_group_offset_size()->erase(_parse_meta.group_offset_size().begin());
437
9
    }
438
439
    // Get next entry from current group
440
32
    VLOG_DEBUG << "After deserialization: " << _current_parse_group.DebugString();
441
32
    auto entry = _current_parse_group.entries(0);
442
32
    hash = UInt128Wrapper((static_cast<uint128_t>(entry.hash().high()) << 64) | entry.hash().low());
443
32
    offset = entry.offset();
444
32
    size = entry.size();
445
446
    // Remove processed entry
447
32
    _current_parse_group.mutable_entries()->erase(_current_parse_group.entries().begin());
448
32
    return Status::OK();
449
32
}
450
451
void CacheLRUDumper::restore_queue(LRUQueue& queue, const std::string& queue_name,
452
557
                                   std::lock_guard<std::mutex>& cache_lock) {
453
557
    Status st;
454
557
    std::string filename = fmt::format("{}/lru_dump_{}.tail", _mgr->_cache_base_path, queue_name);
455
557
    std::ifstream in(filename, std::ios::binary);
456
557
    int64_t duration_ns = 0;
457
557
    if (in) {
458
11
        LOG(INFO) << "lru dump file is founded for " << queue_name << ". starting lru restore.";
459
460
11
        SCOPED_RAW_TIMER(&duration_ns);
461
11
        size_t entry_num = 0;
462
11
        RETURN_IF_STATUS_ERROR(st, parse_dump_footer(in, filename, entry_num));
463
11
        LOG(INFO) << "lru dump file for " << queue_name << " has " << entry_num << " entries.";
464
11
        in.seekg(0, std::ios::beg);
465
11
        UInt128Wrapper hash;
466
11
        size_t offset, size;
467
43
        for (int i = 0; i < entry_num; ++i) {
468
32
            RETURN_IF_STATUS_ERROR(st, parse_one_lru_entry(in, filename, hash, offset, size));
469
32
            CacheContext ctx;
470
32
            if (queue_name == "ttl") {
471
7
                ctx.cache_type = FileCacheType::TTL;
472
                // TODO(zhengyu): we haven't persist expiration time yet, use 3h default
473
                // There are mulitiple places we can correct this fake 3h ttl, e.g.:
474
                // 1. during load_cache_info_into_memory (this will cause overwriting the ttl of async load)
475
                // 2. after restoring, use sync_meta to modify the ttl
476
                // However, I plan not to do this in this commit but to figure a more elegant way
477
                // after ttl expiration time being changed from file name encoding to rocksdb persistency.
478
7
                ctx.expiration_time = 10800;
479
25
            } else if (queue_name == "index") {
480
5
                ctx.cache_type = FileCacheType::INDEX;
481
20
            } else if (queue_name == "normal") {
482
13
                ctx.cache_type = FileCacheType::NORMAL;
483
13
            } else if (queue_name == "disposable") {
484
7
                ctx.cache_type = FileCacheType::DISPOSABLE;
485
7
            } else {
486
0
                LOG_WARNING("unknown queue type for lru restore, skip");
487
0
                DCHECK(false);
488
0
                return;
489
0
            }
490
            // TODO(zhengyu): we don't use stats yet, see if this will cause any problem
491
32
            _mgr->add_cell(hash, ctx, offset, size, FileBlock::State::DOWNLOADED, cache_lock);
492
32
        }
493
11
        in.close();
494
546
    } else {
495
546
        LOG(INFO) << "no lru dump file is founded for " << queue_name;
496
546
    }
497
557
    LOG(INFO) << "lru restore time costs: " << (duration_ns / 1000) << "us.";
498
557
};
499
500
60
void CacheLRUDumper::remove_lru_dump_files() {
501
60
    std::vector<std::string> queue_names = {"disposable", "index", "normal", "ttl"};
502
240
    for (const auto& queue_name : queue_names) {
503
240
        std::string filename =
504
240
                fmt::format("{}/lru_dump_{}.tail", _mgr->_cache_base_path, queue_name);
505
240
        if (std::filesystem::exists(filename)) {
506
4
            std::filesystem::remove(filename);
507
4
        }
508
240
    }
509
60
}
510
511
} // end of namespace doris::io