Coverage Report

Created: 2026-08-05 13:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/io/fs/hdfs_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/hdfs_file_reader.h"
19
20
#include <stdint.h>
21
22
#include <algorithm>
23
#include <filesystem>
24
#include <ostream>
25
#include <utility>
26
27
#include "bvar/latency_recorder.h"
28
#include "bvar/reducer.h"
29
#include "common/compiler_util.h" // IWYU pragma: keep
30
#include "common/logging.h"
31
#include "common/metrics/doris_metrics.h"
32
#include "cpp/sync_point.h"
33
#include "io/fs/err_utils.h"
34
#include "io/hdfs_util.h"
35
#include "runtime/file_scan_profile.h"
36
#include "runtime/thread_context.h"
37
#include "runtime/workload_group/workload_group.h"
38
#include "runtime/workload_management/io_throttle.h"
39
#include "runtime/workload_management/resource_context.h"
40
#include "service/backend_options.h"
41
42
namespace doris::io {
43
44
bvar::Adder<uint64_t> hdfs_bytes_read_total("hdfs_file_reader", "bytes_read");
45
bvar::LatencyRecorder hdfs_bytes_per_read("hdfs_file_reader", "bytes_per_read"); // also QPS
46
bvar::PerSecond<bvar::Adder<uint64_t>> hdfs_read_througthput("hdfs_file_reader",
47
                                                             "hdfs_read_throughput",
48
                                                             &hdfs_bytes_read_total);
49
50
namespace {
51
52
Result<FileHandleCache::Accessor> get_file(const hdfsFS& fs, const Path& file, int64_t mtime,
53
91.0k
                                           int64_t file_size) {
54
91.0k
    static FileHandleCache cache(config::max_hdfs_file_handle_cache_num, 16,
55
91.0k
                                 config::max_hdfs_file_handle_cache_time_sec);
56
91.0k
    bool cache_hit;
57
91.0k
    FileHandleCache::Accessor accessor;
58
91.0k
    RETURN_IF_ERROR_RESULT(cache.get_file_handle(fs, file.native(), mtime, file_size, false,
59
91.0k
                                                 &accessor, &cache_hit));
60
91.0k
    return accessor;
61
91.0k
}
62
63
} // namespace
64
65
Result<FileReaderSPtr> HdfsFileReader::create(Path full_path, const hdfsFS& fs, std::string fs_name,
66
                                              const FileReaderOptions& opts,
67
91.0k
                                              RuntimeProfile* profile) {
68
91.0k
    auto path = convert_path(full_path, fs_name);
69
91.0k
    return get_file(fs, path, opts.mtime, opts.file_size).transform([&](auto&& accessor) {
70
90.9k
        return std::make_shared<HdfsFileReader>(std::move(path), std::move(fs_name),
71
90.9k
                                                std::move(accessor), profile, opts.mtime);
72
90.9k
    });
73
91.0k
}
74
75
HdfsFileReader::HdfsFileReader(Path path, std::string fs_name, FileHandleCache::Accessor accessor,
76
                               RuntimeProfile* profile, int64_t mtime)
77
91.0k
        : _path(std::move(path)),
78
91.0k
          _fs_name(std::move(fs_name)),
79
91.0k
          _accessor(std::move(accessor)),
80
91.0k
          _profile(profile),
81
91.0k
          _mtime(mtime) {
82
91.0k
    _handle = _accessor.get();
83
84
91.0k
    DorisMetrics::instance()->hdfs_file_open_reading->increment(1);
85
91.0k
    DorisMetrics::instance()->hdfs_file_reader_total->increment(1);
86
91.0k
    if (_profile != nullptr && is_hdfs(_fs_name)) {
87
88.6k
#ifdef USE_HADOOP_HDFS
88
88.6k
        const char* hdfs_profile_name = "HdfsIO";
89
88.6k
        _total_read_time =
90
88.6k
                ADD_CHILD_TIMER(_profile, hdfs_profile_name,
91
88.6k
                                file_scan_profile::parent_or_root(_profile, file_scan_profile::IO));
92
88.6k
        _hdfs_profile.total_bytes_read =
93
88.6k
                ADD_CHILD_COUNTER(_profile, "TotalBytesRead", TUnit::BYTES, hdfs_profile_name);
94
88.6k
        _hdfs_profile.total_local_bytes_read =
95
88.6k
                ADD_CHILD_COUNTER(_profile, "TotalLocalBytesRead", TUnit::BYTES, hdfs_profile_name);
96
88.6k
        _hdfs_profile.total_short_circuit_bytes_read = ADD_CHILD_COUNTER(
97
88.6k
                _profile, "TotalShortCircuitBytesRead", TUnit::BYTES, hdfs_profile_name);
98
88.6k
        _hdfs_profile.total_total_zero_copy_bytes_read = ADD_CHILD_COUNTER(
99
88.6k
                _profile, "TotalZeroCopyBytesRead", TUnit::BYTES, hdfs_profile_name);
100
101
88.6k
        _hdfs_profile.total_hedged_read =
102
88.6k
                ADD_CHILD_COUNTER(_profile, "TotalHedgedRead", TUnit::UNIT, hdfs_profile_name);
103
88.6k
        _hdfs_profile.hedged_read_in_cur_thread = ADD_CHILD_COUNTER(
104
88.6k
                _profile, "HedgedReadInCurThread", TUnit::UNIT, hdfs_profile_name);
105
88.6k
        _hdfs_profile.hedged_read_wins =
106
88.6k
                ADD_CHILD_COUNTER(_profile, "HedgedReadWins", TUnit::UNIT, hdfs_profile_name);
107
88.6k
#endif
108
88.6k
    }
109
91.0k
}
110
111
90.9k
HdfsFileReader::~HdfsFileReader() {
112
90.9k
    static_cast<void>(close());
113
90.9k
}
114
115
91.7k
Status HdfsFileReader::close() {
116
91.7k
    bool expected = false;
117
91.7k
    if (_closed.compare_exchange_strong(expected, true, std::memory_order_acq_rel)) {
118
90.9k
        DorisMetrics::instance()->hdfs_file_open_reading->increment(-1);
119
90.9k
    }
120
91.7k
    return Status::OK();
121
91.7k
}
122
123
Status HdfsFileReader::read_at_impl(size_t offset, Slice result, size_t* bytes_read,
124
224k
                                    const IOContext* io_ctx) {
125
224k
    SCOPED_TIMER(_total_read_time);
126
224k
    auto st = do_read_at_impl(offset, result, bytes_read, io_ctx);
127
224k
    if (!st.ok()) {
128
0
        _handle = nullptr;
129
0
        _accessor.destroy();
130
0
    }
131
224k
    return st;
132
224k
}
133
134
#ifdef USE_HADOOP_HDFS
135
Status HdfsFileReader::do_read_at_impl(size_t offset, Slice result, size_t* bytes_read,
136
224k
                                       const IOContext* /*io_ctx*/) {
137
224k
    if (closed()) [[unlikely]] {
138
0
        return Status::InternalError("read closed file: {}", _path.native());
139
0
    }
140
141
224k
    if (_handle == nullptr) [[unlikely]] {
142
0
        return Status::InternalError("cached hdfs file handle has been destroyed: {}",
143
0
                                     _path.native());
144
0
    }
145
146
224k
    if (offset > _handle->file_size()) {
147
0
        return Status::IOError("offset exceeds file size(offset: {}, file size: {}, path: {})",
148
0
                               offset, _handle->file_size(), _path.native());
149
0
    }
150
151
224k
    size_t bytes_req = result.size;
152
224k
    char* to = result.data;
153
224k
    bytes_req = std::min(bytes_req, (size_t)(_handle->file_size() - offset));
154
224k
    *bytes_read = 0;
155
224k
    if (UNLIKELY(bytes_req == 0)) {
156
3.60k
        return Status::OK();
157
3.60k
    }
158
159
220k
    LIMIT_REMOTE_SCAN_IO(bytes_read);
160
161
220k
    size_t has_read = 0;
162
440k
    while (has_read < bytes_req) {
163
220k
        int64_t max_to_read = bytes_req - has_read;
164
220k
        tSize to_read = static_cast<tSize>(
165
220k
                std::min(max_to_read, static_cast<int64_t>(std::numeric_limits<tSize>::max())));
166
220k
        tSize loop_read = hdfsPread(_handle->fs(), _handle->file(), offset + has_read,
167
220k
                                    to + has_read, to_read);
168
220k
        {
169
220k
            [[maybe_unused]] Status error_ret;
170
220k
            TEST_INJECTION_POINT_RETURN_WITH_VALUE("HdfsFileReader:read_error", error_ret);
171
220k
        }
172
220k
        if (loop_read < 0) {
173
            // invoker maybe just skip Status.NotFound and continue
174
            // so we need distinguish between it and other kinds of errors
175
0
            std::string _err_msg = hdfs_error();
176
0
            if (_err_msg.find("No such file or directory") != std::string::npos) {
177
0
                return Status::NotFound(_err_msg);
178
0
            }
179
0
            return Status::InternalError(
180
0
                    "Read hdfs file failed. (BE: {}) namenode:{}, path:{}, err: {}",
181
0
                    BackendOptions::get_localhost(), _fs_name, _path.string(), _err_msg);
182
0
        }
183
220k
        if (loop_read == 0) {
184
0
            break;
185
0
        }
186
220k
        has_read += loop_read;
187
220k
    }
188
220k
    *bytes_read = has_read;
189
220k
    hdfs_bytes_read_total << *bytes_read;
190
220k
    hdfs_bytes_per_read << *bytes_read;
191
220k
    return Status::OK();
192
220k
}
193
194
#else
195
// The hedged read only support hdfsPread().
196
// TODO: rethink here to see if there are some difference between hdfsPread() and hdfsRead()
197
Status HdfsFileReader::do_read_at_impl(size_t offset, Slice result, size_t* bytes_read,
198
                                       const IOContext* /*io_ctx*/) {
199
    if (closed()) [[unlikely]] {
200
        return Status::InternalError("read closed file: ", _path.native());
201
    }
202
203
    if (offset > _handle->file_size()) {
204
        return Status::IOError("offset exceeds file size(offset: {}, file size: {}, path: {})",
205
                               offset, _handle->file_size(), _path.native());
206
    }
207
208
    int res = hdfsSeek(_handle->fs(), _handle->file(), offset);
209
    if (res != 0) {
210
        // invoker maybe just skip Status.NotFound and continue
211
        // so we need distinguish between it and other kinds of errors
212
        std::string _err_msg = hdfs_error();
213
        if (_err_msg.find("No such file or directory") != std::string::npos) {
214
            return Status::NotFound(_err_msg);
215
        }
216
        return Status::InternalError("Seek to offset failed. (BE: {}) offset={}, err: {}",
217
                                     BackendOptions::get_localhost(), offset, _err_msg);
218
    }
219
220
    size_t bytes_req = result.size;
221
    char* to = result.data;
222
    bytes_req = std::min(bytes_req, (size_t)(_handle->file_size() - offset));
223
    *bytes_read = 0;
224
    if (UNLIKELY(bytes_req == 0)) {
225
        return Status::OK();
226
    }
227
228
    LIMIT_REMOTE_SCAN_IO(bytes_read);
229
230
    size_t has_read = 0;
231
    while (has_read < bytes_req) {
232
        int64_t loop_read = hdfsRead(_handle->fs(), _handle->file(), to + has_read,
233
                                     static_cast<int32_t>(bytes_req - has_read));
234
        if (loop_read < 0) {
235
            // invoker maybe just skip Status.NotFound and continue
236
            // so we need distinguish between it and other kinds of errors
237
            std::string _err_msg = hdfs_error();
238
            if (_err_msg.find("No such file or directory") != std::string::npos) {
239
                return Status::NotFound(_err_msg);
240
            }
241
            return Status::InternalError(
242
                    "Read hdfs file failed. (BE: {}) namenode:{}, path:{}, err: {}",
243
                    BackendOptions::get_localhost(), _fs_name, _path.string(), _err_msg);
244
        }
245
        if (loop_read == 0) {
246
            break;
247
        }
248
        has_read += loop_read;
249
    }
250
    *bytes_read = has_read;
251
    hdfs_bytes_read_total << *bytes_read;
252
    hdfs_bytes_per_read << *bytes_read;
253
    return Status::OK();
254
}
255
#endif
256
257
12.2k
void HdfsFileReader::_collect_profile_before_close() {
258
12.2k
    if (_profile != nullptr && is_hdfs(_fs_name)) {
259
11.5k
#ifdef USE_HADOOP_HDFS
260
11.5k
        if (_handle == nullptr) [[unlikely]] {
261
0
            return;
262
0
        }
263
264
11.5k
        struct hdfsReadStatistics* hdfs_statistics = nullptr;
265
11.5k
        auto r = hdfsFileGetReadStatistics(_handle->file(), &hdfs_statistics);
266
11.5k
        if (r != 0) {
267
0
            LOG(WARNING) << "Failed to run hdfsFileGetReadStatistics(): " << r
268
0
                         << ", name node: " << _fs_name;
269
0
            return;
270
0
        }
271
11.5k
        COUNTER_UPDATE(_hdfs_profile.total_bytes_read, hdfs_statistics->totalBytesRead);
272
11.5k
        COUNTER_UPDATE(_hdfs_profile.total_local_bytes_read, hdfs_statistics->totalLocalBytesRead);
273
11.5k
        COUNTER_UPDATE(_hdfs_profile.total_short_circuit_bytes_read,
274
11.5k
                       hdfs_statistics->totalShortCircuitBytesRead);
275
11.5k
        COUNTER_UPDATE(_hdfs_profile.total_total_zero_copy_bytes_read,
276
11.5k
                       hdfs_statistics->totalZeroCopyBytesRead);
277
11.5k
        hdfsFileFreeReadStatistics(hdfs_statistics);
278
279
11.5k
        struct hdfsHedgedReadMetrics* hdfs_hedged_read_statistics = nullptr;
280
11.5k
        r = hdfsGetHedgedReadMetrics(_handle->fs(), &hdfs_hedged_read_statistics);
281
11.5k
        if (r != 0) {
282
0
            LOG(WARNING) << "Failed to run hdfsGetHedgedReadMetrics(): " << r
283
0
                         << ", name node: " << _fs_name;
284
0
            return;
285
0
        }
286
287
11.5k
        COUNTER_UPDATE(_hdfs_profile.total_hedged_read, hdfs_hedged_read_statistics->hedgedReadOps);
288
11.5k
        COUNTER_UPDATE(_hdfs_profile.hedged_read_in_cur_thread,
289
11.5k
                       hdfs_hedged_read_statistics->hedgedReadOpsInCurThread);
290
11.5k
        COUNTER_UPDATE(_hdfs_profile.hedged_read_wins,
291
11.5k
                       hdfs_hedged_read_statistics->hedgedReadOpsWin);
292
293
11.5k
        hdfsFreeHedgedReadMetrics(hdfs_hedged_read_statistics);
294
11.5k
        hdfsFileClearReadStatistics(_handle->file());
295
11.5k
#endif
296
11.5k
    }
297
12.2k
}
298
299
} // namespace doris::io