Coverage Report

Created: 2025-04-15 14:42

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