Coverage Report

Created: 2024-11-21 14:46

/root/doris/be/src/olap/lru_cache.cpp
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file. See the AUTHORS file for names of contributors.
4
5
#include "olap/lru_cache.h"
6
7
#include <stdlib.h>
8
9
#include <mutex>
10
#include <new>
11
#include <sstream>
12
#include <string>
13
14
#include "gutil/bits.h"
15
#include "util/doris_metrics.h"
16
#include "util/time.h"
17
18
using std::string;
19
using std::stringstream;
20
21
namespace doris {
22
23
DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(cache_capacity, MetricUnit::BYTES);
24
DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(cache_usage, MetricUnit::BYTES);
25
DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(cache_element_count, MetricUnit::NOUNIT);
26
DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(cache_usage_ratio, MetricUnit::NOUNIT);
27
DEFINE_COUNTER_METRIC_PROTOTYPE_2ARG(cache_lookup_count, MetricUnit::OPERATIONS);
28
DEFINE_COUNTER_METRIC_PROTOTYPE_2ARG(cache_hit_count, MetricUnit::OPERATIONS);
29
DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(cache_hit_ratio, MetricUnit::NOUNIT);
30
31
449k
uint32_t CacheKey::hash(const char* data, size_t n, uint32_t seed) const {
32
    // Similar to murmur hash
33
449k
    const uint32_t m = 0xc6a4a793;
34
449k
    const uint32_t r = 24;
35
449k
    const char* limit = data + n;
36
449k
    uint32_t h = seed ^ (n * m);
37
38
    // Pick up four bytes at a time
39
1.10M
    while (data + 4 <= limit) {
40
659k
        uint32_t w = _decode_fixed32(data);
41
659k
        data += 4;
42
659k
        h += w;
43
659k
        h *= m;
44
659k
        h ^= (h >> 16);
45
659k
    }
46
47
    // Pick up remaining bytes
48
449k
    switch (limit - data) {
49
8.91k
    case 3:
50
8.91k
        h += static_cast<unsigned char>(data[2]) << 16;
51
52
        // fall through
53
11.4k
    case 2:
54
11.4k
        h += static_cast<unsigned char>(data[1]) << 8;
55
56
        // fall through
57
12.1k
    case 1:
58
12.1k
        h += static_cast<unsigned char>(data[0]);
59
12.1k
        h *= m;
60
12.1k
        h ^= (h >> r);
61
12.1k
        break;
62
63
437k
    default:
64
437k
        break;
65
449k
    }
66
67
449k
    return h;
68
449k
}
69
70
268
Cache::~Cache() {}
71
72
8.39k
HandleTable::~HandleTable() {
73
8.39k
    delete[] _list;
74
8.39k
}
75
76
// LRU cache implementation
77
241k
LRUHandle* HandleTable::lookup(const CacheKey& key, uint32_t hash) {
78
241k
    return *_find_pointer(key, hash);
79
241k
}
80
81
207k
LRUHandle* HandleTable::insert(LRUHandle* h) {
82
207k
    LRUHandle** ptr = _find_pointer(h->key(), h->hash);
83
207k
    LRUHandle* old = *ptr;
84
207k
    h->next_hash = old ? old->next_hash : nullptr;
85
207k
    *ptr = h;
86
87
207k
    if (old == nullptr) {
88
207k
        ++_elems;
89
207k
        if (_elems > _length) {
90
            // Since each cache entry is fairly large, we aim for a small
91
            // average linked list length (<= 1).
92
80
            _resize();
93
80
        }
94
207k
    }
95
96
207k
    return old;
97
207k
}
98
99
12
LRUHandle* HandleTable::remove(const CacheKey& key, uint32_t hash) {
100
12
    LRUHandle** ptr = _find_pointer(key, hash);
101
12
    LRUHandle* result = *ptr;
102
103
12
    if (result != nullptr) {
104
5
        *ptr = result->next_hash;
105
5
        _elems--;
106
5
    }
107
108
12
    return result;
109
12
}
110
111
202k
bool HandleTable::remove(const LRUHandle* h) {
112
202k
    LRUHandle** ptr = &(_list[h->hash & (_length - 1)]);
113
203k
    while (*ptr != nullptr && *ptr != h) {
114
90
        ptr = &(*ptr)->next_hash;
115
90
    }
116
117
202k
    LRUHandle* result = *ptr;
118
202k
    if (result != nullptr) {
119
202k
        *ptr = result->next_hash;
120
202k
        _elems--;
121
202k
        return true;
122
202k
    }
123
0
    return false;
124
202k
}
125
126
449k
LRUHandle** HandleTable::_find_pointer(const CacheKey& key, uint32_t hash) {
127
449k
    LRUHandle** ptr = &(_list[hash & (_length - 1)]);
128
730k
    while (*ptr != nullptr && ((*ptr)->hash != hash || key != (*ptr)->key())) {
129
281k
        ptr = &(*ptr)->next_hash;
130
281k
    }
131
132
449k
    return ptr;
133
449k
}
134
135
9.01k
void HandleTable::_resize() {
136
9.01k
    uint32_t new_length = 16;
137
9.25k
    while (new_length < _elems * 1.5) {
138
240
        new_length *= 2;
139
240
    }
140
141
9.01k
    LRUHandle** new_list = new (std::nothrow) LRUHandle*[new_length];
142
9.01k
    memset(new_list, 0, sizeof(new_list[0]) * new_length);
143
144
9.01k
    uint32_t count = 0;
145
16.9k
    for (uint32_t i = 0; i < _length; i++) {
146
7.93k
        LRUHandle* h = _list[i];
147
15.9k
        while (h != nullptr) {
148
8.01k
            LRUHandle* next = h->next_hash;
149
8.01k
            uint32_t hash = h->hash;
150
8.01k
            LRUHandle** ptr = &new_list[hash & (new_length - 1)];
151
8.01k
            h->next_hash = *ptr;
152
8.01k
            *ptr = h;
153
8.01k
            h = next;
154
8.01k
            count++;
155
8.01k
        }
156
7.93k
    }
157
158
9.01k
    DCHECK_EQ(_elems, count);
159
9.01k
    delete[] _list;
160
9.01k
    _list = new_list;
161
9.01k
    _length = new_length;
162
9.01k
}
163
164
26
uint32_t HandleTable::element_count() const {
165
26
    return _elems;
166
26
}
167
168
8.93k
LRUCache::LRUCache(LRUCacheType type) : _type(type) {
169
    // Make empty circular linked list
170
8.93k
    _lru_normal.next = &_lru_normal;
171
8.93k
    _lru_normal.prev = &_lru_normal;
172
8.93k
    _lru_durable.next = &_lru_durable;
173
8.93k
    _lru_durable.prev = &_lru_durable;
174
8.93k
}
175
176
8.39k
LRUCache::~LRUCache() {
177
8.39k
    prune();
178
8.39k
}
179
180
636k
bool LRUCache::_unref(LRUHandle* e) {
181
636k
    DCHECK(e->refs > 0);
182
636k
    e->refs--;
183
636k
    return e->refs == 0;
184
636k
}
185
186
423k
void LRUCache::_lru_remove(LRUHandle* e) {
187
423k
    e->next->prev = e->prev;
188
423k
    e->prev->next = e->next;
189
423k
    e->prev = e->next = nullptr;
190
191
423k
    if (_cache_value_check_timestamp) {
192
27
        if (e->priority == CachePriority::NORMAL) {
193
27
            auto pair = std::make_pair(_cache_value_time_extractor(e->value), e);
194
27
            auto found_it = _sorted_normal_entries_with_timestamp.find(pair);
195
27
            if (found_it != _sorted_normal_entries_with_timestamp.end()) {
196
27
                _sorted_normal_entries_with_timestamp.erase(found_it);
197
27
            }
198
27
        } else if (e->priority == CachePriority::DURABLE) {
199
0
            auto pair = std::make_pair(_cache_value_time_extractor(e->value), e);
200
0
            auto found_it = _sorted_durable_entries_with_timestamp.find(pair);
201
0
            if (found_it != _sorted_durable_entries_with_timestamp.end()) {
202
0
                _sorted_durable_entries_with_timestamp.erase(found_it);
203
0
            }
204
0
        }
205
27
    }
206
423k
}
207
208
428k
void LRUCache::_lru_append(LRUHandle* list, LRUHandle* e) {
209
    // Make "e" newest entry by inserting just before *list
210
428k
    e->next = list;
211
428k
    e->prev = list->prev;
212
428k
    e->prev->next = e;
213
428k
    e->next->prev = e;
214
215
    // _cache_value_check_timestamp is true,
216
    // means evict entry will depends on the timestamp asc set,
217
    // the timestamp is updated by higher level caller,
218
    // and the timestamp of hit entry is different with the insert entry,
219
    // that is why need check timestamp to evict entry,
220
    // in order to keep the survival time of hit entries
221
    // longer than the entries just inserted,
222
    // so use asc set to sorted these entries's timestamp and LRUHandle*
223
428k
    if (_cache_value_check_timestamp) {
224
27
        if (e->priority == CachePriority::NORMAL) {
225
27
            _sorted_normal_entries_with_timestamp.insert(
226
27
                    std::make_pair(_cache_value_time_extractor(e->value), e));
227
27
        } else if (e->priority == CachePriority::DURABLE) {
228
0
            _sorted_durable_entries_with_timestamp.insert(
229
0
                    std::make_pair(_cache_value_time_extractor(e->value), e));
230
0
        }
231
27
    }
232
428k
}
233
234
241k
Cache::Handle* LRUCache::lookup(const CacheKey& key, uint32_t hash) {
235
241k
    std::lock_guard l(_mutex);
236
241k
    ++_lookup_count;
237
241k
    LRUHandle* e = _table.lookup(key, hash);
238
241k
    if (e != nullptr) {
239
        // we get it from _table, so in_cache must be true
240
226k
        DCHECK(e->in_cache);
241
226k
        if (e->refs == 1) {
242
            // only in LRU free list, remove it from list
243
225k
            _lru_remove(e);
244
225k
        }
245
226k
        e->refs++;
246
226k
        ++_hit_count;
247
226k
        e->last_visit_time = UnixMillis();
248
226k
    }
249
241k
    return reinterpret_cast<Cache::Handle*>(e);
250
241k
}
251
252
433k
void LRUCache::release(Cache::Handle* handle) {
253
433k
    if (handle == nullptr) {
254
0
        return;
255
0
    }
256
433k
    LRUHandle* e = reinterpret_cast<LRUHandle*>(handle);
257
433k
    bool last_ref = false;
258
433k
    {
259
433k
        std::lock_guard l(_mutex);
260
433k
        last_ref = _unref(e);
261
433k
        if (last_ref) {
262
2
            _usage -= e->total_size;
263
433k
        } else if (e->in_cache && e->refs == 1) {
264
            // only exists in cache
265
432k
            if (_usage > _capacity) {
266
                // take this opportunity and remove the item
267
4.35k
                bool removed = _table.remove(e);
268
4.35k
                DCHECK(removed);
269
4.35k
                e->in_cache = false;
270
4.35k
                _unref(e);
271
4.35k
                _usage -= e->total_size;
272
4.35k
                last_ref = true;
273
428k
            } else {
274
                // put it to LRU free list
275
428k
                if (e->priority == CachePriority::NORMAL) {
276
428k
                    _lru_append(&_lru_normal, e);
277
428k
                } else if (e->priority == CachePriority::DURABLE) {
278
15
                    _lru_append(&_lru_durable, e);
279
15
                }
280
428k
            }
281
432k
        }
282
433k
    }
283
284
    // free handle out of mutex
285
433k
    if (last_ref) {
286
4.35k
        e->free();
287
4.35k
    }
288
433k
}
289
290
13
void LRUCache::_evict_from_lru_with_time(size_t total_size, LRUHandle** to_remove_head) {
291
    // 1. evict normal cache entries
292
17
    while ((_usage + total_size > _capacity || _check_element_count_limit()) &&
293
17
           !_sorted_normal_entries_with_timestamp.empty()) {
294
4
        auto entry_pair = _sorted_normal_entries_with_timestamp.begin();
295
4
        LRUHandle* remove_handle = entry_pair->second;
296
4
        DCHECK(remove_handle != nullptr);
297
4
        DCHECK(remove_handle->priority == CachePriority::NORMAL);
298
4
        _evict_one_entry(remove_handle);
299
4
        remove_handle->next = *to_remove_head;
300
4
        *to_remove_head = remove_handle;
301
4
    }
302
303
    // 2. evict durable cache entries if need
304
13
    while ((_usage + total_size > _capacity || _check_element_count_limit()) &&
305
13
           !_sorted_durable_entries_with_timestamp.empty()) {
306
0
        auto entry_pair = _sorted_durable_entries_with_timestamp.begin();
307
0
        LRUHandle* remove_handle = entry_pair->second;
308
0
        DCHECK(remove_handle != nullptr);
309
0
        DCHECK(remove_handle->priority == CachePriority::DURABLE);
310
0
        _evict_one_entry(remove_handle);
311
0
        remove_handle->next = *to_remove_head;
312
0
        *to_remove_head = remove_handle;
313
0
    }
314
13
}
315
316
207k
void LRUCache::_evict_from_lru(size_t total_size, LRUHandle** to_remove_head) {
317
    // 1. evict normal cache entries
318
405k
    while ((_usage + total_size > _capacity || _check_element_count_limit()) &&
319
405k
           _lru_normal.next != &_lru_normal) {
320
197k
        LRUHandle* old = _lru_normal.next;
321
197k
        DCHECK(old->priority == CachePriority::NORMAL);
322
197k
        _evict_one_entry(old);
323
197k
        old->next = *to_remove_head;
324
197k
        *to_remove_head = old;
325
197k
    }
326
    // 2. evict durable cache entries if need
327
207k
    while ((_usage + total_size > _capacity || _check_element_count_limit()) &&
328
207k
           _lru_durable.next != &_lru_durable) {
329
5
        LRUHandle* old = _lru_durable.next;
330
5
        DCHECK(old->priority == CachePriority::DURABLE);
331
5
        _evict_one_entry(old);
332
5
        old->next = *to_remove_head;
333
5
        *to_remove_head = old;
334
5
    }
335
207k
}
336
337
198k
void LRUCache::_evict_one_entry(LRUHandle* e) {
338
198k
    DCHECK(e->in_cache);
339
198k
    DCHECK(e->refs == 1); // LRU list contains elements which may be evicted
340
198k
    _lru_remove(e);
341
198k
    bool removed = _table.remove(e);
342
198k
    DCHECK(removed);
343
198k
    e->in_cache = false;
344
198k
    _unref(e);
345
198k
    _usage -= e->total_size;
346
198k
}
347
348
406k
bool LRUCache::_check_element_count_limit() {
349
406k
    return _element_count_capacity != 0 && _table.element_count() >= _element_count_capacity;
350
406k
}
351
352
Cache::Handle* LRUCache::insert(const CacheKey& key, uint32_t hash, void* value, size_t charge,
353
207k
                                CachePriority priority) {
354
207k
    size_t handle_size = sizeof(LRUHandle) - 1 + key.size();
355
207k
    auto* e = reinterpret_cast<LRUHandle*>(malloc(handle_size));
356
207k
    e->value = value;
357
207k
    e->charge = charge;
358
207k
    e->key_length = key.size();
359
    // if LRUCacheType::NUMBER, charge not add handle_size,
360
    // because charge at this time is no longer the memory size, but an weight.
361
207k
    e->total_size = (_type == LRUCacheType::SIZE ? handle_size + charge : charge);
362
207k
    e->hash = hash;
363
207k
    e->refs = 2; // one for the returned handle, one for LRUCache.
364
207k
    e->next = e->prev = nullptr;
365
207k
    e->in_cache = true;
366
207k
    e->priority = priority;
367
207k
    e->type = _type;
368
207k
    memcpy(e->key_data, key.data(), key.size());
369
207k
    e->last_visit_time = UnixMillis();
370
207k
    LRUHandle* to_remove_head = nullptr;
371
207k
    {
372
207k
        std::lock_guard l(_mutex);
373
374
        // Free the space following strict LRU policy until enough space
375
        // is freed or the lru list is empty
376
207k
        if (_cache_value_check_timestamp) {
377
13
            _evict_from_lru_with_time(e->total_size, &to_remove_head);
378
207k
        } else {
379
207k
            _evict_from_lru(e->total_size, &to_remove_head);
380
207k
        }
381
382
        // insert into the cache
383
        // note that the cache might get larger than its capacity if not enough
384
        // space was freed
385
207k
        auto old = _table.insert(e);
386
207k
        _usage += e->total_size;
387
207k
        if (old != nullptr) {
388
46
            old->in_cache = false;
389
46
            if (_unref(old)) {
390
45
                _usage -= old->total_size;
391
                // old is on LRU because it's in cache and its reference count
392
                // was just 1 (Unref returned 0)
393
45
                _lru_remove(old);
394
45
                old->next = to_remove_head;
395
45
                to_remove_head = old;
396
45
            }
397
46
        }
398
207k
    }
399
400
    // we free the entries here outside of mutex for
401
    // performance reasons
402
405k
    while (to_remove_head != nullptr) {
403
197k
        LRUHandle* next = to_remove_head->next;
404
197k
        to_remove_head->free();
405
197k
        to_remove_head = next;
406
197k
    }
407
408
207k
    return reinterpret_cast<Cache::Handle*>(e);
409
207k
}
410
411
9
void LRUCache::erase(const CacheKey& key, uint32_t hash) {
412
9
    LRUHandle* e = nullptr;
413
9
    bool last_ref = false;
414
9
    {
415
9
        std::lock_guard l(_mutex);
416
9
        e = _table.remove(key, hash);
417
9
        if (e != nullptr) {
418
2
            last_ref = _unref(e);
419
2
            if (last_ref) {
420
1
                _usage -= e->total_size;
421
1
                if (e->in_cache) {
422
                    // locate in free list
423
1
                    _lru_remove(e);
424
1
                }
425
1
            }
426
2
            e->in_cache = false;
427
2
        }
428
9
    }
429
    // free handle out of mutex, when last_ref is true, e must not be nullptr
430
9
    if (last_ref) {
431
1
        e->free();
432
1
    }
433
9
}
434
435
8.39k
PrunedInfo LRUCache::prune() {
436
8.39k
    LRUHandle* to_remove_head = nullptr;
437
8.39k
    {
438
8.39k
        std::lock_guard l(_mutex);
439
9.09k
        while (_lru_normal.next != &_lru_normal) {
440
698
            LRUHandle* old = _lru_normal.next;
441
698
            _evict_one_entry(old);
442
698
            old->next = to_remove_head;
443
698
            to_remove_head = old;
444
698
        }
445
8.39k
        while (_lru_durable.next != &_lru_durable) {
446
4
            LRUHandle* old = _lru_durable.next;
447
4
            _evict_one_entry(old);
448
4
            old->next = to_remove_head;
449
4
            to_remove_head = old;
450
4
        }
451
8.39k
    }
452
8.39k
    int64_t pruned_count = 0;
453
8.39k
    int64_t pruned_size = 0;
454
9.09k
    while (to_remove_head != nullptr) {
455
702
        ++pruned_count;
456
702
        pruned_size += to_remove_head->total_size;
457
702
        LRUHandle* next = to_remove_head->next;
458
702
        to_remove_head->free();
459
702
        to_remove_head = next;
460
702
    }
461
8.39k
    return {pruned_count, pruned_size};
462
8.39k
}
463
464
8
PrunedInfo LRUCache::prune_if(CachePrunePredicate pred, bool lazy_mode) {
465
8
    LRUHandle* to_remove_head = nullptr;
466
8
    {
467
8
        std::lock_guard l(_mutex);
468
8
        LRUHandle* p = _lru_normal.next;
469
23
        while (p != &_lru_normal) {
470
18
            LRUHandle* next = p->next;
471
18
            if (pred(p)) {
472
10
                _evict_one_entry(p);
473
10
                p->next = to_remove_head;
474
10
                to_remove_head = p;
475
10
            } else if (lazy_mode) {
476
3
                break;
477
3
            }
478
15
            p = next;
479
15
        }
480
481
8
        p = _lru_durable.next;
482
15
        while (p != &_lru_durable) {
483
10
            LRUHandle* next = p->next;
484
10
            if (pred(p)) {
485
2
                _evict_one_entry(p);
486
2
                p->next = to_remove_head;
487
2
                to_remove_head = p;
488
8
            } else if (lazy_mode) {
489
3
                break;
490
3
            }
491
7
            p = next;
492
7
        }
493
8
    }
494
8
    int64_t pruned_count = 0;
495
8
    int64_t pruned_size = 0;
496
20
    while (to_remove_head != nullptr) {
497
12
        ++pruned_count;
498
12
        pruned_size += to_remove_head->total_size;
499
12
        LRUHandle* next = to_remove_head->next;
500
12
        to_remove_head->free();
501
12
        to_remove_head = next;
502
12
    }
503
8
    return {pruned_count, pruned_size};
504
8
}
505
506
4
void LRUCache::set_cache_value_time_extractor(CacheValueTimeExtractor cache_value_time_extractor) {
507
4
    _cache_value_time_extractor = cache_value_time_extractor;
508
4
}
509
510
4
void LRUCache::set_cache_value_check_timestamp(bool cache_value_check_timestamp) {
511
4
    _cache_value_check_timestamp = cache_value_check_timestamp;
512
4
}
513
514
449k
inline uint32_t ShardedLRUCache::_hash_slice(const CacheKey& s) {
515
449k
    return s.hash(s.data(), s.size(), 0);
516
449k
}
517
518
ShardedLRUCache::ShardedLRUCache(const std::string& name, size_t total_capacity, LRUCacheType type,
519
                                 uint32_t num_shards, uint32_t total_element_count_capacity)
520
        : _name(name),
521
          _num_shard_bits(Bits::FindLSBSetNonZero(num_shards)),
522
          _num_shards(num_shards),
523
          _shards(nullptr),
524
          _last_id(1),
525
278
          _total_capacity(total_capacity) {
526
278
    CHECK(num_shards > 0) << "num_shards cannot be 0";
527
278
    CHECK_EQ((num_shards & (num_shards - 1)), 0)
528
0
            << "num_shards should be power of two, but got " << num_shards;
529
530
278
    const size_t per_shard = (total_capacity + (_num_shards - 1)) / _num_shards;
531
278
    const size_t per_shard_element_count_capacity =
532
278
            (total_element_count_capacity + (_num_shards - 1)) / _num_shards;
533
278
    LRUCache** shards = new (std::nothrow) LRUCache*[_num_shards];
534
9.21k
    for (int s = 0; s < _num_shards; s++) {
535
8.93k
        shards[s] = new LRUCache(type);
536
8.93k
        shards[s]->set_capacity(per_shard);
537
8.93k
        shards[s]->set_element_count_capacity(per_shard_element_count_capacity);
538
8.93k
    }
539
278
    _shards = shards;
540
541
278
    _entity = DorisMetrics::instance()->metric_registry()->register_entity(
542
278
            std::string("lru_cache:") + name, {{"name", name}});
543
278
    _entity->register_hook(name, std::bind(&ShardedLRUCache::update_cache_metrics, this));
544
278
    INT_GAUGE_METRIC_REGISTER(_entity, cache_capacity);
545
278
    INT_GAUGE_METRIC_REGISTER(_entity, cache_usage);
546
278
    INT_GAUGE_METRIC_REGISTER(_entity, cache_element_count);
547
278
    INT_DOUBLE_METRIC_REGISTER(_entity, cache_usage_ratio);
548
278
    INT_ATOMIC_COUNTER_METRIC_REGISTER(_entity, cache_lookup_count);
549
278
    INT_ATOMIC_COUNTER_METRIC_REGISTER(_entity, cache_hit_count);
550
278
    INT_DOUBLE_METRIC_REGISTER(_entity, cache_hit_ratio);
551
552
278
    _hit_count_bvar.reset(new bvar::Adder<uint64_t>("doris_cache", _name));
553
278
    _hit_count_per_second.reset(new bvar::PerSecond<bvar::Adder<uint64_t>>(
554
278
            "doris_cache", _name + "_persecond", _hit_count_bvar.get(), 60));
555
278
    _lookup_count_bvar.reset(new bvar::Adder<uint64_t>("doris_cache", _name));
556
278
    _lookup_count_per_second.reset(new bvar::PerSecond<bvar::Adder<uint64_t>>(
557
278
            "doris_cache", _name + "_persecond", _lookup_count_bvar.get(), 60));
558
278
}
559
560
ShardedLRUCache::ShardedLRUCache(const std::string& name, size_t total_capacity, LRUCacheType type,
561
                                 uint32_t num_shards,
562
                                 CacheValueTimeExtractor cache_value_time_extractor,
563
                                 bool cache_value_check_timestamp,
564
                                 uint32_t total_element_count_capacity)
565
4
        : ShardedLRUCache(name, total_capacity, type, num_shards, total_element_count_capacity) {
566
8
    for (int s = 0; s < _num_shards; s++) {
567
4
        _shards[s]->set_cache_value_time_extractor(cache_value_time_extractor);
568
4
        _shards[s]->set_cache_value_check_timestamp(cache_value_check_timestamp);
569
4
    }
570
4
}
571
572
268
ShardedLRUCache::~ShardedLRUCache() {
573
268
    _entity->deregister_hook(_name);
574
268
    DorisMetrics::instance()->metric_registry()->deregister_entity(_entity);
575
268
    if (_shards) {
576
8.65k
        for (int s = 0; s < _num_shards; s++) {
577
8.38k
            delete _shards[s];
578
8.38k
        }
579
268
        delete[] _shards;
580
268
    }
581
268
}
582
583
Cache::Handle* ShardedLRUCache::insert(const CacheKey& key, void* value, size_t charge,
584
207k
                                       CachePriority priority) {
585
207k
    const uint32_t hash = _hash_slice(key);
586
207k
    return _shards[_shard(hash)]->insert(key, hash, value, charge, priority);
587
207k
}
588
589
241k
Cache::Handle* ShardedLRUCache::lookup(const CacheKey& key) {
590
241k
    const uint32_t hash = _hash_slice(key);
591
241k
    return _shards[_shard(hash)]->lookup(key, hash);
592
241k
}
593
594
433k
void ShardedLRUCache::release(Handle* handle) {
595
433k
    LRUHandle* h = reinterpret_cast<LRUHandle*>(handle);
596
433k
    _shards[_shard(h->hash)]->release(handle);
597
433k
}
598
599
9
void ShardedLRUCache::erase(const CacheKey& key) {
600
9
    const uint32_t hash = _hash_slice(key);
601
9
    _shards[_shard(hash)]->erase(key, hash);
602
9
}
603
604
230k
void* ShardedLRUCache::value(Handle* handle) {
605
230k
    return reinterpret_cast<LRUHandle*>(handle)->value;
606
230k
}
607
608
2
uint64_t ShardedLRUCache::new_id() {
609
2
    return _last_id.fetch_add(1, std::memory_order_relaxed);
610
2
}
611
612
0
PrunedInfo ShardedLRUCache::prune() {
613
0
    PrunedInfo pruned_info;
614
0
    for (int s = 0; s < _num_shards; s++) {
615
0
        PrunedInfo info = _shards[s]->prune();
616
0
        pruned_info.pruned_count += info.pruned_count;
617
0
        pruned_info.pruned_size += info.pruned_size;
618
0
    }
619
0
    return pruned_info;
620
0
}
621
622
0
PrunedInfo ShardedLRUCache::prune_if(CachePrunePredicate pred, bool lazy_mode) {
623
0
    PrunedInfo pruned_info;
624
0
    for (int s = 0; s < _num_shards; s++) {
625
0
        PrunedInfo info = _shards[s]->prune_if(pred, lazy_mode);
626
0
        pruned_info.pruned_count += info.pruned_count;
627
0
        pruned_info.pruned_size += info.pruned_size;
628
0
    }
629
0
    return pruned_info;
630
0
}
631
632
0
int64_t ShardedLRUCache::get_usage() {
633
0
    size_t total_usage = 0;
634
0
    for (int i = 0; i < _num_shards; i++) {
635
0
        total_usage += _shards[i]->get_usage();
636
0
    }
637
0
    return total_usage;
638
0
}
639
640
0
void ShardedLRUCache::update_cache_metrics() const {
641
0
    size_t total_capacity = 0;
642
0
    size_t total_usage = 0;
643
0
    size_t total_lookup_count = 0;
644
0
    size_t total_hit_count = 0;
645
0
    size_t total_element_count = 0;
646
0
    for (int i = 0; i < _num_shards; i++) {
647
0
        total_capacity += _shards[i]->get_capacity();
648
0
        total_usage += _shards[i]->get_usage();
649
0
        total_lookup_count += _shards[i]->get_lookup_count();
650
0
        total_hit_count += _shards[i]->get_hit_count();
651
0
        total_element_count += _shards[i]->get_element_count();
652
0
    }
653
654
0
    cache_capacity->set_value(total_capacity);
655
0
    cache_usage->set_value(total_usage);
656
0
    cache_element_count->set_value(total_element_count);
657
0
    cache_lookup_count->set_value(total_lookup_count);
658
0
    cache_hit_count->set_value(total_hit_count);
659
0
    cache_usage_ratio->set_value(total_capacity == 0 ? 0 : ((double)total_usage / total_capacity));
660
0
    cache_hit_ratio->set_value(
661
0
            total_lookup_count == 0 ? 0 : ((double)total_hit_count / total_lookup_count));
662
0
}
663
664
Cache::Handle* DummyLRUCache::insert(const CacheKey& key, void* value, size_t charge,
665
176
                                     CachePriority priority) {
666
176
    size_t handle_size = sizeof(LRUHandle);
667
176
    auto* e = reinterpret_cast<LRUHandle*>(malloc(handle_size));
668
176
    e->value = value;
669
176
    e->charge = charge;
670
176
    e->key_length = 0;
671
176
    e->total_size = 0;
672
176
    e->hash = 0;
673
176
    e->refs = 1; // only one for the returned handle
674
176
    e->next = e->prev = nullptr;
675
176
    e->in_cache = false;
676
176
    return reinterpret_cast<Cache::Handle*>(e);
677
176
}
678
679
176
void DummyLRUCache::release(Cache::Handle* handle) {
680
176
    if (handle == nullptr) {
681
0
        return;
682
0
    }
683
176
    auto* e = reinterpret_cast<LRUHandle*>(handle);
684
176
    e->free();
685
176
}
686
687
0
void* DummyLRUCache::value(Handle* handle) {
688
0
    return reinterpret_cast<LRUHandle*>(handle)->value;
689
0
}
690
691
} // namespace doris