risingwave_storage/hummock/
mod.rs1use std::ops::Bound;
18use std::sync::Arc;
19
20use bytes::Bytes;
21use risingwave_hummock_sdk::key::{FullKey, TableKey, UserKeyRangeRef};
22use risingwave_hummock_sdk::sstable_info::SstableInfo;
23use risingwave_hummock_sdk::{HummockEpoch, *};
24
25pub mod block_cache;
26pub use block_cache::*;
27
28pub mod sstable;
29pub use sstable::*;
30
31pub mod compactor;
32mod error;
33pub mod hummock_meta_client;
34pub mod iterator;
35pub mod shared_buffer;
36pub mod sstable_store;
37#[cfg(any(test, feature = "test"))]
38pub mod test_utils;
39pub mod utils;
40pub use utils::MemoryLimiter;
41pub mod backup_reader;
42pub mod event_handler;
43pub mod local_version;
44pub mod observer_manager;
45pub mod store;
46pub use store::*;
47mod validator;
48pub mod value;
49pub mod write_limiter;
50
51pub mod recent_filter;
52pub use recent_filter::*;
53
54pub mod block_stream;
55mod time_travel_version_cache;
56
57pub(crate) mod vector;
58
59mod object_id_manager;
60pub use error::*;
61pub use object_id_manager::*;
62pub use risingwave_common::cache::{CacheableEntry, LookupResult, LruCache};
63pub use validator::*;
64use value::*;
65
66use self::iterator::HummockIterator;
67pub use self::sstable_store::*;
68use crate::mem_table::ImmutableMemtable;
69use crate::monitor::StoreLocalStatistic;
70use crate::store::ReadOptions;
71
72pub(in crate::hummock) struct IteratorStatsGuard<'a, TI: HummockIterator> {
73 iter: Option<TI>,
74 parent_stats: &'a mut StoreLocalStatistic,
75}
76
77impl<'a, TI: HummockIterator> IteratorStatsGuard<'a, TI> {
78 pub(in crate::hummock) fn new(iter: TI, parent_stats: &'a mut StoreLocalStatistic) -> Self {
79 Self {
80 iter: Some(iter),
81 parent_stats,
82 }
83 }
84
85 pub(in crate::hummock) fn iter(&self) -> &TI {
86 self.iter.as_ref().expect("iterator must be present")
87 }
88
89 pub(in crate::hummock) fn iter_mut(&mut self) -> &mut TI {
90 self.iter.as_mut().expect("iterator must be present")
91 }
92
93 fn collect(&mut self) {
94 if let Some(iter) = &self.iter {
95 iter.collect_local_statistic(self.parent_stats);
96 }
97 }
98
99 pub(in crate::hummock) fn handoff(mut self) -> TI {
100 self.iter.take().expect("iterator must be present")
101 }
102
103 pub(in crate::hummock) fn collect_and_take(mut self) -> TI {
104 self.collect();
105 self.iter.take().expect("iterator must be present")
106 }
107}
108
109impl<TI: HummockIterator> Drop for IteratorStatsGuard<'_, TI> {
110 fn drop(&mut self) {
111 self.collect();
112 }
113}
114
115pub async fn get_from_sstable_info(
116 sstable_store_ref: SstableStoreRef,
117 sstable_info: &SstableInfo,
118 full_key: FullKey<&[u8]>,
119 read_options: &ReadOptions,
120 dist_key_hash: Option<u64>,
121 local_stats: &mut StoreLocalStatistic,
122) -> HummockResult<Option<impl HummockIterator>> {
123 let sstable = sstable_store_ref.sstable(sstable_info, local_stats).await?;
124
125 if let Some(hash) = dist_key_hash
128 && !hit_sstable_filter(
129 &sstable,
130 &(
131 Bound::Included(full_key.user_key),
132 Bound::Included(full_key.user_key),
133 ),
134 hash,
135 local_stats,
136 )
137 {
138 return Ok(None);
139 }
140
141 let mut iterator_read_options = SstableIteratorReadOptions::from_read_options(read_options);
142 iterator_read_options.read_table_id = Some(full_key.user_key.table_id);
143
144 let mut iter = IteratorStatsGuard::new(
145 SstableIterator::create(
146 sstable,
147 sstable_store_ref.clone(),
148 Arc::new(iterator_read_options),
149 sstable_info,
150 ),
151 local_stats,
152 );
153 iter.iter_mut().seek(full_key).await?;
154 if !iter.iter().is_valid() {
156 return Ok(None);
157 }
158
159 let value = if iter.iter().key().user_key == full_key.user_key {
162 Some(iter.collect_and_take())
163 } else {
164 None
165 };
166
167 Ok(value)
168}
169
170pub fn hit_sstable_filter(
171 sstable_ref: &Sstable,
172 user_key_range: &UserKeyRangeRef<'_>,
173 prefix_hash: u64,
174 local_stats: &mut StoreLocalStatistic,
175) -> bool {
176 local_stats.bloom_filter_check_counts += 1;
177 let may_exist = sstable_ref.may_match_hash(user_key_range, prefix_hash);
178 if !may_exist {
179 local_stats.bloom_filter_true_negative_counts += 1;
180 }
181 may_exist
182}
183
184pub fn get_from_batch<'a>(
186 imm: &'a ImmutableMemtable,
187 table_key: TableKey<&[u8]>,
188 read_epoch: HummockEpoch,
189 read_options: &ReadOptions,
190 local_stats: &mut StoreLocalStatistic,
191) -> Option<(HummockValue<&'a Bytes>, EpochWithGap)> {
192 imm.get(table_key, read_epoch, read_options).inspect(|_| {
193 local_stats.get_shared_buffer_hit_counts += 1;
194 })
195}
196
197#[cfg(test)]
198mod tests {
199 use bytes::Bytes;
200 use risingwave_common::catalog::TableId;
201 use risingwave_common::hash::VirtualNode;
202 use risingwave_common::util::epoch::test_epoch;
203 use risingwave_hummock_sdk::EpochWithGap;
204 use risingwave_hummock_sdk::key::{FullKey, TableKey, UserKey};
205
206 use super::{CachePolicy, get_from_sstable_info};
207 use crate::hummock::iterator::test_utils::{iterator_test_key_of, mock_sstable_store};
208 use crate::hummock::test_utils::{
209 default_builder_opt_for_test, gen_test_sstable_info, gen_test_sstable_with_table_ids,
210 test_value_of,
211 };
212 use crate::hummock::value::HummockValue;
213 use crate::monitor::StoreLocalStatistic;
214 use crate::store::ReadOptions;
215
216 #[tokio::test]
217 async fn test_get_collects_stats_when_seek_passes_sst_end() {
218 let sstable_store = mock_sstable_store().await;
219 let sstable_info = gen_test_sstable_info(
220 default_builder_opt_for_test(),
221 1,
222 (0..10).map(|idx| {
223 (
224 iterator_test_key_of(idx),
225 HummockValue::put(format!("value_{idx}").into_bytes()),
226 )
227 }),
228 sstable_store.clone(),
229 )
230 .await;
231 let mut stats = StoreLocalStatistic::default();
232 let key = iterator_test_key_of(10);
233 let read_options = ReadOptions::default();
234
235 let result = get_from_sstable_info(
236 sstable_store,
237 &sstable_info,
238 key.to_ref(),
239 &read_options,
240 None,
241 &mut stats,
242 )
243 .await
244 .unwrap();
245
246 assert!(result.is_none());
247 drop(result);
248 assert_eq!(stats.cache_data_block_total, 1);
249 }
250
251 #[tokio::test]
252 async fn test_point_get_reads_only_requested_table_blocks() {
253 let sstable_store = mock_sstable_store().await;
254 let mut builder_options = default_builder_opt_for_test();
255 builder_options.block_capacity = 128;
256
257 let test_user_key = |table_id, key: &str| {
258 UserKey::new(
259 TableId::new(table_id),
260 TableKey(Bytes::from(
261 [VirtualNode::ZERO.to_be_bytes().as_slice(), key.as_bytes()].concat(),
262 )),
263 )
264 };
265 let test_key = |table_id, key: &str| FullKey {
266 user_key: test_user_key(table_id, key),
267 epoch_with_gap: EpochWithGap::new_from_epoch(test_epoch(1)),
268 };
269 let kv_pairs = (1..=2).flat_map(|table_id| {
270 (0..8).map(move |idx| {
271 (
272 test_key(table_id, &format!("key_{idx:05}")),
273 HummockValue::put(Bytes::from(test_value_of(idx))),
274 )
275 })
276 });
277 let (sstable, sstable_info) = gen_test_sstable_with_table_ids(
278 builder_options,
279 10,
280 kv_pairs,
281 sstable_store.clone(),
282 vec![1, 2],
283 )
284 .await;
285 let table_2_block_start = sstable
286 .meta
287 .block_metas
288 .partition_point(|block_meta| block_meta.table_id() < TableId::new(2));
289 assert!(table_2_block_start > 0);
290
291 let full_key = test_key(2, "key");
294 let mut local_stats = StoreLocalStatistic::default();
295 let read_options = ReadOptions {
296 cache_policy: CachePolicy::Disable,
297 ..Default::default()
298 };
299
300 {
301 let result = get_from_sstable_info(
302 sstable_store,
303 &sstable_info,
304 full_key.to_ref(),
305 &read_options,
306 None,
307 &mut local_stats,
308 )
309 .await
310 .unwrap();
311 assert!(result.is_none());
312 }
313 assert_eq!(local_stats.cache_data_block_total, 1);
314 }
315}