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 |