Coverage Report

Created: 2026-07-27 20:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/io/fs/stream_sink_file_writer.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/stream_sink_file_writer.h"
19
20
#include <gen_cpp/internal_service.pb.h>
21
22
#include <algorithm>
23
24
#include "exec/sink/load_stream_stub.h"
25
#include "storage/olap_common.h"
26
#include "storage/rowset/beta_rowset_writer.h"
27
#include "util/debug_points.h"
28
#include "util/uid_util.h"
29
30
namespace doris::io {
31
32
22
std::unordered_set<int64_t> StreamSinkFileWriter::_get_fault_injection_failed_dst_ids() const {
33
22
    size_t failed_replica_num = 0;
34
22
    DBUG_EXECUTE_IF("StreamSinkFileWriter.appendv.write_segment_failed_one_replica",
35
22
                    { failed_replica_num = 1; });
36
22
    DBUG_EXECUTE_IF("StreamSinkFileWriter.appendv.write_segment_failed_two_replica",
37
22
                    { failed_replica_num = 2; });
38
22
    DBUG_EXECUTE_IF("StreamSinkFileWriter.appendv.write_segment_failed_all_replica",
39
22
                    { failed_replica_num = _streams.size(); });
40
22
    if (failed_replica_num == 0) {
41
6
        return {};
42
6
    }
43
44
16
    std::vector<int64_t> dst_ids;
45
16
    dst_ids.reserve(_streams.size());
46
48
    for (const auto& stream : _streams) {
47
48
        dst_ids.push_back(stream->dst_id());
48
48
    }
49
16
    std::sort(dst_ids.begin(), dst_ids.end());
50
16
    dst_ids.resize(std::min(failed_replica_num, dst_ids.size()));
51
16
    return std::unordered_set<int64_t>(dst_ids.begin(), dst_ids.end());
52
22
}
53
54
void StreamSinkFileWriter::init(PUniqueId load_id, int64_t partition_id, int64_t index_id,
55
10
                                int64_t tablet_id, int32_t segment_id, FileType file_type) {
56
10
    VLOG_DEBUG << "init stream writer, load id(" << UniqueId(load_id).to_string()
57
0
               << "), partition id(" << partition_id << "), index id(" << index_id
58
0
               << "), tablet_id(" << tablet_id << "), segment_id(" << segment_id << ")"
59
0
               << ", file_type(" << file_type << ")";
60
10
    _load_id = load_id;
61
10
    _partition_id = partition_id;
62
10
    _index_id = index_id;
63
10
    _tablet_id = tablet_id;
64
10
    _segment_id = segment_id;
65
10
    _file_type = file_type;
66
10
}
67
68
10
Status StreamSinkFileWriter::appendv(const Slice* data, size_t data_cnt) {
69
10
    size_t bytes_req = 0;
70
30
    for (int i = 0; i < data_cnt; i++) {
71
20
        bytes_req += data[i].get_size();
72
20
    }
73
74
10
    VLOG_DEBUG << "writer appendv, load_id: " << print_id(_load_id) << ", index_id: " << _index_id
75
0
               << ", tablet_id: " << _tablet_id << ", segment_id: " << _segment_id
76
0
               << ", data_length: " << bytes_req << "file_type" << _file_type;
77
78
10
    std::span<const Slice> slices {data, data_cnt};
79
10
    auto fault_injection_failed_dst_ids = _get_fault_injection_failed_dst_ids();
80
10
    bool ok = false;
81
10
    Status st;
82
30
    for (auto& stream : _streams) {
83
30
        if (fault_injection_failed_dst_ids.contains(stream->dst_id())) {
84
12
            LOG(INFO) << "fault injection skips segment data to backend " << stream->dst_id()
85
12
                      << ", load_id: " << print_id(_load_id) << ", index_id: " << _index_id
86
12
                      << ", tablet_id: " << _tablet_id << ", segment_id: " << _segment_id;
87
12
            continue;
88
12
        }
89
18
        st = stream->append_data(_partition_id, _index_id, _tablet_id, _segment_id, _bytes_appended,
90
18
                                 slices, false, _file_type);
91
18
        ok = ok || st.ok();
92
18
        if (!st.ok()) {
93
0
            LOG(WARNING) << "failed to send segment data to backend " << stream->dst_id()
94
0
                         << ", load_id: " << print_id(_load_id) << ", index_id: " << _index_id
95
0
                         << ", tablet_id: " << _tablet_id << ", segment_id: " << _segment_id
96
0
                         << ", data_length: " << bytes_req << ", reason: " << st;
97
0
        }
98
18
    }
99
10
    if (!ok) {
100
0
        std::stringstream ss;
101
0
        for (auto& stream : _streams) {
102
0
            ss << " " << stream->dst_id();
103
0
        }
104
0
        LOG(WARNING) << "failed to send segment data to any replicas, load_id: "
105
0
                     << print_id(_load_id) << ", index_id: " << _index_id
106
0
                     << ", tablet_id: " << _tablet_id << ", segment_id: " << _segment_id
107
0
                     << ", data_length: " << bytes_req << ", backends:" << ss.str();
108
0
        return Status::InternalError(
109
0
                "failed to send segment data to any replicas, tablet_id={}, segment_id={}",
110
0
                _tablet_id, _segment_id);
111
0
    }
112
10
    _bytes_appended += bytes_req;
113
10
    return Status::OK();
114
10
}
115
116
14
Status StreamSinkFileWriter::close(bool non_block) {
117
14
    if (_state == State::CLOSED) {
118
0
        return Status::InternalError("StreamSinkFileWriter already closed, load id {}",
119
0
                                     print_id(_load_id));
120
0
    }
121
14
    if (_state == State::ASYNC_CLOSING) {
122
2
        if (non_block) {
123
0
            return Status::InternalError("Don't submit async close multi times");
124
0
        }
125
        // Actucally the first time call to close(true) would return the value of _finalize, if it returned one
126
        // error status then the code would never call the second close(true)
127
2
        _state = State::CLOSED;
128
2
        return Status::OK();
129
2
    }
130
12
    if (non_block) {
131
2
        _state = State::ASYNC_CLOSING;
132
10
    } else {
133
10
        _state = State::CLOSED;
134
10
    }
135
12
    return _finalize();
136
14
}
137
138
12
Status StreamSinkFileWriter::_finalize() {
139
12
    VLOG_DEBUG << "writer finalize, load_id: " << print_id(_load_id) << ", index_id: " << _index_id
140
0
               << ", tablet_id: " << _tablet_id << ", segment_id: " << _segment_id;
141
    // TODO(zhengyu): update get_inverted_index_file_size into stat
142
12
    auto fault_injection_failed_dst_ids = _get_fault_injection_failed_dst_ids();
143
12
    bool ok = false;
144
36
    for (auto& stream : _streams) {
145
36
        if (fault_injection_failed_dst_ids.contains(stream->dst_id())) {
146
12
            LOG(INFO) << "fault injection skips segment eos to backend " << stream->dst_id()
147
12
                      << ", load_id: " << print_id(_load_id) << ", index_id: " << _index_id
148
12
                      << ", tablet_id: " << _tablet_id << ", segment_id: " << _segment_id;
149
12
            continue;
150
12
        }
151
24
        auto st = stream->append_data(_partition_id, _index_id, _tablet_id, _segment_id,
152
24
                                      _bytes_appended, {}, true, _file_type);
153
24
        ok = ok || st.ok();
154
24
        if (!st.ok()) {
155
0
            LOG(WARNING) << "failed to send segment eos to backend " << stream->dst_id()
156
0
                         << ", load_id: " << print_id(_load_id) << ", index_id: " << _index_id
157
0
                         << ", tablet_id: " << _tablet_id << ", segment_id: " << _segment_id
158
0
                         << ", reason: " << st;
159
0
        }
160
24
    }
161
12
    DBUG_EXECUTE_IF("StreamSinkFileWriter.finalize.finalize_failed", { ok = false; });
162
12
    if (!ok) {
163
0
        std::stringstream ss;
164
0
        for (auto& stream : _streams) {
165
0
            ss << " " << stream->dst_id();
166
0
        }
167
0
        LOG(WARNING) << "failed to send segment eos to any replicas, load_id: "
168
0
                     << print_id(_load_id) << ", index_id: " << _index_id
169
0
                     << ", tablet_id: " << _tablet_id << ", segment_id: " << _segment_id
170
0
                     << ", backends:" << ss.str();
171
0
        return Status::InternalError(
172
0
                "failed to send segment eos to any replicas, tablet_id={}, segment_id={}",
173
0
                _tablet_id, _segment_id);
174
0
    }
175
12
    return Status::OK();
176
12
}
177
178
} // namespace doris::io