Coverage Report

Created: 2026-10-12 09:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/load/channel/tablets_channel.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 <glog/logging.h>
21
22
#include <atomic>
23
#include <condition_variable>
24
#include <cstdint>
25
#include <mutex>
26
#include <ostream>
27
#include <shared_mutex>
28
#include <string>
29
#include <unordered_map>
30
#include <unordered_set>
31
#include <vector>
32
33
#include "common/status.h"
34
#include "core/custom_allocator.h"
35
#include "exec/sink/vtablet_finder.h"
36
#include "load/channel/adaptive_random_bucket_state.h"
37
#include "load/delta_writer/delta_writer_context.h"
38
#include "runtime/runtime_profile.h"
39
#include "util/bitmap.h"
40
#include "util/uid_util.h"
41
42
namespace google::protobuf {
43
template <typename Element>
44
class RepeatedField;
45
template <typename Key, typename T>
46
class Map;
47
template <typename T>
48
class RepeatedPtrField;
49
} // namespace google::protobuf
50
51
namespace doris {
52
class PTabletError;
53
class PTabletInfo;
54
class PTabletWriterOpenRequest;
55
class PTabletWriterOpenResult;
56
class PTabletWriterAddBlockRequest;
57
class PTabletWriterAddBlockResult;
58
class PUniqueId;
59
class TupleDescriptor;
60
class OpenPartitionRequest;
61
class StorageEngine;
62
63
struct TabletsChannelKey {
64
    UniqueId id;
65
    int64_t index_id;
66
67
32.7k
    TabletsChannelKey(const PUniqueId& pid, int64_t index_id_) : id(pid), index_id(index_id_) {}
68
69
65.4k
    ~TabletsChannelKey() noexcept = default;
70
71
0
    bool operator==(const TabletsChannelKey& rhs) const noexcept {
72
0
        return index_id == rhs.index_id && id == rhs.id;
73
0
    }
74
75
    std::string to_string() const;
76
};
77
78
std::ostream& operator<<(std::ostream& os, const TabletsChannelKey& key);
79
80
class BaseDeltaWriter;
81
class MemTableWriter;
82
class OlapTableSchemaParam;
83
class LoadChannel;
84
class EosCompletion;
85
struct WriteRequest;
86
87
// Write channel for a particular (load, index).
88
class BaseTabletsChannel {
89
public:
90
    BaseTabletsChannel(const TabletsChannelKey& key, const UniqueId& load_id, bool is_high_priority,
91
                       RuntimeProfile* profile);
92
93
    virtual ~BaseTabletsChannel();
94
95
    Status open(const PTabletWriterOpenRequest& request);
96
    // open + open writers
97
    Status incremental_open(const PTabletWriterOpenRequest& params);
98
99
    virtual std::unique_ptr<BaseDeltaWriter> create_delta_writer(const WriteRequest& request) = 0;
100
101
    // no-op when this channel has been closed or cancelled
102
    virtual Status add_batch(const PTabletWriterAddBlockRequest& request,
103
                             PTabletWriterAddBlockResult* response) = 0;
104
105
    // Mark sender with 'sender_id' as closed.
106
    // If all senders are closed, close this channel, set '*finished' to true, update 'tablet_vec'
107
    // to include all tablets written in this channel.
108
    // Release the EOS arrival barrier before final flushing/committing, outside _lock.
109
    // no-op when this channel has been closed or cancelled
110
    virtual Status close(LoadChannel* parent, const PTabletWriterAddBlockRequest& req,
111
                         PTabletWriterAddBlockResult* res, bool* finished) = 0;
112
113
    // no-op when this channel has been closed or cancelled
114
    virtual Status cancel();
115
116
397
    const std::shared_ptr<EosCompletion>& eos_completion() const { return _eos_completion; }
117
118
    void refresh_profile();
119
120
32.6k
    size_t total_received_rows() const { return _total_received_rows; }
121
122
32.6k
    size_t num_rows_filtered() const { return _num_rows_filtered; }
123
124
    // means this tablets in this BE is incremental opened partitions.
125
256
    bool is_incremental_channel() const { return _open_by_incremental; }
126
127
    bool is_finished() const { return _state == kFinished; }
128
129
protected:
130
    // Called by the final sender under _lock. Publish arrival before flushing,
131
    // releasing the channel lock while invoking RPC callbacks.
132
    Status _close_and_notify(std::unique_lock<std::mutex>& lock, int sender_id,
133
                             PTabletWriterAddBlockResult* response);
134
    Status _get_close_result(std::unique_lock<std::mutex>& lock, int sender_id,
135
                             PTabletWriterAddBlockResult* response);
136
    // Called exactly once by the final sender, with _lock held.
137
    virtual Status _close_writers(PTabletWriterAddBlockResult* response) = 0;
138
139
    Status _init_adaptive_random_bucket_state(const PTabletWriterOpenRequest& request);
140
    Status _write_block_data(const PTabletWriterAddBlockRequest& request, int64_t cur_seq,
141
                             std::unordered_map<int64_t, TabletAddRowsPayload>& tablet_to_rows,
142
                             PTabletWriterAddBlockResult* response);
143
    Status _write_block_data_for_adaptive_random_bucket(
144
            const PTabletWriterAddBlockRequest& request, int64_t cur_seq,
145
            std::unordered_map<int64_t, DorisVector<uint32_t>>& partition_to_rowidxs,
146
            PTabletWriterAddBlockResult* response);
147
    virtual Status _prepare_adaptive_random_bucket_writer(BaseDeltaWriter* writer);
148
    Status _build_partition_to_rowidxs_for_adaptive_random_bucket(
149
            const PTabletWriterAddBlockRequest& request,
150
            std::unordered_map<int64_t, DorisVector<uint32_t>>* partition_to_rowidxs);
151
    std::shared_ptr<std::mutex> _get_partition_route_lock(int64_t partition_id);
152
153
    // OK does not imply a write: duplicates and closed channels are no-ops.
154
    Status _get_current_seq(int64_t& cur_seq, const PTabletWriterAddBlockRequest& request,
155
                            bool& should_write);
156
157
    // open all writer
158
    Status _open_all_writers(const PTabletWriterOpenRequest& request);
159
160
    void _add_broken_tablet(int64_t tablet_id);
161
    // thread-unsafe, add a shared lock for `_tablet_writers_lock` if needed
162
    bool _is_broken_tablet(int64_t tablet_id) const;
163
    void _add_error_tablet(google::protobuf::RepeatedPtrField<PTabletError>* tablet_errors,
164
                           int64_t tablet_id, Status error) const;
165
    void _build_tablet_to_rows(
166
            const PTabletWriterAddBlockRequest& request,
167
            std::unordered_map<int64_t /* tablet_id */, TabletAddRowsPayload>* tablet_to_rows);
168
    virtual void _init_profile(RuntimeProfile* profile);
169
170
    // id of this load channel
171
    TabletsChannelKey _key;
172
    std::shared_ptr<EosCompletion> _eos_completion;
173
174
    // protect _state change. open and close. when add_batch finished, lock to change _next_seqs also
175
    std::mutex _lock;
176
    enum State {
177
        kInitialized,
178
        kOpened,
179
        kFinished // closed or cancelled
180
    };
181
    State _state;
182
183
    UniqueId _load_id;
184
185
    // initialized in open function
186
    int64_t _txn_id = -1;
187
    int64_t _index_id = -1;
188
    std::shared_ptr<OlapTableSchemaParam> _schema;
189
    TupleDescriptor* _tuple_desc = nullptr;
190
    bool _open_by_incremental = false;
191
192
    // next sequence we expect
193
    std::set<int32_t> _recieved_senders;
194
    int _num_remaining_senders = 0;
195
    std::vector<int64_t> _next_seqs;
196
    Bitmap _closed_senders;
197
    // Arrival releases earlier senders; only the final sender's retries must
198
    // wait for flush/commit and receive its status and tablet results. All these
199
    // fields are protected by _lock, including while callbacks run without it.
200
    int _final_sender_id = -1;
201
    bool _final_close_in_progress = false;
202
    std::condition_variable _final_close_cv;
203
    Status _close_status;
204
    std::unique_ptr<PTabletWriterAddBlockResult> _final_close_result;
205
206
    // tablet_id -> TabletChannel. it will only be changed in open() or inc_open()
207
    std::unordered_map<int64_t, std::unique_ptr<BaseDeltaWriter>> _tablet_writers;
208
    // protect _tablet_writers
209
    std::mutex _tablet_writers_lock;
210
    // broken tablet ids.
211
    // If a tablet write fails, it's id will be added to this set.
212
    // So that following batch will not handle this tablet anymore.
213
    std::unordered_set<int64_t> _broken_tablets;
214
215
    std::shared_mutex _broken_tablets_lock;
216
217
    std::unordered_set<int64_t> _reducing_tablets;
218
219
    std::unordered_set<int64_t> _partition_ids;
220
    std::shared_ptr<AdaptiveRandomBucketState> _adaptive_random_bucket_state;
221
    // Protects the route-lock map. Each entry serializes current-tablet selection, write,
222
    // and rotation for one partition so all senders on this BE share one current bucket.
223
    std::mutex _partition_route_locks_lock;
224
    std::unordered_map<int64_t, std::shared_ptr<std::mutex>> _partition_route_locks;
225
226
    static std::atomic<uint64_t> _s_tablet_writer_count;
227
228
    bool _is_high_priority = false;
229
230
    RuntimeProfile* _profile = nullptr;
231
    RuntimeProfile::Counter* _add_batch_number_counter = nullptr;
232
    RuntimeProfile::HighWaterMarkCounter* _memory_usage_counter = nullptr;
233
    RuntimeProfile::HighWaterMarkCounter* _write_memory_usage_counter = nullptr;
234
    RuntimeProfile::HighWaterMarkCounter* _flush_memory_usage_counter = nullptr;
235
    RuntimeProfile::HighWaterMarkCounter* _max_tablet_memory_usage_counter = nullptr;
236
    RuntimeProfile::HighWaterMarkCounter* _max_tablet_write_memory_usage_counter = nullptr;
237
    RuntimeProfile::HighWaterMarkCounter* _max_tablet_flush_memory_usage_counter = nullptr;
238
    RuntimeProfile::Counter* _add_batch_timer = nullptr;
239
    RuntimeProfile::Counter* _write_block_timer = nullptr;
240
    RuntimeProfile::Counter* _incremental_open_timer = nullptr;
241
242
    // record rows received and filtered
243
    size_t _total_received_rows = 0;
244
    size_t _num_rows_filtered = 0;
245
};
246
247
class DeltaWriter;
248
249
// `StorageEngine` mixin for `BaseTabletsChannel`
250
class TabletsChannel final : public BaseTabletsChannel {
251
public:
252
    TabletsChannel(StorageEngine& engine, const TabletsChannelKey& key, const UniqueId& load_id,
253
                   bool is_high_priority, RuntimeProfile* profile);
254
255
    ~TabletsChannel() override;
256
257
    std::unique_ptr<BaseDeltaWriter> create_delta_writer(const WriteRequest& request) override;
258
259
    Status add_batch(const PTabletWriterAddBlockRequest& request,
260
                     PTabletWriterAddBlockResult* response) override;
261
262
    Status close(LoadChannel* parent, const PTabletWriterAddBlockRequest& req,
263
                 PTabletWriterAddBlockResult* res, bool* finished) override;
264
265
private:
266
    Status _close_writers(PTabletWriterAddBlockResult* response) override;
267
268
    // deal with DeltaWriter commit_txn(), add tablet to list for return.
269
    void _commit_txn(DeltaWriter* writer, PTabletWriterAddBlockResult* res);
270
271
    StorageEngine& _engine;
272
};
273
274
} // namespace doris