Coverage Report

Created: 2026-10-17 10:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/service/point_query_executor.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 <assert.h>
21
#include <butil/macros.h>
22
#include <butil/time.h>
23
#include <gen_cpp/Metrics_types.h>
24
#include <parallel_hashmap/phmap.h>
25
#include <stdint.h>
26
#include <string.h>
27
28
#include <algorithm>
29
#include <memory>
30
#include <mutex>
31
#include <optional>
32
#include <ostream>
33
#include <string>
34
#include <unordered_map>
35
#include <unordered_set>
36
#include <utility>
37
#include <vector>
38
39
#include "butil/containers/doubly_buffered_data.h"
40
#include "common/config.h"
41
#include "common/logging.h"
42
#include "common/status.h"
43
#include "core/block/block.h"
44
#include "core/data_type_serde/data_type_serde.h"
45
#include "exprs/vexpr_fwd.h"
46
#include "io/cache/remote_scan_cache_write_limiter.h"
47
#include "runtime/descriptors.h"
48
#include "runtime/exec_env.h"
49
#include "runtime/runtime_profile.h"
50
#include "storage/olap_common.h"
51
#include "storage/rowset/rowset.h"
52
#include "storage/tablet/tablet.h"
53
#include "storage/utils.h"
54
#include "util/lru_cache.h"
55
#include "util/mysql_global.h"
56
#include "util/slice.h"
57
58
namespace doris {
59
60
class PTabletKeyLookupRequest;
61
class PTabletKeyLookupResponse;
62
class RuntimeState;
63
class TDescriptorTable;
64
class TExpr;
65
66
// For caching point lookup pre allocted blocks and exprs
67
class Reusable {
68
public:
69
    ~Reusable();
70
71
0
    bool is_expired(int64_t ttl_ms) const {
72
0
        return butil::gettimeofday_ms() - _create_timestamp > ttl_ms;
73
0
    }
74
75
    Status init(const TDescriptorTable& t_desc_tbl, const std::vector<TExpr>& output_exprs,
76
                const TQueryOptions& query_options, const TabletSchema& schema,
77
                size_t block_size = 1);
78
79
    std::unique_ptr<Block> get_block();
80
81
0
    const DataTypeSerDeSPtrs& get_data_type_serdes() const { return _data_type_serdes; }
82
83
0
    const std::unordered_map<uint32_t, uint32_t>& get_col_uid_to_idx() const {
84
0
        return _col_uid_to_idx;
85
0
    }
86
87
0
    const std::vector<std::string>& get_col_default_values() const { return _col_default_values; }
88
89
    // do not touch block after returned
90
    void return_block(std::unique_ptr<Block>& block);
91
92
210k
    TupleDescriptor* tuple_desc() { return _desc_tbl->get_tuple_descriptor(0); }
93
94
0
    const VExprContextSPtrs& output_exprs() { return _output_exprs_ctxs; }
95
96
4
    int32_t rs_column_uid() const { return _row_store_column_ids; }
97
98
8
    const std::unordered_set<int32_t> missing_col_uids() const { return _missing_col_uids; }
99
100
7
    const std::unordered_set<int32_t> include_col_uids() const { return _include_col_uids; }
101
102
7
    const std::unordered_set<int32_t>& column_store_col_uids() const {
103
7
        return _column_store_col_uids;
104
7
    }
105
106
    // Whether the projection holds VERSION/COMMIT_TSO, whose value depends on the owning rowset.
107
5
    bool has_rowset_derived_hidden_columns() const { return _has_rowset_derived_hidden_columns; }
108
109
    // Whether the row-store JSONB still serves any projected column once the rowset-derived
110
    // hidden columns are left out of it.
111
6
    bool decode_row_store() const { return _row_store_column_ids != -1 && _decode_row_store; }
112
113
0
    RuntimeState* runtime_state() { return _runtime_state.get(); }
114
115
    // delete sign idx in block
116
1
    int32_t delete_sign_idx() const { return _delete_sign_idx; }
117
118
private:
119
    // caching TupleDescriptor, output_expr, etc...
120
    std::unique_ptr<RuntimeState> _runtime_state;
121
    DescriptorTbl* _desc_tbl = nullptr;
122
    std::mutex _block_mutex;
123
    // prevent from allocte too many tmp blocks
124
    std::vector<std::unique_ptr<Block>> _block_pool;
125
    VExprContextSPtrs _output_exprs_ctxs;
126
    int64_t _create_timestamp = 0;
127
    DataTypeSerDeSPtrs _data_type_serdes;
128
    std::unordered_map<uint32_t, uint32_t> _col_uid_to_idx;
129
    std::vector<std::string> _col_default_values;
130
    // picked rowstore(column group) column unique id
131
    int32_t _row_store_column_ids = -1;
132
    // some column is missing in rowstore(column group), we need to fill them with column store values
133
    std::unordered_set<int32_t> _missing_col_uids;
134
    // cids decoded from the rowstore(column group) JSONB; empty means every slot
135
    std::unordered_set<int32_t> _include_col_uids;
136
    // Missing columns plus the projected VERSION/COMMIT_TSO: everything the JSONB does not serve.
137
    std::unordered_set<int32_t> _column_store_col_uids;
138
    bool _has_rowset_derived_hidden_columns = false;
139
    bool _decode_row_store = true;
140
    // delete sign idx in block
141
    int32_t _delete_sign_idx = -1;
142
};
143
144
// RowCache is a LRU cache for row store
145
class RowCache : public LRUCachePolicy {
146
public:
147
    using LRUCachePolicy::insert;
148
149
    // The cache key for row lru cache
150
    struct RowCacheKey {
151
25
        RowCacheKey(int64_t tablet_id, const Slice& key) : tablet_id(tablet_id), key(key) {}
152
        int64_t tablet_id;
153
        Slice key;
154
155
        // Encode to a flat binary which can be used as LRUCache's key
156
28
        std::string encode() const {
157
28
            std::string full_key;
158
28
            full_key.reserve(sizeof(int64_t) + key.size);
159
28
            const char* tid = reinterpret_cast<const char*>(&tablet_id);
160
28
            full_key.append(tid, tid + sizeof(int64_t));
161
28
            full_key.append(key.data, key.size);
162
28
            return full_key;
163
28
        }
164
    };
165
166
    class RowCacheValue : public LRUCacheValueBase {
167
    public:
168
15
        ~RowCacheValue() override { free(cache_value); }
169
        char* cache_value;
170
    };
171
172
    // A handle for RowCache entry. This class make it easy to handle
173
    // Cache entry. Users don't need to release the obtained cache entry. This
174
    // class will release the cache entry when it is destroyed.
175
    class CacheHandle {
176
    public:
177
10
        CacheHandle() = default;
178
        CacheHandle(LRUCachePolicy* cache, Cache::Handle* handle)
179
20
                : _cache(cache), _handle(handle) {}
180
30
        ~CacheHandle() {
181
30
            if (_handle != nullptr) {
182
20
                _cache->release(_handle);
183
20
            }
184
30
        }
185
186
0
        CacheHandle(CacheHandle&& other) noexcept {
187
0
            std::swap(_cache, other._cache);
188
0
            std::swap(_handle, other._handle);
189
0
        }
190
191
5
        CacheHandle& operator=(CacheHandle&& other) noexcept {
192
5
            std::swap(_cache, other._cache);
193
5
            std::swap(_handle, other._handle);
194
5
            return *this;
195
5
        }
196
197
2
        bool valid() { return _cache != nullptr && _handle != nullptr; }
198
199
0
        LRUCachePolicy* cache() const { return _cache; }
200
1
        Slice data() const {
201
1
            return {((RowCacheValue*)_cache->value(_handle))->cache_value,
202
1
                    reinterpret_cast<LRUHandle*>(_handle)->charge};
203
1
        }
204
205
    private:
206
        LRUCachePolicy* _cache = nullptr;
207
        Cache::Handle* _handle = nullptr;
208
209
        // Don't allow copy and assign
210
        DISALLOW_COPY_AND_ASSIGN(CacheHandle);
211
    };
212
213
    // Create global instance of this class
214
    static RowCache* create_global_cache(int64_t capacity, uint32_t num_shards = kDefaultNumShards);
215
216
    static RowCache* instance();
217
218
    // Lookup a row key from cache,
219
    // If the Row key is found, the cache entry will be written into handle.
220
    // CacheHandle will release cache entry to cache when it destructs
221
    // Return true if entry is found, otherwise return false.
222
    bool lookup(const RowCacheKey& key, CacheHandle* handle);
223
224
    // Insert a row with key into this cache.
225
    // This function is thread-safe, and when two clients insert two same key
226
    // concurrently, this function can assure that only one page is cached.
227
    // The in_memory page will have higher priority.
228
    void insert(const RowCacheKey& key, const Slice& data);
229
230
    //
231
    void erase(const RowCacheKey& key);
232
233
private:
234
    static constexpr uint32_t kDefaultNumShards = 128;
235
    RowCache(int64_t capacity, int num_shards = kDefaultNumShards);
236
};
237
238
// A cache used for prepare stmt.
239
// One connection per stmt perf uuid
240
class LookupConnectionCache : public LRUCachePolicy {
241
public:
242
0
    static LookupConnectionCache* instance() {
243
0
        return ExecEnv::GetInstance()->get_lookup_connection_cache();
244
0
    }
245
246
    static LookupConnectionCache* create_global_instance(size_t capacity);
247
248
private:
249
    friend class PointQueryExecutor;
250
    LookupConnectionCache(size_t capacity)
251
12
            : LRUCachePolicy(CachePolicy::CacheType::LOOKUP_CONNECTION_CACHE, capacity,
252
12
                             LRUCacheType::NUMBER, config::tablet_lookup_cache_stale_sweep_time_sec,
253
12
                             /*num shards*/ 32, /*element count capacity */ 0,
254
12
                             /*enable prune*/ true, /*is lru-k*/ true) {}
255
256
4.14k
    static std::string encode_key(__int128_t cache_id) {
257
4.14k
        fmt::memory_buffer buffer;
258
4.14k
        fmt::format_to(buffer, "{}", cache_id);
259
4.14k
        return std::string(buffer.data(), buffer.size());
260
4.14k
    }
261
262
2.09k
    void add(__int128_t cache_id, std::shared_ptr<Reusable> item) {
263
2.09k
        std::string key = encode_key(cache_id);
264
2.09k
        auto* value = new CacheValue;
265
2.09k
        value->item = item;
266
18.4E
        VLOG_DEBUG << "Add item mem"
267
18.4E
                   << ", cache_capacity: " << get_capacity() << ", cache_usage: " << get_usage()
268
18.4E
                   << ", mem_consum: " << mem_consumption();
269
2.09k
        auto* lru_handle = insert(key, value, 1, sizeof(Reusable), CachePriority::NORMAL);
270
2.09k
        release(lru_handle);
271
2.09k
    }
272
273
2.04k
    std::shared_ptr<Reusable> get(__int128_t cache_id) {
274
2.04k
        std::string key = encode_key(cache_id);
275
2.04k
        auto* lru_handle = lookup(key);
276
2.04k
        if (lru_handle) {
277
46
            Defer release([cache = this, lru_handle] { cache->release(lru_handle); });
278
46
            auto* value = (CacheValue*)(LRUCachePolicy::value(lru_handle));
279
46
            return value->item;
280
46
        }
281
2.00k
        return nullptr;
282
2.04k
    }
283
284
    class CacheValue : public LRUCacheValueBase {
285
    public:
286
        ~CacheValue() override;
287
        std::shared_ptr<Reusable> item;
288
    };
289
};
290
291
struct Metrics {
292
    Metrics()
293
0
            : init_ns(TUnit::TIME_NS),
294
0
              init_key_ns(TUnit::TIME_NS),
295
0
              lookup_key_ns(TUnit::TIME_NS),
296
0
              lookup_data_ns(TUnit::TIME_NS),
297
0
              output_data_ns(TUnit::TIME_NS),
298
0
              load_segment_key_stage_ns(TUnit::TIME_NS),
299
0
              load_segment_data_stage_ns(TUnit::TIME_NS) {}
300
    RuntimeProfile::Counter init_ns;
301
    RuntimeProfile::Counter init_key_ns;
302
    RuntimeProfile::Counter lookup_key_ns;
303
    RuntimeProfile::Counter lookup_data_ns;
304
    RuntimeProfile::Counter output_data_ns;
305
    RuntimeProfile::Counter load_segment_key_stage_ns;
306
    RuntimeProfile::Counter load_segment_data_stage_ns;
307
    OlapReaderStatistics read_stats;
308
    size_t row_cache_hits = 0;
309
    bool hit_lookup_cache = false;
310
    size_t result_data_bytes;
311
};
312
313
// An util to do tablet lookup
314
class PointQueryExecutor {
315
public:
316
    ~PointQueryExecutor();
317
318
    Status init(const PTabletKeyLookupRequest* request, PTabletKeyLookupResponse* response);
319
320
    Status lookup_up();
321
322
    void print_profile();
323
324
0
    const OlapReaderStatistics& read_stats() const { return _read_stats; }
325
326
private:
327
    Status _init_keys(const PTabletKeyLookupRequest* request);
328
329
    Status _lookup_row_key();
330
331
    Status _lookup_row_data();
332
333
    Status _output_data();
334
335
    void _init_remote_scan_cache_write_limiter();
336
337
0
    static void release_rowset(RowsetSharedPtr* r) {
338
0
        if (r && *r) {
339
0
            VLOG_DEBUG << "release rowset " << (*r)->rowset_id();
340
0
            (*r)->release();
341
0
        }
342
0
        delete r;
343
0
    }
344
345
    // Read context for each row
346
    struct RowReadContext {
347
0
        RowReadContext() : _rowset_ptr(nullptr, &release_rowset) {}
348
        std::string _primary_key;
349
        RowCache::CacheHandle _cached_row_data;
350
        std::optional<RowLocation> _row_location;
351
        // rowset will be aquired during read
352
        // and released after used
353
        std::unique_ptr<RowsetSharedPtr, decltype(&release_rowset)> _rowset_ptr;
354
    };
355
356
    PTabletKeyLookupResponse* _response = nullptr;
357
    BaseTabletSPtr _tablet;
358
    std::vector<RowReadContext> _row_read_ctxs;
359
    std::shared_ptr<Reusable> _reusable;
360
    std::unique_ptr<Block> _result_block;
361
    Metrics _profile_metrics;
362
    bool _binary_row_format = false;
363
    OlapReaderStatistics _read_stats;
364
    std::unique_ptr<io::RemoteScanCacheWriteLimiter> _remote_scan_cache_write_limiter;
365
    int32_t _row_hits = 0;
366
    // snapshot read version
367
    int64_t _version = -1;
368
};
369
370
} // namespace doris