Coverage Report

Created: 2025-07-06 19:09

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