Coverage Report

Created: 2026-08-07 08:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/io/fs/local_file_reader.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/fs/local_file_reader.h"
19
20
#include <bthread/bthread.h>
21
#include <butil/iobuf.h>
22
// IWYU pragma: no_include <bthread/errno.h>
23
#include <bvar/bvar.h>
24
#include <errno.h> // IWYU pragma: keep
25
#include <fmt/format.h>
26
#include <glog/logging.h>
27
#include <unistd.h>
28
29
#include <algorithm>
30
#include <atomic>
31
#include <cstring>
32
#include <string>
33
#include <utility>
34
35
#include "common/compiler_util.h" // IWYU pragma: keep
36
#include "common/metrics/doris_metrics.h"
37
#include "cpp/sync_point.h"
38
#include "io/fs/err_utils.h"
39
#include "runtime/thread_context.h"
40
#include "runtime/workload_group/workload_group.h"
41
#include "runtime/workload_management/io_throttle.h"
42
#include "runtime/workload_management/resource_context.h"
43
#include "storage/data_dir.h"
44
#include "storage/olap_common.h"
45
#include "storage/options.h"
46
#include "util/async_io.h"
47
#include "util/debug_points.h"
48
#include "util/defer_op.h"
49
50
namespace doris {
51
namespace io {
52
// 1: initing 2: inited 0: before init
53
std::atomic_int BeConfDataDirReader::be_config_data_dir_list_state = 0;
54
55
std::vector<doris::DataDirInfo> BeConfDataDirReader::be_config_data_dir_list;
56
57
void BeConfDataDirReader::get_data_dir_by_file_path(io::Path* file_path,
58
199k
                                                    std::string* data_dir_arg) {
59
199k
    int state = be_config_data_dir_list_state.load(std::memory_order_acquire);
60
199k
    if (state == 0) [[unlikely]] {
61
19.5k
        return;
62
180k
    } else if (state == 1) [[unlikely]] {
63
0
        be_config_data_dir_list_state.wait(1);
64
0
    }
65
66
532k
    for (const auto& data_dir_info : be_config_data_dir_list) {
67
532k
        if (data_dir_info.path.size() >= file_path->string().size()) {
68
174k
            continue;
69
174k
        }
70
358k
        if (file_path->string().compare(0, data_dir_info.path.size(), data_dir_info.path) == 0) {
71
179k
            *data_dir_arg = data_dir_info.path;
72
179k
            break;
73
179k
        }
74
358k
    }
75
180k
}
76
77
void BeConfDataDirReader::init_be_conf_data_dir(
78
        const std::vector<doris::StorePath>& store_paths,
79
        const std::vector<doris::StorePath>& spill_store_paths,
80
6
        const std::vector<doris::CachePath>& cache_paths) {
81
6
    be_config_data_dir_list_state.store(1, std::memory_order_release);
82
6
    Defer defer {[]() {
83
6
        be_config_data_dir_list_state.store(2, std::memory_order_release);
84
6
        be_config_data_dir_list_state.notify_all();
85
6
    }};
86
15
    for (int i = 0; i < store_paths.size(); i++) {
87
9
        DataDirInfo data_dir_info;
88
9
        data_dir_info.path = store_paths[i].path;
89
9
        data_dir_info.storage_medium = store_paths[i].storage_medium;
90
9
        data_dir_info.data_dir_type = DataDirType::OLAP_DATA_DIR;
91
9
        data_dir_info.metric_name = "local_data_dir_" + std::to_string(i);
92
9
        be_config_data_dir_list.push_back(data_dir_info);
93
9
    }
94
95
15
    for (int i = 0; i < spill_store_paths.size(); i++) {
96
9
        doris::DataDirInfo data_dir_info;
97
9
        data_dir_info.path = spill_store_paths[i].path;
98
9
        data_dir_info.storage_medium = spill_store_paths[i].storage_medium;
99
9
        data_dir_info.data_dir_type = doris::DataDirType::SPILL_DISK_DIR;
100
9
        data_dir_info.metric_name = "spill_data_dir_" + std::to_string(i);
101
9
        be_config_data_dir_list.push_back(data_dir_info);
102
9
    }
103
104
11
    for (int i = 0; i < cache_paths.size(); i++) {
105
5
        doris::DataDirInfo data_dir_info;
106
5
        data_dir_info.path = cache_paths[i].path;
107
5
        data_dir_info.storage_medium = TStorageMedium::REMOTE_CACHE;
108
5
        data_dir_info.data_dir_type = doris::DataDirType::DATA_CACHE_DIR;
109
5
        data_dir_info.metric_name = "local_cache_dir_" + std::to_string(i);
110
5
        be_config_data_dir_list.push_back(data_dir_info);
111
5
    }
112
113
6
    std::sort(be_config_data_dir_list.begin(), be_config_data_dir_list.end(),
114
39
              [](const DataDirInfo& a, const DataDirInfo& b) {
115
39
                  return a.path.length() > b.path.length();
116
39
              });
117
6
}
118
119
LocalFileReader::LocalFileReader(Path path, size_t file_size, int fd)
120
200k
        : _fd(fd), _path(std::move(path)), _file_size(file_size) {
121
200k
    _data_dir_path = "";
122
200k
    BeConfDataDirReader::get_data_dir_by_file_path(&_path, &_data_dir_path);
123
200k
    DorisMetrics::instance()->local_file_open_reading->increment(1);
124
200k
    DorisMetrics::instance()->local_file_reader_total->increment(1);
125
200k
}
126
127
197k
LocalFileReader::~LocalFileReader() {
128
197k
    WARN_IF_ERROR(close(), fmt::format("Failed to close file {}", _path.native()));
129
197k
}
130
131
200k
Status LocalFileReader::close() {
132
200k
    bool expected = false;
133
200k
    if (_closed.compare_exchange_strong(expected, true, std::memory_order_acq_rel)) {
134
197k
        DorisMetrics::instance()->local_file_open_reading->increment(-1);
135
197k
        int res = -1;
136
197k
        if (bthread_self() == 0) {
137
197k
            res = ::close(_fd);
138
197k
        } else {
139
25
            auto task = [&] { res = ::close(_fd); };
140
25
            AsyncIO::run_task(task, io::FileSystemType::LOCAL);
141
25
        }
142
197k
        if (-1 == res) {
143
0
            std::string err = errno_to_str();
144
0
            return localfs_error(errno, fmt::format("failed to close {}", _path.native()));
145
0
        }
146
197k
        _fd = -1;
147
197k
    }
148
200k
    return Status::OK();
149
200k
}
150
151
Status LocalFileReader::read_at_impl(size_t offset, Slice result, size_t* bytes_read,
152
2.01M
                                     const IOContext* /*io_ctx*/) {
153
2.01M
    TEST_SYNC_POINT_RETURN_WITH_VALUE("LocalFileReader::read_at_impl",
154
2.01M
                                      Status::IOError("inject io error"));
155
2.01M
    if (closed()) [[unlikely]] {
156
0
        return Status::InternalError("read closed file: ", _path.native());
157
0
    }
158
159
2.01M
    if (offset > _file_size) {
160
0
        return Status::InternalError(
161
0
                "offset exceeds file size(offset: {}, file size: {}, path: {})", offset, _file_size,
162
0
                _path.native());
163
0
    }
164
2.01M
    size_t bytes_req = result.size;
165
2.01M
    char* to = result.data;
166
2.01M
    bytes_req = std::min(bytes_req, _file_size - offset);
167
2.01M
    *bytes_read = 0;
168
169
2.01M
    LIMIT_LOCAL_SCAN_IO(get_data_dir_path(), bytes_read);
170
171
4.03M
    while (bytes_req != 0) {
172
2.01M
        auto res = SYNC_POINT_HOOK_RETURN_VALUE(::pread(_fd, to, bytes_req, offset),
173
2.01M
                                                "LocalFileReader::pread", _fd, to);
174
2.01M
        DBUG_EXECUTE_IF("LocalFileReader::read_at_impl.io_error", {
175
2.01M
            auto sub_path = dp->param<std::string>("sub_path", "");
176
2.01M
            if ((sub_path.empty() && _path.filename().compare(kTestFilePath)) ||
177
2.01M
                (!sub_path.empty() && _path.native().find(sub_path) != std::string::npos)) {
178
2.01M
                res = -1;
179
2.01M
                errno = EIO;
180
2.01M
                LOG(WARNING) << Status::IOError("debug read io error: {}", _path.native());
181
2.01M
            }
182
2.01M
        });
183
2.01M
        if (UNLIKELY(-1 == res && errno != EINTR)) {
184
0
            return localfs_error(errno, fmt::format("failed to read {}", _path.native()));
185
0
        }
186
2.01M
        if (UNLIKELY(res == 0)) {
187
0
            return Status::InternalError("cannot read from {}: unexpected EOF", _path.native());
188
0
        }
189
2.01M
        if (res > 0) {
190
2.01M
            to += res;
191
2.01M
            offset += res;
192
2.01M
            bytes_req -= res;
193
2.01M
            *bytes_read += res;
194
2.01M
        }
195
2.01M
    }
196
2.01M
    DorisMetrics::instance()->local_bytes_read_total->increment(*bytes_read);
197
2.01M
    return Status::OK();
198
2.01M
}
199
200
Status LocalFileReader::read_at_iobuf_impl(size_t offset, size_t bytes_req, butil::IOBuf* out,
201
0
                                           size_t* bytes_read, const IOContext* /*io_ctx*/) {
202
0
    TEST_SYNC_POINT_RETURN_WITH_VALUE("LocalFileReader::read_at_iobuf_impl",
203
0
                                      Status::IOError("inject io error"));
204
0
    if (out == nullptr || bytes_read == nullptr) {
205
0
        return Status::InvalidArgument("read_at_iobuf requires non-null out and bytes_read");
206
0
    }
207
0
    if (closed()) [[unlikely]] {
208
0
        return Status::InternalError("read closed file: ", _path.native());
209
0
    }
210
211
0
    if (offset > _file_size) {
212
0
        return Status::InternalError(
213
0
                "offset exceeds file size(offset: {}, file size: {}, path: {})", offset, _file_size,
214
0
                _path.native());
215
0
    }
216
0
    bytes_req = std::min(bytes_req, _file_size - offset);
217
0
    *bytes_read = 0;
218
0
    if (bytes_req == 0) {
219
0
        return Status::OK();
220
0
    }
221
222
0
    LIMIT_LOCAL_SCAN_IO(get_data_dir_path(), bytes_read);
223
224
0
    butil::IOPortal portal;
225
0
    while (bytes_req != 0) {
226
0
        ssize_t res =
227
0
                portal.pappend_from_file_descriptor(_fd, static_cast<off_t>(offset), bytes_req);
228
0
        if (UNLIKELY(-1 == res && errno != EINTR)) {
229
0
            return localfs_error(errno, fmt::format("failed to read {}", _path.native()));
230
0
        }
231
0
        if (UNLIKELY(res == 0)) {
232
0
            return Status::InternalError("cannot read from {}: unexpected EOF", _path.native());
233
0
        }
234
0
        if (res > 0) {
235
0
            offset += static_cast<size_t>(res);
236
0
            bytes_req -= static_cast<size_t>(res);
237
0
            *bytes_read += static_cast<size_t>(res);
238
0
        }
239
0
    }
240
0
    out->append(portal);
241
0
    DorisMetrics::instance()->local_bytes_read_total->increment(*bytes_read);
242
0
    return Status::OK();
243
0
}
244
245
} // namespace io
246
} // namespace doris