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 |