risingwave_hummock_sdk/
sstable_info.rs

1// Copyright 2025 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::mem::size_of;
16use std::ops::Deref;
17use std::sync::Arc;
18
19use risingwave_pb::hummock::{PbBloomFilterType, PbKeyRange, PbSstableInfo};
20
21use crate::key_range::KeyRange;
22use crate::version::{ObjectIdReader, SstableIdReader};
23use crate::{HummockSstableId, HummockSstableObjectId};
24
25#[derive(Debug, PartialEq, Clone, Default)]
26pub struct SstableInfoInner {
27    pub object_id: u64,
28    pub sst_id: u64,
29    pub key_range: KeyRange,
30    pub file_size: u64,
31    pub table_ids: Vec<u32>,
32    pub meta_offset: u64,
33    pub stale_key_count: u64,
34    pub total_key_count: u64,
35    pub min_epoch: u64,
36    pub max_epoch: u64,
37    pub uncompressed_file_size: u64,
38    pub range_tombstone_count: u64,
39    pub bloom_filter_kind: PbBloomFilterType,
40    pub sst_size: u64,
41}
42
43impl SstableInfoInner {
44    pub fn estimated_encode_len(&self) -> usize {
45        let mut basic = size_of::<u64>() // object_id
46            + size_of::<u64>() // sstable_id
47            + size_of::<u64>() // file_size
48            + self.table_ids.len() * size_of::<u32>() // table_ids
49            + size_of::<u64>() // meta_offset
50            + size_of::<u64>() // stale_key_count
51            + size_of::<u64>() // total_key_count
52            + size_of::<u64>() // min_epoch
53            + size_of::<u64>() // max_epoch
54            + size_of::<u64>() // uncompressed_file_size
55            + size_of::<u64>() // range_tombstone_count
56            + size_of::<u32>() // bloom_filter_kind
57            + size_of::<u64>(); // sst_size
58        basic += self.key_range.left.len() + self.key_range.right.len() + size_of::<bool>();
59
60        basic
61    }
62
63    pub fn to_protobuf(&self) -> PbSstableInfo {
64        self.into()
65    }
66}
67
68impl From<PbSstableInfo> for SstableInfoInner {
69    fn from(pb_sstable_info: PbSstableInfo) -> Self {
70        assert!(pb_sstable_info.table_ids.is_sorted());
71        Self {
72            object_id: pb_sstable_info.object_id,
73            sst_id: pb_sstable_info.sst_id,
74            key_range: {
75                // Due to the stripped key range, the key range may be `None`
76                if let Some(pb_keyrange) = pb_sstable_info.key_range {
77                    KeyRange {
78                        left: pb_keyrange.left.into(),
79                        right: pb_keyrange.right.into(),
80                        right_exclusive: pb_keyrange.right_exclusive,
81                    }
82                } else {
83                    KeyRange::inf()
84                }
85            },
86            file_size: pb_sstable_info.file_size,
87            table_ids: pb_sstable_info.table_ids.clone(),
88            meta_offset: pb_sstable_info.meta_offset,
89            stale_key_count: pb_sstable_info.stale_key_count,
90            total_key_count: pb_sstable_info.total_key_count,
91            min_epoch: pb_sstable_info.min_epoch,
92            max_epoch: pb_sstable_info.max_epoch,
93            uncompressed_file_size: pb_sstable_info.uncompressed_file_size,
94            range_tombstone_count: pb_sstable_info.range_tombstone_count,
95            bloom_filter_kind: PbBloomFilterType::try_from(pb_sstable_info.bloom_filter_kind)
96                .unwrap(),
97            sst_size: if pb_sstable_info.sst_size == 0 {
98                pb_sstable_info.file_size
99            } else {
100                pb_sstable_info.sst_size
101            },
102        }
103    }
104}
105
106impl From<&PbSstableInfo> for SstableInfoInner {
107    fn from(pb_sstable_info: &PbSstableInfo) -> Self {
108        assert!(pb_sstable_info.table_ids.is_sorted());
109        Self {
110            object_id: pb_sstable_info.object_id,
111            sst_id: pb_sstable_info.sst_id,
112            key_range: {
113                if let Some(pb_keyrange) = &pb_sstable_info.key_range {
114                    KeyRange {
115                        left: pb_keyrange.left.clone().into(),
116                        right: pb_keyrange.right.clone().into(),
117                        right_exclusive: pb_keyrange.right_exclusive,
118                    }
119                } else {
120                    KeyRange::inf()
121                }
122            },
123            file_size: pb_sstable_info.file_size,
124            table_ids: pb_sstable_info.table_ids.clone(),
125            meta_offset: pb_sstable_info.meta_offset,
126            stale_key_count: pb_sstable_info.stale_key_count,
127            total_key_count: pb_sstable_info.total_key_count,
128            min_epoch: pb_sstable_info.min_epoch,
129            max_epoch: pb_sstable_info.max_epoch,
130            uncompressed_file_size: pb_sstable_info.uncompressed_file_size,
131            range_tombstone_count: pb_sstable_info.range_tombstone_count,
132            bloom_filter_kind: PbBloomFilterType::try_from(pb_sstable_info.bloom_filter_kind)
133                .unwrap(),
134            sst_size: if pb_sstable_info.sst_size == 0 {
135                pb_sstable_info.file_size
136            } else {
137                pb_sstable_info.sst_size
138            },
139        }
140    }
141}
142
143impl From<SstableInfoInner> for PbSstableInfo {
144    fn from(sstable_info: SstableInfoInner) -> Self {
145        assert!(sstable_info.table_ids.is_sorted());
146        PbSstableInfo {
147            object_id: sstable_info.object_id,
148            sst_id: sstable_info.sst_id,
149            key_range: {
150                let keyrange = sstable_info.key_range;
151                if keyrange.inf_key_range() {
152                    // For empty key range, we don't need to encode it
153                    // Timetravel will use the default key range to stripped the PbSstableInfo
154                    // Note: If new fields are added, using Default to implement stripped may not work, resulting in an increase in encode size.
155                    None
156                } else {
157                    let pb_key_range = PbKeyRange {
158                        left: keyrange.left.into(),
159                        right: keyrange.right.into(),
160                        right_exclusive: keyrange.right_exclusive,
161                    };
162                    Some(pb_key_range)
163                }
164            },
165
166            file_size: sstable_info.file_size,
167            table_ids: sstable_info.table_ids.clone(),
168            meta_offset: sstable_info.meta_offset,
169            stale_key_count: sstable_info.stale_key_count,
170            total_key_count: sstable_info.total_key_count,
171            min_epoch: sstable_info.min_epoch,
172            max_epoch: sstable_info.max_epoch,
173            uncompressed_file_size: sstable_info.uncompressed_file_size,
174            range_tombstone_count: sstable_info.range_tombstone_count,
175            bloom_filter_kind: sstable_info.bloom_filter_kind.into(),
176            sst_size: sstable_info.sst_size,
177        }
178    }
179}
180
181impl From<&SstableInfoInner> for PbSstableInfo {
182    fn from(sstable_info: &SstableInfoInner) -> Self {
183        assert!(sstable_info.table_ids.is_sorted());
184        PbSstableInfo {
185            object_id: sstable_info.object_id,
186            sst_id: sstable_info.sst_id,
187            key_range: {
188                let keyrange = &sstable_info.key_range;
189                if keyrange.inf_key_range() {
190                    None
191                } else {
192                    let pb_key_range = PbKeyRange {
193                        left: keyrange.left.to_vec(),
194                        right: keyrange.right.to_vec(),
195                        right_exclusive: keyrange.right_exclusive,
196                    };
197                    Some(pb_key_range)
198                }
199            },
200
201            file_size: sstable_info.file_size,
202            table_ids: sstable_info.table_ids.clone(),
203            meta_offset: sstable_info.meta_offset,
204            stale_key_count: sstable_info.stale_key_count,
205            total_key_count: sstable_info.total_key_count,
206            min_epoch: sstable_info.min_epoch,
207            max_epoch: sstable_info.max_epoch,
208            uncompressed_file_size: sstable_info.uncompressed_file_size,
209            range_tombstone_count: sstable_info.range_tombstone_count,
210            bloom_filter_kind: sstable_info.bloom_filter_kind.into(),
211            sst_size: sstable_info.sst_size,
212        }
213    }
214}
215
216impl SstableInfo {
217    pub fn remove_key_range(&mut self) {
218        let mut sst = self.get_inner();
219        sst.key_range = KeyRange::default();
220        *self = sst.into()
221    }
222}
223
224impl SstableIdReader for SstableInfoInner {
225    fn sst_id(&self) -> HummockSstableId {
226        self.sst_id
227    }
228}
229
230impl ObjectIdReader for SstableInfoInner {
231    fn object_id(&self) -> HummockSstableObjectId {
232        self.object_id
233    }
234}
235
236#[derive(Debug, PartialEq, Clone, Default)]
237pub struct SstableInfo(Arc<SstableInfoInner>);
238
239impl From<&PbSstableInfo> for SstableInfo {
240    fn from(s: &PbSstableInfo) -> Self {
241        SstableInfo(SstableInfoInner::from(s).into())
242    }
243}
244
245impl From<PbSstableInfo> for SstableInfo {
246    fn from(s: PbSstableInfo) -> Self {
247        SstableInfo(SstableInfoInner::from(s).into())
248    }
249}
250
251impl From<SstableInfo> for PbSstableInfo {
252    fn from(s: SstableInfo) -> Self {
253        (&s).into()
254    }
255}
256
257impl From<SstableInfoInner> for SstableInfo {
258    fn from(s: SstableInfoInner) -> Self {
259        Self(s.into())
260    }
261}
262
263impl From<&SstableInfo> for PbSstableInfo {
264    fn from(s: &SstableInfo) -> Self {
265        s.0.as_ref().into()
266    }
267}
268
269impl Deref for SstableInfo {
270    type Target = SstableInfoInner;
271
272    fn deref(&self) -> &Self::Target {
273        &self.0
274    }
275}
276
277impl SstableInfo {
278    pub fn get_inner(&self) -> SstableInfoInner {
279        (*self.0).clone()
280    }
281}
282
283impl SstableIdReader for SstableInfo {
284    fn sst_id(&self) -> HummockSstableId {
285        self.sst_id
286    }
287}
288
289impl ObjectIdReader for SstableInfo {
290    fn object_id(&self) -> HummockSstableObjectId {
291        self.object_id
292    }
293}