Coverage Report

Created: 2026-08-28 09:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/load/channel/load_stream.h
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
#pragma once
19
20
#include <bthread/condition_variable.h>
21
#include <bthread/mutex.h>
22
#include <gen_cpp/olap_common.pb.h>
23
24
#include <memory>
25
#include <mutex>
26
#include <unordered_map>
27
#include <utility>
28
29
#include "brpc/stream.h"
30
#include "butil/iobuf.h"
31
#include "common/compiler_util.h" // IWYU pragma: keep
32
#include "common/status.h"
33
#include "load/channel/load_stream_writer.h"
34
#include "runtime/runtime_profile.h"
35
#include "runtime/workload_management/resource_context.h"
36
37
namespace doris {
38
39
class LoadStreamMgr;
40
class ThreadPoolToken;
41
class OlapTableSchemaParam;
42
43
// origin_segid(index) -> new_segid(value in vector)
44
using SegIdMapping = std::vector<uint32_t>;
45
using FailedTablets = std::vector<std::pair<int64_t, Status>>;
46
class TabletStream {
47
public:
48
    TabletStream(const PUniqueId& load_id, int64_t id, int64_t txn_id,
49
                 LoadStreamMgr* load_stream_mgr, RuntimeProfile* profile);
50
51
    Status init(std::shared_ptr<OlapTableSchemaParam> schema, int64_t index_id,
52
                int64_t partition_id);
53
54
    Status append_data(const PStreamHeader& header, butil::IOBuf* data);
55
    Status add_segment(const PStreamHeader& header, butil::IOBuf* data);
56
86
    void add_num_segments(int64_t num_segments) { _num_segments += num_segments; }
57
3
    void disable_num_segments_check() { _check_num_segments = false; }
58
    // Wait for all pending flush tasks to complete and shut down the flush token.
59
    // Safe to call multiple times.
60
    void wait_for_flush_tasks();
61
    void pre_close();
62
    Status close();
63
85
    int64_t id() const { return _id; }
64
65
    friend std::ostream& operator<<(std::ostream& ostr, const TabletStream& tablet_stream);
66
67
private:
68
    Status _run_in_heavy_work_pool(std::function<Status()> fn);
69
    Status _wait_for_task_slot(size_t max_tasks, int64_t timeout_ms);
70
    void _release_task_slot();
71
72
    int64_t _id;
73
    LoadStreamWriterSharedPtr _load_stream_writer;
74
    std::unique_ptr<ThreadPoolToken> _flush_token;
75
    std::unordered_map<int64_t, std::unique_ptr<SegIdMapping>> _segids_mapping;
76
    std::atomic<uint32_t> _next_segid;
77
    int64_t _num_segments = 0;
78
    bool _check_num_segments = true;
79
    bool _flush_tasks_done = false;
80
    bthread::Mutex _lock;
81
    AtomicStatus _status;
82
    PUniqueId _load_id;
83
    int64_t _txn_id;
84
    RuntimeProfile* _profile = nullptr;
85
    RuntimeProfile::Counter* _append_data_timer = nullptr;
86
    RuntimeProfile::Counter* _add_segment_timer = nullptr;
87
    RuntimeProfile::Counter* _close_wait_timer = nullptr;
88
    LoadStreamMgr* _load_stream_mgr = nullptr;
89
    bthread::Mutex _flush_task_lock;
90
    bthread::ConditionVariable _flush_task_cv;
91
    size_t _pending_flush_tasks = 0;
92
};
93
94
using TabletStreamSharedPtr = std::shared_ptr<TabletStream>;
95
96
class IndexStream {
97
public:
98
    IndexStream(const PUniqueId& load_id, int64_t id, int64_t txn_id,
99
                std::shared_ptr<OlapTableSchemaParam> schema, LoadStreamMgr* load_stream_mgr,
100
                RuntimeProfile* profile);
101
    ~IndexStream();
102
103
    Status append_data(const PStreamHeader& header, butil::IOBuf* data);
104
105
    void close(const std::vector<PTabletID>& tablets_to_commit,
106
               std::vector<int64_t>* success_tablet_ids, FailedTablets* failed_tablet_ids);
107
108
    void get_all_write_tablet_ids(std::vector<int64_t>* tablet_ids);
109
110
private:
111
    void _init_tablet_stream(TabletStreamSharedPtr& tablet_stream, int64_t tablet_id,
112
                             int64_t partition_id);
113
114
private:
115
    int64_t _id;
116
    std::unordered_map<int64_t /*tabletid*/, TabletStreamSharedPtr> _tablet_streams_map;
117
    bthread::Mutex _lock;
118
    PUniqueId _load_id;
119
    int64_t _txn_id;
120
    std::shared_ptr<OlapTableSchemaParam> _schema;
121
    std::unordered_map<int64_t, int64_t> _tablet_partitions;
122
    RuntimeProfile* _profile = nullptr;
123
    RuntimeProfile::Counter* _append_data_timer = nullptr;
124
    RuntimeProfile::Counter* _close_wait_timer = nullptr;
125
    LoadStreamMgr* _load_stream_mgr = nullptr;
126
};
127
using IndexStreamSharedPtr = std::shared_ptr<IndexStream>;
128
129
using StreamId = brpc::StreamId;
130
class LoadStream : public brpc::StreamInputHandler {
131
public:
132
    LoadStream(const PUniqueId& load_id, LoadStreamMgr* load_stream_mgr, bool enable_profile);
133
    ~LoadStream() override;
134
135
    Status init(const POpenLoadStreamRequest* request);
136
137
39
    void add_source(int64_t src_id) {
138
39
        std::lock_guard lock_guard(_lock);
139
39
        _open_streams[src_id]++;
140
39
        if (_is_incremental) {
141
0
            _total_streams++;
142
0
        }
143
39
    }
144
145
    // Count this CLOSE_LOAD. Returns true once CLOSE_LOAD from all streams has been
146
    // received (i.e. the load is ready to commit). Only counts and commits; stream
147
    // closing is handled by the caller in _dispatch to keep EOS-before-close ordering.
148
    bool close(int64_t src_id, const std::vector<PTabletID>& tablets_to_commit,
149
               std::vector<int64_t>* success_tablet_ids, FailedTablets* failed_tablet_ids);
150
151
    // Close/park `stream_id` after its EOS was sent, then return stream ids that are
152
    // now safe to close. A non-incremental stream is returned right away; an incremental
153
    // stream is parked (fencing) until all CLOSE_LOADs arrive, after which any thread
154
    // reaching here drains the parked streams. Only a stream whose EOS was sent is ever
155
    // returned -- this closes both the split-lock leak race and the close-before-EOS race.
156
    std::vector<int64_t> mark_eos_sent_and_collect(int64_t stream_id, bool is_incremental);
157
158
    // callbacks called by brpc
159
    int on_received_messages(StreamId id, butil::IOBuf* const messages[], size_t size) override;
160
    void on_idle_timeout(StreamId id) override;
161
    void on_closed(StreamId id) override;
162
163
    friend std::ostream& operator<<(std::ostream& ostr, const LoadStream& load_stream);
164
165
private:
166
    void _parse_header(butil::IOBuf* const message, PStreamHeader& hdr);
167
    void _dispatch(StreamId id, const PStreamHeader& hdr, butil::IOBuf* data);
168
    Status _append_data(const PStreamHeader& header, butil::IOBuf* data);
169
170
    void _report_result(StreamId stream, const Status& status,
171
                        const std::vector<int64_t>& success_tablet_ids,
172
                        const FailedTablets& failed_tablets, bool eos);
173
    void _report_schema(StreamId stream, const PStreamHeader& hdr);
174
    void _report_tablet_load_info(StreamId stream, int64_t index_id);
175
    void _collect_tablet_load_info_from_tablets(
176
            const std::vector<int64_t>& tablet_ids,
177
            google::protobuf::RepeatedPtrField<PTabletLoadRowsetInfo>* tablet_load_infos);
178
179
    // report failure for one message
180
20
    void _report_failure(StreamId stream, const Status& status, const PStreamHeader& header) {
181
20
        FailedTablets failed_tablets;
182
20
        if (header.has_tablet_id()) {
183
4
            failed_tablets.emplace_back(header.tablet_id(), status);
184
4
        }
185
20
        _report_result(stream, status, {}, failed_tablets, false);
186
20
    }
187
188
    Status _write_stream(StreamId stream, butil::IOBuf& buf);
189
190
private:
191
    PUniqueId _load_id;
192
    std::unordered_map<int64_t, IndexStreamSharedPtr> _index_streams_map;
193
    int32_t _total_streams = 0;
194
    int32_t _close_load_cnt = 0;
195
    std::atomic<int32_t> _close_rpc_cnt = 0;
196
    std::vector<PTabletID> _tablets_to_commit;
197
    bthread::Mutex _lock;
198
    std::unordered_map<int64_t, int32_t> _open_streams;
199
    int64_t _txn_id = 0;
200
    std::shared_ptr<OlapTableSchemaParam> _schema;
201
    bool _enable_profile = false;
202
    std::unique_ptr<RuntimeProfile> _profile;
203
    RuntimeProfile::Counter* _append_data_timer = nullptr;
204
    RuntimeProfile::Counter* _close_wait_timer = nullptr;
205
    LoadStreamMgr* _load_stream_mgr = nullptr;
206
    std::shared_ptr<ResourceContext> _resource_ctx;
207
    // Streams whose EOS has been sent and are waiting to be closed. A stream is added
208
    // here (under _lock) only after its _report_result() finished, and drained once
209
    // _all_close_load_received becomes true. Replaces the old _closing_stream_ids whose
210
    // registration happened in a separate lock scope from the all-received check,
211
    // causing a race that leaked (never-closed) streams.
212
    std::vector<int64_t> _eos_sent_stream_ids;
213
    bool _all_close_load_received = false;
214
    bool _is_incremental = false;
215
};
216
217
using LoadStreamPtr = std::unique_ptr<LoadStream>;
218
219
} // namespace doris