risingwave_storage/hummock/event_handler/
refiller.rs1use std::collections::{HashMap, HashSet};
16use std::sync::Arc;
17use std::time::Duration;
18
19use risingwave_common::bitmap::Bitmap;
20use risingwave_common::config::streaming::CacheRefillPolicy;
21use risingwave_common::license::Feature;
22use risingwave_hummock_sdk::compaction_group::hummock_version_ext::SstDeltaInfo;
23use risingwave_pb::id::TableId;
24use thiserror_ext::AsReport;
25use tokio::sync::Semaphore;
26use tokio::task::JoinHandle;
27
28use crate::hummock::SstableStoreRef;
29use crate::hummock::table_cache_metadata::TableCacheMetadata;
30pub use crate::monitor::{CacheRefillMetrics, GLOBAL_CACHE_REFILL_METRICS};
31use crate::opts::StorageOpts;
32
33mod foyer;
35
36#[derive(Debug)]
37pub struct CacheRefillConfig {
38 pub timeout: Duration,
40
41 pub data_refill_levels: HashSet<u32>,
43
44 pub meta_refill_concurrency: usize,
46
47 pub concurrency: usize,
49
50 pub unit: usize,
52
53 pub threshold: f64,
57
58 pub skip_recent_filter: bool,
60
61 pub skip_inheritance_filter: bool,
63}
64
65impl CacheRefillConfig {
66 pub fn from_storage_opts(options: &StorageOpts) -> Self {
67 let data_refill_levels = match Feature::ElasticDiskCache.check_available() {
68 Ok(_) => options
69 .cache_refill_data_refill_levels
70 .iter()
71 .copied()
72 .collect(),
73 Err(e) => {
74 tracing::warn!(error = %e.as_report(), "ElasticDiskCache is not available.");
75 HashSet::new()
76 }
77 };
78
79 Self {
80 timeout: Duration::from_millis(options.cache_refill_timeout_ms),
81 data_refill_levels,
82 concurrency: options.cache_refill_concurrency,
83 meta_refill_concurrency: options.cache_refill_meta_refill_concurrency,
84 unit: options.cache_refill_unit,
85 threshold: options.cache_refill_threshold,
86 skip_recent_filter: options.cache_refill_skip_recent_filter,
87 skip_inheritance_filter: options.cache_refill_skip_inheritance_filter,
88 }
89 }
90}
91
92#[cfg(test)]
93type SpawnRefillTask =
94 Arc<dyn Fn(Vec<SstDeltaInfo>, CacheRefillContext) -> JoinHandle<()> + Send + Sync + 'static>;
95
96pub type TableCacheRefillContextMap = HashMap<TableId, TableCacheRefillContext>;
97
98#[derive(Clone)]
101pub struct TableCacheRefillContext {
102 pub streaming_vnode_bitmap: Option<Bitmap>,
104 pub serving_vnode_bitmap: Option<Bitmap>,
106 pub policy: CacheRefillPolicy,
108}
109
110#[derive(Clone)]
112pub struct TableCacheRefillMonitorSnapshot {
113 pub contexts: TableCacheRefillContextMap,
114 pub policies: HashMap<TableId, CacheRefillPolicy>,
115 pub default_policy: CacheRefillPolicy,
116 pub streaming_table_vnode_mapping: HashMap<TableId, Bitmap>,
117 pub serving_table_vnode_mapping: HashMap<TableId, Bitmap>,
118}
119
120pub(crate) struct CacheRefiller {
122 #[cfg(test)]
123 spawn_refill_task: Option<SpawnRefillTask>,
124
125 config: Arc<CacheRefillConfig>,
126 meta_refill_concurrency: Option<Arc<Semaphore>>,
127 concurrency: Arc<Semaphore>,
128 sstable_store: SstableStoreRef,
129}
130
131impl CacheRefiller {
132 pub(crate) fn new(config: CacheRefillConfig, sstable_store: SstableStoreRef) -> Self {
133 let config = Arc::new(config);
134 let concurrency = Arc::new(Semaphore::new(config.concurrency));
135 let meta_refill_concurrency = if config.meta_refill_concurrency == 0 {
136 None
137 } else {
138 Some(Arc::new(Semaphore::new(config.meta_refill_concurrency)))
139 };
140 Self {
141 #[cfg(test)]
142 spawn_refill_task: None,
143 config,
144 meta_refill_concurrency,
145 concurrency,
146 sstable_store,
147 }
148 }
149
150 #[cfg(test)]
151 pub(super) fn with_foyer_spawn_task(mut self, spawn: SpawnRefillTask) -> Self {
152 self.spawn_refill_task = Some(spawn);
153 self
154 }
155
156 pub(crate) fn start_cache_refill(
157 &self,
158 mut deltas: Vec<SstDeltaInfo>,
159 metadata: &TableCacheMetadata,
160 ) -> JoinHandle<()> {
161 for delta in &mut deltas {
162 let for_serving = metadata.role.for_serving();
163 let for_streaming =
166 metadata.role.for_streaming() && !delta.delete_sst_object_ids.is_empty();
167
168 if !for_serving && !for_streaming {
169 delta.insert_sst_infos.clear();
170 continue;
171 }
172
173 delta.insert_sst_infos.retain(|sst| {
178 sst.table_ids.iter().any(|table_id| {
179 let policy = metadata
182 .table_cache_refill_policies
183 .get(table_id)
184 .copied()
185 .unwrap_or(metadata.default_policy);
186
187 match policy {
190 CacheRefillPolicy::Enabled => for_streaming || for_serving,
191 CacheRefillPolicy::Disabled => false,
192 CacheRefillPolicy::Streaming => {
193 for_streaming
194 && metadata
195 .streaming_table_vnode_mapping
196 .contains_key(table_id)
197 }
198 CacheRefillPolicy::Serving => {
199 for_serving
200 && metadata.serving_table_vnode_mapping.contains_key(table_id)
201 }
202 CacheRefillPolicy::Both => {
203 (for_streaming
204 && metadata
205 .streaming_table_vnode_mapping
206 .contains_key(table_id))
207 || (for_serving
208 && metadata.serving_table_vnode_mapping.contains_key(table_id))
209 }
210 }
211 })
212 });
213 }
214 let context = self.new_cache_refill_context(&deltas, metadata);
215 #[cfg(test)]
216 if let Some(spawn) = &self.spawn_refill_task {
217 return spawn(deltas, context);
218 }
219 foyer::spawn_refill_task(deltas, context)
220 }
221
222 fn new_cache_refill_context(
223 &self,
224 deltas: &[SstDeltaInfo],
225 metadata: &TableCacheMetadata,
226 ) -> CacheRefillContext {
227 let table_ids = deltas.iter().flat_map(|delta| {
228 delta
229 .insert_sst_infos
230 .iter()
231 .flat_map(|sst| sst.table_ids.iter().copied())
232 });
233 CacheRefillContext {
234 config: self.config.clone(),
235 meta_refill_concurrency: self.meta_refill_concurrency.clone(),
236 concurrency: self.concurrency.clone(),
237 sstable_store: self.sstable_store.clone(),
238 table_cache_refill_context_map: Arc::new(Self::table_cache_refill_contexts(
239 metadata, table_ids,
240 )),
241 }
242 }
243
244 fn table_cache_refill_contexts(
245 metadata: &TableCacheMetadata,
246 table_ids: impl IntoIterator<Item = TableId>,
247 ) -> TableCacheRefillContextMap {
248 let for_streaming = metadata.role.for_streaming();
249 let for_serving = metadata.role.for_serving();
250 let mut contexts = HashMap::new();
251 for table_id in table_ids {
252 if for_serving
253 && !for_streaming
254 && !metadata.serving_table_vnode_mapping.contains_key(&table_id)
255 {
256 continue;
257 }
258 contexts.entry(table_id).or_insert_with(|| {
259 let policy = metadata
260 .table_cache_refill_policies
261 .get(&table_id)
262 .copied()
263 .unwrap_or(metadata.default_policy);
264 let streaming_vnode_bitmap = (for_streaming && policy.is_streaming_scoped())
265 .then(|| {
266 metadata
267 .streaming_table_vnode_mapping
268 .get(&table_id)
269 .cloned()
270 })
271 .flatten();
272 let serving_vnode_bitmap = (for_serving
275 && (policy.is_serving_scoped() || policy.is_unscoped_enabled()))
276 .then(|| metadata.serving_table_vnode_mapping.get(&table_id).cloned())
277 .flatten();
278 TableCacheRefillContext {
279 streaming_vnode_bitmap,
280 serving_vnode_bitmap,
281 policy,
282 }
283 });
284 }
285 contexts
286 }
287
288 pub(crate) fn table_cache_refill_monitor_snapshot(
289 metadata: &TableCacheMetadata,
290 ) -> TableCacheRefillMonitorSnapshot {
291 let table_ids = metadata
292 .table_cache_refill_policies
293 .keys()
294 .chain(metadata.streaming_table_vnode_mapping.keys())
295 .chain(metadata.serving_table_vnode_mapping.keys())
296 .copied();
297 TableCacheRefillMonitorSnapshot {
298 contexts: Self::table_cache_refill_contexts(metadata, table_ids),
299 policies: metadata.table_cache_refill_policies.clone(),
300 default_policy: metadata.default_policy,
301 streaming_table_vnode_mapping: metadata.streaming_table_vnode_mapping.clone(),
302 serving_table_vnode_mapping: metadata.serving_table_vnode_mapping.clone(),
303 }
304 }
305}
306
307#[derive(Clone)]
308pub(crate) struct CacheRefillContext {
309 config: Arc<CacheRefillConfig>,
310 meta_refill_concurrency: Option<Arc<Semaphore>>,
311 concurrency: Arc<Semaphore>,
312 sstable_store: SstableStoreRef,
313 table_cache_refill_context_map: Arc<TableCacheRefillContextMap>,
314}