Coverage Report

Created: 2024-11-21 23:45

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