Coverage Report

Created: 2026-09-20 01:59

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