Coverage Report

Created: 2026-07-14 18:05

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