Coverage Report

Created: 2026-08-26 16:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/segment/segment_loader.h
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#pragma once
19
20
#include <butil/macros.h>
21
#include <glog/logging.h>
22
#include <stddef.h>
23
#include <stdint.h>
24
25
#include <atomic>
26
#include <memory>
27
#include <ostream>
28
#include <string>
29
#include <utility>
30
#include <vector>
31
32
#include "common/cast_set.h"
33
#include "common/status.h"
34
#include "runtime/memory/lru_cache_policy.h"
35
#include "storage/olap_common.h" // for rowset id
36
#include "storage/segment/segment.h"
37
#include "util/lru_cache.h"
38
#include "util/time.h"
39
40
namespace doris {
41
42
class SegmentCacheHandle;
43
class BetaRowset;
44
class RowsetMeta;
45
struct RowsetSegmentRef;
46
47
// SegmentLoader is used to load the Segment of BetaRowset.
48
// An LRUCache is encapsulated inside it, which is used to cache the opened segments.
49
// The caller should use the following method to load and obtain
50
// the segments of a specified rowset:
51
//
52
//  SegmentCacheHandle cache_handle;
53
//  RETURN_IF_ERROR(SegmentCache::instance()->load_segments(_rowset, &cache_handle));
54
//  for (auto& seg_ptr : cache_handle.value()->segments) {
55
//      ... visit segment ...
56
//  }
57
//
58
// Make sure that cache_handle is valid during the segment usage period.
59
using BetaRowsetSharedPtr = std::shared_ptr<BetaRowset>;
60
61
class SegmentCache : public LRUCachePolicy {
62
public:
63
    using LRUCachePolicy::insert;
64
    // The cache key or segment lru cache
65
    struct CacheKey {
66
        CacheKey(RowsetId rowset_id_, int64_t segment_id_)
67
1.63M
                : rowset_id(rowset_id_), segment_id(segment_id_) {}
68
        RowsetId rowset_id;
69
        int64_t segment_id;
70
71
        // Encode to a flat binary which can be used as LRUCache's key
72
1.69M
        [[nodiscard]] std::string encode() const {
73
1.69M
            return rowset_id.to_string() + std::to_string(segment_id);
74
1.69M
        }
75
    };
76
77
    // The cache value of segment lru cache.
78
    // Holding all opened segments of a rowset.
79
    class CacheValue : public LRUCacheValueBase {
80
    public:
81
60.7k
        CacheValue(segment_v2::SegmentSharedPtr segment_) : segment(std::move(segment_)) {}
82
83
        const segment_v2::SegmentSharedPtr segment;
84
    };
85
86
    SegmentCache(size_t memory_bytes_limit, size_t segment_num_limit)
87
8
            : LRUCachePolicy(CachePolicy::CacheType::SEGMENT_CACHE, memory_bytes_limit,
88
8
                             LRUCacheType::SIZE, config::tablet_rowset_stale_sweep_time_sec,
89
8
                             /*num shards*/ 64,
90
8
                             /*element count capacity */ cast_set<uint32_t>(segment_num_limit),
91
8
                             config::enable_segment_cache_prune, /*is lru-k*/ true) {}
92
93
    // Lookup the given segment in the cache.
94
    // If the segment is found, the cache entry will be written into handle.
95
    // Return true if entry is found, otherwise return false.
96
    bool lookup(const SegmentCache::CacheKey& key, SegmentCacheHandle* handle);
97
98
    // Insert a cache entry by key.
99
    // And the cache entry will be returned in handle.
100
    // This function is thread-safe.
101
    void insert(const SegmentCache::CacheKey& key, CacheValue& value, SegmentCacheHandle* handle);
102
103
    void erase(const SegmentCache::CacheKey& key);
104
};
105
106
class SegmentLoader {
107
public:
108
    static SegmentLoader* instance();
109
110
    // Create global instance of this class.
111
    // "capacity" is the capacity of lru cache.
112
    // TODO: Currently we use the number of rowset as the cache capacity.
113
    // That is, the limit of cache is the number of rowset.
114
    // This is because currently we cannot accurately estimate the memory occupied by a segment.
115
    // After the estimation of segment memory usage is provided later, it is recommended
116
    // to use Memory as the capacity limit of the cache.
117
118
8
    SegmentLoader(size_t memory_limit_bytes, size_t segment_num_count) {
119
8
        _segment_cache = std::make_unique<SegmentCache>(memory_limit_bytes, segment_num_count);
120
8
    }
121
122
    // Load segments of "rowset", return the "cache_handle" which contains segments.
123
    // If use_cache is true, it will be loaded from _cache.
124
    Status load_segments(const BetaRowsetSharedPtr& rowset, SegmentCacheHandle* cache_handle,
125
                         bool use_cache = false, bool need_load_pk_index_and_bf = false,
126
                         OlapReaderStatistics* index_load_stats = nullptr,
127
                         const io::IOContext* io_ctx = nullptr);
128
129
    // Load one segment of "rowset", return the "cache_handle" which contains segments.
130
    // If use_cache is true, it will be loaded from _cache.
131
    Status load_segment(const BetaRowsetSharedPtr& rowset, RowsetSegmentRef seg,
132
                        SegmentCacheHandle* cache_handle, bool use_cache = false,
133
                        bool need_load_pk_index_and_bf = false,
134
                        OlapReaderStatistics* index_load_stats = nullptr,
135
                        const io::IOContext* io_ctx = nullptr);
136
137
    void erase_segment(const SegmentCache::CacheKey& key);
138
139
    void erase_segments(const RowsetMeta& rowset_meta);
140
141
366
    int64_t cache_mem_usage() const {
142
#ifdef BE_TEST
143
        return _cache_mem_usage;
144
#else
145
366
        return _segment_cache->value_mem_consumption();
146
366
#endif
147
366
    }
148
149
private:
150
    SegmentLoader();
151
    std::unique_ptr<SegmentCache> _segment_cache;
152
    // Just used for BE UT
153
    int64_t _cache_mem_usage = 0;
154
};
155
156
// A handle for a single rowset from segment lru cache.
157
// The handle can ensure that the segment is valid
158
// and will not be closed while the holder of the handle is accessing the segment.
159
// The handle will automatically release the cache entry when it is destroyed.
160
// So the caller need to make sure the handle is valid in lifecycle.
161
class SegmentCacheHandle {
162
public:
163
1.59M
    SegmentCacheHandle() = default;
164
1.59M
    ~SegmentCacheHandle() = default;
165
166
1.57M
    void push_segment(LRUCachePolicy* cache, Cache::Handle* handle) {
167
1.57M
        segments.push_back(((SegmentCache::CacheValue*)cache->value(handle))->segment);
168
1.57M
        cache->release(handle);
169
1.57M
    }
170
171
11.1k
    void push_segment(segment_v2::SegmentSharedPtr segment) {
172
11.1k
        segments.push_back(std::move(segment));
173
11.1k
    }
174
175
5.47M
    std::vector<segment_v2::SegmentSharedPtr>& get_segments() { return segments; }
176
177
77.8k
    [[nodiscard]] bool is_inited() const { return _init; }
178
179
77.5k
    void set_inited() {
180
77.5k
        DCHECK(!_init);
181
77.5k
        _init = true;
182
77.5k
    }
183
184
1.51M
    segment_v2::SegmentSharedPtr pop_unhealthy_segment() {
185
1.51M
        if (segments.empty()) {
186
0
            return nullptr;
187
0
        }
188
1.51M
        segment_v2::SegmentSharedPtr last_segment = segments.back();
189
1.52M
        if (last_segment->healthy_status().ok()) {
190
1.52M
            return nullptr;
191
1.52M
        }
192
18.4E
        segments.pop_back();
193
18.4E
        return last_segment;
194
1.51M
    }
195
196
private:
197
    std::vector<segment_v2::SegmentSharedPtr> segments;
198
    bool _init {false};
199
200
    // Don't allow copy and assign
201
    DISALLOW_COPY_AND_ASSIGN(SegmentCacheHandle);
202
};
203
204
} // namespace doris