Coverage Report

Created: 2026-07-18 16:12

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
11
std::unordered_set<int64_t> StreamSinkFileWriter::_get_fault_injection_failed_dst_ids() const {
33
11
    size_t failed_replica_num = 0;
34
11
    DBUG_EXECUTE_IF("StreamSinkFileWriter.appendv.write_segment_failed_one_replica",
35
11
                    { failed_replica_num = 1; });
36
11
    DBUG_EXECUTE_IF("StreamSinkFileWriter.appendv.write_segment_failed_two_replica",
37
11
                    { failed_replica_num = 2; });
38
11
    DBUG_EXECUTE_IF("StreamSinkFileWriter.appendv.write_segment_failed_all_replica",
39
11
                    { failed_replica_num = _streams.size(); });
40
11
    if (failed_replica_num == 0) {
41
3
        return {};
42
3
    }
43
44
8
    std::vector<int64_t> dst_ids;
45
8
    dst_ids.reserve(_streams.size());
46
24
    for (const auto& stream : _streams) {
47
24
        dst_ids.push_back(stream->dst_id());
48
24
    }
49
8
    std::sort(dst_ids.begin(), dst_ids.end());
50
8
    dst_ids.resize(std::min(failed_replica_num, dst_ids.size()));
51
8
    return std::unordered_set<int64_t>(dst_ids.begin(), dst_ids.end());
52
11
}
53
54
void StreamSinkFileWriter::init(PUniqueId load_id, int64_t partition_id, int64_t index_id,
55
5
                                int64_t tablet_id, int32_t segment_id, FileType file_type) {
56
5
    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
5
    _load_id = load_id;
61
5
    _partition_id = partition_id;
62
5
    _index_id = index_id;
63
5
    _tablet_id = tablet_id;
64
5
    _segment_id = segment_id;
65
5
    _file_type = file_type;
66
5
}
67
68
5
Status StreamSinkFileWriter::appendv(const Slice* data, size_t data_cnt) {
69
5
    size_t bytes_req = 0;
70
15
    for (int i = 0; i < data_cnt; i++) {
71
10
        bytes_req += data[i].get_size();
72
10
    }
73
74
5
    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
5
    std::span<const Slice> slices {data, data_cnt};
79
5
    auto fault_injection_failed_dst_ids = _get_fault_injection_failed_dst_ids();
80
5
    bool ok = false;
81
5
    Status st;
82
15
    for (auto& stream : _streams) {
83
15
        if (fault_injection_failed_dst_ids.contains(stream->dst_id())) {
84
6
            LOG(INFO) << "fault injection skips segment data to backend " << stream->dst_id()
85
6
                      << ", load_id: " << print_id(_load_id) << ", index_id: " << _index_id
86
6
                      << ", tablet_id: " << _tablet_id << ", segment_id: " << _segment_id;
87
6
            continue;
88
6
        }
89
9
        st = stream->append_data(_partition_id, _index_id, _tablet_id, _segment_id, _bytes_appended,
90
9
                                 slices, false, _file_type);
91
9
        ok = ok || st.ok();
92
9
        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
9
    }
99
5
    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
5
    _bytes_appended += bytes_req;
113
5
    return Status::OK();
114
5
}
115
116
7
Status StreamSinkFileWriter::close(bool non_block) {
117
7
    if (_state == State::CLOSED) {
118
0
        return Status::InternalError("StreamSinkFileWriter already closed, load id {}",
119
0
                                     print_id(_load_id));
120
0
    }
121
7
    if (_state == State::ASYNC_CLOSING) {
122
1
        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
1
        _state = State::CLOSED;
128
1
        return Status::OK();
129
1
    }
130
6
    if (non_block) {
131
1
        _state = State::ASYNC_CLOSING;
132
5
    } else {
133
5
        _state = State::CLOSED;
134
5
    }
135
6
    return _finalize();
136
7
}
137
138
6
Status StreamSinkFileWriter::_finalize() {
139
6
    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
6
    auto fault_injection_failed_dst_ids = _get_fault_injection_failed_dst_ids();
143
6
    bool ok = false;
144
18
    for (auto& stream : _streams) {
145
18
        if (fault_injection_failed_dst_ids.contains(stream->dst_id())) {
146
6
            LOG(INFO) << "fault injection skips segment eos to backend " << stream->dst_id()
147
6
                      << ", load_id: " << print_id(_load_id) << ", index_id: " << _index_id
148
6
                      << ", tablet_id: " << _tablet_id << ", segment_id: " << _segment_id;
149
6
            continue;
150
6
        }
151
12
        auto st = stream->append_data(_partition_id, _index_id, _tablet_id, _segment_id,
152
12
                                      _bytes_appended, {}, true, _file_type);
153
12
        ok = ok || st.ok();
154
12
        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
12
    }
161
6
    DBUG_EXECUTE_IF("StreamSinkFileWriter.finalize.finalize_failed", { ok = false; });
162
6
    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
6
    return Status::OK();
176
6
}
177
178
} // namespace doris::io