1use std::cell::RefCell;
16use std::collections::{HashMap, HashSet, VecDeque};
17use std::pin::pin;
18use std::sync::atomic::AtomicUsize;
19use std::sync::atomic::Ordering::Relaxed;
20use std::sync::{Arc, LazyLock};
21use std::time::Duration;
22
23use arc_swap::ArcSwap;
24use await_tree::{InstrumentAwait, SpanExt};
25use futures::FutureExt;
26use itertools::Itertools;
27use parking_lot::RwLock;
28use prometheus::{Histogram, IntGauge, IntGaugeVec};
29use risingwave_common::bitmap::Bitmap;
30use risingwave_common::catalog::TableId;
31use risingwave_common::config::Role;
32use risingwave_common::config::streaming::CacheRefillPolicy;
33use risingwave_common::metrics::UintGauge;
34use risingwave_hummock_sdk::{HummockEpoch, SyncResult};
35use risingwave_pb::meta::table_cache_refill_policies::table_cache_refill_policy::PbCacheRefillPolicy;
36use risingwave_pb::meta::{PbServingTableVnodeMappings, PbTableRefillRuntimeConfig};
37use tokio::spawn;
38use tokio::sync::mpsc::error::SendError;
39use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel};
40use tokio::sync::oneshot;
41use tracing::{debug, error, info, trace, warn};
42
43use super::refiller::{CacheRefillConfig, CacheRefiller};
44use super::version_update::{VersionUpdateEvent, VersionUpdateQueue};
45use super::{LocalInstanceGuard, LocalInstanceId, ReadVersionMappingType};
46use crate::compaction_catalog_manager::CompactionCatalogManagerRef;
47use crate::hummock::compactor::{CompactorContext, await_tree_key, compact};
48use crate::hummock::event_handler::uploader::{
49 HummockUploader, SpawnUploadTask, SyncedData, UploadTaskOutput,
50};
51use crate::hummock::event_handler::{
52 HummockEvent, HummockObserverEvent, HummockReadVersionRef, HummockVersionUpdate,
53 ReadOnlyReadVersionMapping, ReadOnlyRwLockRef,
54};
55use crate::hummock::local_version::pinned_version::PinnedVersion;
56use crate::hummock::local_version::recent_versions::RecentVersions;
57use crate::hummock::store::version::{HummockReadVersion, StagingSstableInfo, VersionUpdate};
58use crate::hummock::table_cache_metadata::TableCacheMetadata;
59use crate::hummock::{HummockResult, MemoryLimiter, ObjectIdManager};
60use crate::mem_table::ImmutableMemtable;
61use crate::monitor::HummockStateStoreMetrics;
62use crate::opts::StorageOpts;
63
64#[derive(Clone)]
65pub(crate) struct BufferTracker {
66 flush_threshold: usize,
67 min_batch_flush_size: usize,
68 global_uploading_memory_limiter: Arc<MemoryLimiter>,
69 uploader_imm_size: UintGauge,
70 uploader_uploading_task_size: UintGauge,
71}
72
73impl BufferTracker {
74 pub fn from_storage_opts(config: &StorageOpts, metrics: &HummockStateStoreMetrics) -> Self {
75 let capacity = config.shared_buffer_capacity_mb * (1 << 20);
76 let flush_threshold = (capacity as f32 * config.shared_buffer_flush_ratio) as usize;
77 let min_batch_flush_size = config.shared_buffer_min_batch_flush_size_mb * (1 << 20);
78 assert!(
79 flush_threshold < capacity,
80 "flush_threshold {} should be less or equal to capacity {}",
81 flush_threshold,
82 capacity
83 );
84 Self {
85 flush_threshold,
86 min_batch_flush_size,
87 global_uploading_memory_limiter: Arc::new(MemoryLimiter::new(capacity as u64)),
88 uploader_imm_size: metrics.uploader_imm_size.clone(),
89 uploader_uploading_task_size: metrics.uploader_uploading_task_size.clone(),
90 }
91 }
92
93 #[cfg(test)]
94 fn for_test_with_config(flush_threshold: usize, min_batch_flush_size: usize) -> Self {
95 Self {
96 flush_threshold,
97 min_batch_flush_size,
98 ..Self::for_test()
99 }
100 }
101
102 #[cfg(test)]
103 pub fn for_test() -> Self {
104 Self::from_storage_opts(&StorageOpts::default(), &HummockStateStoreMetrics::unused())
105 }
106
107 pub fn get_memory_limiter(&self) -> &Arc<MemoryLimiter> {
108 &self.global_uploading_memory_limiter
109 }
110
111 pub fn global_upload_task_size(&self) -> &UintGauge {
112 &self.uploader_uploading_task_size
113 }
114
115 pub fn need_flush(&self) -> bool {
118 self.uploader_imm_size.get()
119 > self.flush_threshold as u64 + self.uploader_uploading_task_size.get()
120 }
121
122 pub fn need_more_flush(&self, curr_batch_flush_size: usize) -> bool {
123 curr_batch_flush_size < self.min_batch_flush_size || self.need_flush()
124 }
125
126 #[cfg(test)]
127 pub(crate) fn flush_threshold(&self) -> usize {
128 self.flush_threshold
129 }
130}
131
132#[derive(Clone)]
133pub struct HummockEventSender {
134 inner: UnboundedSender<HummockEvent>,
135 event_count: IntGaugeVec,
136}
137
138pub fn event_channel(event_count: IntGaugeVec) -> (HummockEventSender, HummockEventReceiver) {
139 let (tx, rx) = unbounded_channel();
140 (
141 HummockEventSender {
142 inner: tx,
143 event_count: event_count.clone(),
144 },
145 HummockEventReceiver {
146 inner: rx,
147 event_count,
148 },
149 )
150}
151
152impl HummockEventSender {
153 pub fn send(&self, event: HummockEvent) -> Result<(), SendError<HummockEvent>> {
154 let event_type = event.event_name();
155 self.inner.send(event)?;
156 get_event_pending_gauge(&self.event_count, event_type).inc();
157 Ok(())
158 }
159}
160
161pub struct HummockEventReceiver {
162 inner: UnboundedReceiver<HummockEvent>,
163 event_count: IntGaugeVec,
164}
165
166impl HummockEventReceiver {
167 async fn recv(&mut self) -> Option<HummockEvent> {
168 let event = self.inner.recv().await?;
169 let event_type = event.event_name();
170 get_event_pending_gauge(&self.event_count, event_type).dec();
171 Some(event)
172 }
173}
174
175thread_local! {
176 static EVENT_PENDING_GAUGE_CACHE: RefCell<HashMap<&'static str, IntGauge>> = RefCell::new(HashMap::new());
177}
178
179fn get_event_pending_gauge(event_count: &IntGaugeVec, event_type: &'static str) -> IntGauge {
180 EVENT_PENDING_GAUGE_CACHE.with(|cache| {
181 cache
182 .borrow_mut()
183 .entry(event_type)
184 .or_insert_with(|| event_count.with_label_values(&[event_type]))
185 .clone()
186 })
187}
188
189struct HummockEventHandlerMetrics {
190 event_handler_on_upload_finish_latency: Histogram,
191 event_handler_on_apply_version_update: Histogram,
192 event_handler_on_recv_version_update: Histogram,
193 event_handler_on_spiller: Histogram,
194 event_handler_on_apply_table_refill_runtime_config: Histogram,
195}
196
197fn table_cache_refill_policy_from_protobuf(
198 pb_policy: risingwave_pb::meta::table_cache_refill_policies::PbTableCacheRefillPolicy,
199) -> Option<(TableId, CacheRefillPolicy)> {
200 let table_id = TableId::from(pb_policy.table_id);
201 let policy = match PbCacheRefillPolicy::try_from(pb_policy.policy) {
202 Ok(policy) => CacheRefillPolicy::from_protobuf(policy)?,
203 Err(_) => {
204 tracing::warn!(
205 table_id = pb_policy.table_id,
206 policy = pb_policy.policy,
207 "ignore unknown table cache refill policy"
208 );
209 return None;
210 }
211 };
212 Some((table_id, policy))
213}
214
215fn serving_table_vnode_mappings_to_map(
216 mappings: PbServingTableVnodeMappings,
217) -> HashMap<TableId, Bitmap> {
218 mappings
219 .mappings
220 .into_iter()
221 .filter_map(|mapping| {
222 let Some(bitmap) = mapping.bitmap else {
223 tracing::warn!(
224 table_id = mapping.table_id,
225 "ignore serving table vnode mapping without bitmap"
226 );
227 return None;
228 };
229 Some((TableId::from(mapping.table_id), Bitmap::from(bitmap)))
230 })
231 .collect()
232}
233
234#[derive(Default)]
235struct LocalReadVersions {
236 by_instance: HashMap<LocalInstanceId, (TableId, HummockReadVersionRef)>,
237 by_table: HashMap<TableId, HashMap<LocalInstanceId, HummockReadVersionRef>>,
238}
239
240impl LocalReadVersions {
241 fn insert(
242 &mut self,
243 instance_id: LocalInstanceId,
244 table_id: TableId,
245 read_version: HummockReadVersionRef,
246 ) {
247 assert!(
248 !self.by_instance.contains_key(&instance_id),
249 "duplicated local read version instance_id {}",
250 instance_id
251 );
252 assert!(
253 !self
254 .by_table
255 .get(&table_id)
256 .is_some_and(|read_versions| read_versions.contains_key(&instance_id)),
257 "duplicated local read version table_id {} instance_id {}",
258 table_id,
259 instance_id
260 );
261 self.by_instance
262 .insert(instance_id, (table_id, read_version.clone()));
263 self.by_table
264 .entry(table_id)
265 .or_default()
266 .insert(instance_id, read_version);
267 }
268
269 fn remove(&mut self, instance_id: LocalInstanceId) -> (TableId, HummockReadVersionRef) {
270 let (table_id, read_version) = self.by_instance.remove(&instance_id).unwrap_or_else(|| {
271 panic!(
272 "DestroyHummockInstance nonexistent instance instance_id {}",
273 instance_id
274 )
275 });
276 let table_read_versions = self.by_table.get_mut(&table_id).unwrap_or_else(|| {
277 panic!(
278 "DestroyHummockInstance table_id {} instance_id {} fail in local mapping",
279 table_id, instance_id
280 )
281 });
282 let removed = table_read_versions.remove(&instance_id).unwrap_or_else(|| {
283 panic!(
284 "DestroyHummockInstance nonexistent instance in local mapping table_id {} instance_id {}",
285 table_id, instance_id
286 )
287 });
288 debug_assert!(Arc::ptr_eq(&read_version, &removed));
289 if table_read_versions.is_empty() {
290 self.by_table.remove(&table_id);
291 }
292 (table_id, read_version)
293 }
294
295 fn get(&self, instance_id: &LocalInstanceId) -> Option<&(TableId, HummockReadVersionRef)> {
296 self.by_instance.get(instance_id)
297 }
298
299 fn contains_instance(&self, instance_id: &LocalInstanceId) -> bool {
300 self.by_instance.contains_key(instance_id)
301 }
302
303 fn instance_ids(&self) -> impl Iterator<Item = LocalInstanceId> + '_ {
304 self.by_instance.keys().copied()
305 }
306
307 fn is_empty(&self) -> bool {
308 self.by_instance.is_empty() && self.by_table.is_empty()
309 }
310
311 fn table_ids(&self) -> impl Iterator<Item = TableId> + '_ {
312 self.by_table.keys().copied()
313 }
314
315 fn table_vnodes(&self, table_id: TableId) -> Option<Bitmap> {
316 let read_versions = self.by_table.get(&table_id)?;
317 let mut vnodes = None;
318 for read_version in read_versions.values() {
319 let read_version_vnodes = read_version.read().vnodes();
320 let current_vnodes =
321 vnodes.get_or_insert_with(|| Bitmap::zeros(read_version_vnodes.as_ref().len()));
322 *current_vnodes |= read_version_vnodes.as_ref();
323 }
324 vnodes
325 }
326}
327
328pub(crate) struct HummockEventHandler {
329 hummock_event_tx: HummockEventSender,
330 hummock_event_rx: HummockEventReceiver,
331 observer_event_rx: UnboundedReceiver<HummockObserverEvent>,
332 read_version_mapping: Arc<RwLock<ReadVersionMappingType>>,
333 local_read_versions: LocalReadVersions,
335
336 version_update_notifier_tx: Arc<tokio::sync::watch::Sender<PinnedVersion>>,
337 recent_versions: Arc<ArcSwap<RecentVersions>>,
338
339 uploader: HummockUploader,
340 refiller: CacheRefiller,
341 table_cache_metadata: TableCacheMetadata,
342 version_updates: VersionUpdateQueue,
343
344 last_instance_id: LocalInstanceId,
345
346 metrics: HummockEventHandlerMetrics,
347}
348
349async fn flush_imms(
350 payload: Vec<ImmutableMemtable>,
351 compactor_context: CompactorContext,
352 compaction_catalog_manager_ref: CompactionCatalogManagerRef,
353 object_id_manager: Arc<ObjectIdManager>,
354) -> HummockResult<UploadTaskOutput> {
355 compact(
356 compactor_context,
357 object_id_manager,
358 payload,
359 compaction_catalog_manager_ref,
360 )
361 .instrument_await("shared_buffer_compact".verbose())
362 .await
363}
364
365impl HummockEventHandler {
366 fn apply_table_refill_runtime_config(
369 metadata: &mut TableCacheMetadata,
370 config: PbTableRefillRuntimeConfig,
371 ) {
372 if let Some(policies) = config.table_cache_refill_policies {
373 let policies = policies
374 .table_policies
375 .into_iter()
376 .chain(policies.internal_table_policies)
377 .filter_map(table_cache_refill_policy_from_protobuf)
378 .collect();
379 metadata.table_cache_refill_policies = policies;
380 }
381
382 if let Some(mappings) = config.serving_table_vnode_mappings {
383 metadata.serving_table_vnode_mapping = serving_table_vnode_mappings_to_map(mappings);
384 }
385 }
386
387 pub fn new(
388 role: Role,
389 observer_event_rx: UnboundedReceiver<HummockObserverEvent>,
390 pinned_version: PinnedVersion,
391 compactor_context: CompactorContext,
392 compaction_catalog_manager_ref: CompactionCatalogManagerRef,
393 object_id_manager: Arc<ObjectIdManager>,
394 state_store_metrics: Arc<HummockStateStoreMetrics>,
395 ) -> Self {
396 let upload_compactor_context = compactor_context.clone();
397 let upload_task_latency = state_store_metrics.uploader_upload_task_latency.clone();
398 let wait_poll_latency = state_store_metrics.uploader_wait_poll_latency.clone();
399 let recent_versions = RecentVersions::new(
400 pinned_version,
401 compactor_context
402 .storage_opts
403 .max_cached_recent_versions_number,
404 state_store_metrics.clone(),
405 );
406 let buffer_tracker =
407 BufferTracker::from_storage_opts(&compactor_context.storage_opts, &state_store_metrics);
408 let refill_config = CacheRefillConfig::from_storage_opts(&compactor_context.storage_opts);
409 let metadata = TableCacheMetadata::new(
410 role,
411 compactor_context
412 .storage_opts
413 .cache_refill_table_cache_refill_default_policy,
414 );
415 let refiller = CacheRefiller::new(refill_config, compactor_context.sstable_store);
416 Self::new_inner(
417 observer_event_rx,
418 state_store_metrics,
419 recent_versions,
420 buffer_tracker,
421 Arc::new(move |payload, task_info| {
422 static NEXT_UPLOAD_TASK_ID: LazyLock<AtomicUsize> =
423 LazyLock::new(|| AtomicUsize::new(0));
424 let tree_root = upload_compactor_context.await_tree_reg.as_ref().map(|reg| {
425 let upload_task_id = NEXT_UPLOAD_TASK_ID.fetch_add(1, Relaxed);
426 reg.register(
427 await_tree_key::SpawnUploadTask { id: upload_task_id },
428 format!("Spawn Upload Task: {}", task_info),
429 )
430 });
431 let upload_task_latency = upload_task_latency.clone();
432 let wait_poll_latency = wait_poll_latency.clone();
433 let upload_compactor_context = upload_compactor_context.clone();
434 let compaction_catalog_manager_ref = compaction_catalog_manager_ref.clone();
435 let object_id_manager = object_id_manager.clone();
436 spawn({
437 let future = async move {
438 let _timer = upload_task_latency.start_timer();
439 let mut output = flush_imms(
440 payload
441 .into_values()
442 .flat_map(|imms| imms.into_iter())
443 .collect(),
444 upload_compactor_context.clone(),
445 compaction_catalog_manager_ref.clone(),
446 object_id_manager.clone(),
447 )
448 .await?;
449 assert!(
450 output
451 .wait_poll_timer
452 .replace(wait_poll_latency.start_timer())
453 .is_none(),
454 "should not set timer before"
455 );
456 Ok(output)
457 };
458 if let Some(tree_root) = tree_root {
459 tree_root.instrument(future).left_future()
460 } else {
461 future.right_future()
462 }
463 })
464 }),
465 refiller,
466 metadata,
467 )
468 }
469
470 fn new_inner(
471 observer_event_rx: UnboundedReceiver<HummockObserverEvent>,
472 state_store_metrics: Arc<HummockStateStoreMetrics>,
473 recent_versions: RecentVersions,
474 buffer_tracker: BufferTracker,
475 spawn_upload_task: SpawnUploadTask,
476 refiller: CacheRefiller,
477 table_cache_metadata: TableCacheMetadata,
478 ) -> Self {
479 let (hummock_event_tx, hummock_event_rx) =
480 event_channel(state_store_metrics.event_handler_pending_event.clone());
481 let (version_update_notifier_tx, _) =
482 tokio::sync::watch::channel(recent_versions.latest_version().clone());
483 let version_update_notifier_tx = Arc::new(version_update_notifier_tx);
484 let read_version_mapping = Arc::new(RwLock::new(HashMap::default()));
485
486 let metrics = HummockEventHandlerMetrics {
487 event_handler_on_upload_finish_latency: state_store_metrics
488 .event_handler_latency
489 .with_label_values(&["on_upload_finish"]),
490 event_handler_on_apply_version_update: state_store_metrics
491 .event_handler_latency
492 .with_label_values(&["apply_version"]),
493 event_handler_on_recv_version_update: state_store_metrics
494 .event_handler_latency
495 .with_label_values(&["recv_version_update"]),
496 event_handler_on_spiller: state_store_metrics
497 .event_handler_latency
498 .with_label_values(&["spiller"]),
499 event_handler_on_apply_table_refill_runtime_config: state_store_metrics
500 .event_handler_latency
501 .with_label_values(&["apply_table_refill_runtime_config"]),
502 };
503
504 let uploader = HummockUploader::new(
505 state_store_metrics,
506 recent_versions.latest_version().clone(),
507 spawn_upload_task,
508 buffer_tracker,
509 );
510 let version_updates = VersionUpdateQueue::new(recent_versions.latest_version().clone());
511
512 Self {
513 hummock_event_tx,
514 hummock_event_rx,
515 observer_event_rx,
516 version_update_notifier_tx,
517 recent_versions: Arc::new(ArcSwap::from_pointee(recent_versions)),
518 read_version_mapping,
519 local_read_versions: Default::default(),
520 uploader,
521 refiller,
522 table_cache_metadata,
523 version_updates,
524 last_instance_id: 0,
525 metrics,
526 }
527 }
528
529 pub fn version_update_notifier_tx(&self) -> Arc<tokio::sync::watch::Sender<PinnedVersion>> {
530 self.version_update_notifier_tx.clone()
531 }
532
533 pub fn recent_versions(&self) -> Arc<ArcSwap<RecentVersions>> {
534 self.recent_versions.clone()
535 }
536
537 pub fn read_version_mapping(&self) -> ReadOnlyReadVersionMapping {
538 ReadOnlyRwLockRef::new(self.read_version_mapping.clone())
539 }
540
541 pub fn event_sender(&self) -> HummockEventSender {
542 self.hummock_event_tx.clone()
543 }
544
545 pub fn buffer_tracker(&self) -> &BufferTracker {
546 self.uploader.buffer_tracker()
547 }
548}
549
550impl HummockEventHandler {
552 fn for_each_read_version(
555 &self,
556 instances: impl IntoIterator<Item = LocalInstanceId>,
557 mut f: impl FnMut(LocalInstanceId, &mut HummockReadVersion),
558 ) {
559 let instances = {
560 #[cfg(debug_assertions)]
561 {
562 let mut id_set = std::collections::HashSet::new();
564 for instance in instances {
565 assert!(id_set.insert(instance));
566 }
567 id_set
568 }
569 #[cfg(not(debug_assertions))]
570 {
571 instances
572 }
573 };
574 let mut pending = VecDeque::new();
575 let mut total_count = 0;
576 for instance_id in instances {
577 let Some((_, read_version)) = self.local_read_versions.get(&instance_id) else {
578 continue;
579 };
580 total_count += 1;
581 match read_version.try_write() {
582 Some(mut write_guard) => {
583 f(instance_id, &mut write_guard);
584 }
585 _ => {
586 pending.push_back(instance_id);
587 }
588 }
589 }
590 if !pending.is_empty() {
591 if pending.len() * 10 > total_count {
592 warn!(
594 pending_count = pending.len(),
595 total_count, "cannot acquire lock for all read version"
596 );
597 } else {
598 debug!(
599 pending_count = pending.len(),
600 total_count, "cannot acquire lock for all read version"
601 );
602 }
603 }
604
605 const TRY_LOCK_TIMEOUT: Duration = Duration::from_millis(1);
606
607 while let Some(instance_id) = pending.pop_front() {
608 let (_, read_version) = self
609 .local_read_versions
610 .get(&instance_id)
611 .expect("have checked exist before");
612 match read_version.try_write_for(TRY_LOCK_TIMEOUT) {
613 Some(mut write_guard) => {
614 f(instance_id, &mut write_guard);
615 }
616 _ => {
617 warn!(instance_id, "failed to get lock again for instance");
618 pending.push_back(instance_id);
619 }
620 }
621 }
622 }
623
624 fn handle_uploaded_ssts_inner(&mut self, ssts: Vec<Arc<StagingSstableInfo>>) {
625 match ssts.as_slice() {
626 [] => {
627 if cfg!(debug_assertions) {
628 panic!("empty ssts")
629 }
630 }
631 [staging_sstable_info] => {
632 trace!("data_flushed. SST size {}", staging_sstable_info.imm_size());
633 self.for_each_read_version(
634 staging_sstable_info.imm_ids().keys().cloned(),
635 |_, read_version| {
636 read_version.update(VersionUpdate::Sst(staging_sstable_info.clone()))
637 },
638 )
639 }
640 ssts => {
641 debug!(
642 batch_size = ssts.len(),
643 "handle multiple uploaded ssts in batch"
644 );
645 let affected_instances: HashSet<_> = ssts
646 .iter()
647 .flat_map(|sst| {
648 trace!("data_flushed. SST size {}", sst.imm_size());
649 sst.imm_ids().keys()
650 })
651 .copied()
652 .collect();
653 self.for_each_read_version(affected_instances, |instance_id, read_version| {
654 for sst in ssts {
655 if sst.imm_ids().contains_key(&instance_id) {
656 read_version.update(VersionUpdate::Sst(sst.clone()));
657 }
658 }
659 })
660 }
661 }
662 }
663
664 fn handle_sync_epoch(
665 &mut self,
666 sync_table_epochs: Vec<(HummockEpoch, HashSet<TableId>)>,
667 sync_result_sender: oneshot::Sender<HummockResult<SyncedData>>,
668 ) {
669 debug!(?sync_table_epochs, "awaiting for epoch to be synced",);
670 self.uploader
671 .start_sync_epoch(sync_result_sender, sync_table_epochs);
672 }
673
674 fn handle_clear(&mut self, notifier: oneshot::Sender<()>, table_ids: Option<HashSet<TableId>>) {
675 info!(
676 current_version_id = ?self.uploader.hummock_version().id(),
677 ?table_ids,
678 "handle clear event"
679 );
680
681 self.uploader.clear(table_ids.clone());
682
683 if table_ids.is_none() {
684 assert!(
685 self.local_read_versions.is_empty(),
686 "read version mapping not empty when clear. remaining tables: {:?}",
687 self.local_read_versions.table_ids().collect_vec()
688 );
689 }
690
691 let _ = notifier.send(()).inspect_err(|e| {
693 error!("failed to notify completion of clear event: {:?}", e);
694 });
695
696 info!("clear finished");
697 }
698
699 fn handle_observer_event(&mut self, event: HummockObserverEvent) {
700 match event {
701 HummockObserverEvent::VersionUpdate(version_update) => {
702 self.handle_version_update(version_update);
703 }
704 HummockObserverEvent::TableRefillRuntimeConfig(_, config) => {
705 let _timer = self
706 .metrics
707 .event_handler_on_apply_table_refill_runtime_config
708 .start_timer();
709 Self::apply_table_refill_runtime_config(&mut self.table_cache_metadata, config);
710 }
711 }
712 }
713
714 fn streaming_table_vnodes(&self, table_id: TableId) -> Option<Bitmap> {
715 self.local_read_versions.table_vnodes(table_id)
716 }
717
718 fn handle_version_update(&mut self, version_payload: HummockVersionUpdate) {
719 let _timer = self
720 .metrics
721 .event_handler_on_recv_version_update
722 .start_timer();
723 let mut sst_delta_infos = Vec::new();
724 if let Some(event) =
725 self.version_updates
726 .resolve_update(version_payload, |before, delta| {
727 sst_delta_infos.extend(before.build_sst_delta_infos(delta));
728 })
729 {
730 let handle = self
731 .refiller
732 .start_cache_refill(sst_delta_infos, &self.table_cache_metadata);
733 let condition = async move { handle.await.unwrap() }.boxed();
735 self.version_updates.enqueue(event, condition);
736 }
737 }
738
739 fn publish_version_updates(&mut self, events: Vec<VersionUpdateEvent>) {
741 let (first_event, remaining_events) = events
742 .split_first()
743 .expect("version update batch must not be empty");
744 let latest_pinned_version = &events.last().unwrap().new_pinned_version;
745 if events.len() > 1 {
746 debug!(
747 count = events.len(),
748 "handle multiple version updates in batch"
749 );
750 }
751 let _timer = self
752 .metrics
753 .event_handler_on_apply_version_update
754 .start_timer();
755 self.recent_versions.rcu(|prev_recent_versions| {
756 let mut recent_versions =
757 prev_recent_versions.with_new_version(first_event.new_pinned_version.clone());
758 for event in remaining_events {
759 recent_versions =
760 recent_versions.with_new_version(event.new_pinned_version.clone());
761 }
762 recent_versions
763 });
764
765 self.for_each_read_version(
766 self.local_read_versions.instance_ids(),
767 |_, read_version| {
768 for event in &events {
769 read_version.update(VersionUpdate::CommittedSnapshot(
770 event.new_pinned_version.clone(),
771 ));
772 }
773 },
774 );
775
776 self.version_update_notifier_tx.send_modify(|state| {
777 assert_eq!(first_event.prev_version_id, state.id());
778 assert!(latest_pinned_version.id() > state.id());
779 *state = latest_pinned_version.clone();
781 });
782
783 debug!("update to hummock version: {}", latest_pinned_version.id());
784
785 self.uploader
786 .update_pinned_version(latest_pinned_version.clone());
787 }
788}
789
790impl HummockEventHandler {
791 pub async fn start_hummock_event_handler_worker(mut self) {
792 loop {
793 tokio::select! {
794 ssts = self.uploader.next_uploaded_ssts() => {
795 self.handle_uploaded_ssts(ssts);
796 }
797 events = self.version_updates.next_events() => {
798 self.publish_version_updates(events);
799 }
800 event = pin!(self.hummock_event_rx.recv()) => {
801 let Some(event) = event else { break };
802 match event {
803 HummockEvent::Shutdown => {
804 info!("event handler shutdown");
805 return;
806 },
807 event => {
808 self.handle_hummock_event(event);
809 }
810 }
811 }
812 observer_event = pin!(self.observer_event_rx.recv()) => {
813 let Some(observer_event) = observer_event else {
814 warn!("observer event stream ends. event handle shutdown");
815 return;
816 };
817 self.handle_observer_event(observer_event);
818 }
819 }
820 }
821 }
822
823 fn handle_uploaded_ssts(&mut self, ssts: Vec<Arc<StagingSstableInfo>>) {
824 let _timer = self
825 .metrics
826 .event_handler_on_upload_finish_latency
827 .start_timer();
828 self.handle_uploaded_ssts_inner(ssts);
829 }
830
831 fn handle_hummock_event(&mut self, event: HummockEvent) {
833 match event {
834 HummockEvent::BufferMayFlush => {
835 self.uploader
836 .may_flush(&self.metrics.event_handler_on_spiller);
837 }
838 HummockEvent::SyncEpoch {
839 sync_result_sender,
840 sync_table_epochs,
841 } => {
842 self.handle_sync_epoch(sync_table_epochs, sync_result_sender);
843 }
844 HummockEvent::Clear(notifier, table_ids) => {
845 self.handle_clear(notifier, table_ids);
846 }
847 HummockEvent::Shutdown => {
848 unreachable!("shutdown is handled specially")
849 }
850 HummockEvent::StartEpoch { epoch, table_ids } => {
851 self.uploader.start_epoch(epoch, table_ids);
852 }
853 HummockEvent::InitEpoch {
854 instance_id,
855 init_epoch,
856 } => {
857 let table_id = self
858 .local_read_versions
859 .get(&instance_id)
860 .expect("should exist")
861 .0;
862 self.uploader
863 .init_instance(instance_id, table_id, init_epoch);
864 }
865 HummockEvent::ImmToUploader { instance_id, imms } => {
866 assert!(
867 self.local_read_versions.contains_instance(&instance_id),
868 "add imm from non-existing read version instance: instance_id: {}, table_id {:?}",
869 instance_id,
870 imms.first().map(|(imm, _)| imm.table_id),
871 );
872 self.uploader.add_imms(instance_id, imms);
873 self.uploader
874 .may_flush(&self.metrics.event_handler_on_spiller);
875 }
876
877 HummockEvent::LocalSealEpoch {
878 next_epoch,
879 opts,
880 instance_id,
881 } => {
882 self.uploader
883 .local_seal_epoch(instance_id, next_epoch, opts);
884 }
885
886 #[cfg(any(test, feature = "test"))]
887 HummockEvent::FlushEvent(sender) => {
888 let _ = sender.send(()).inspect_err(|e| {
889 error!("unable to send flush result: {:?}", e);
890 });
891 }
892
893 HummockEvent::RegisterReadVersion {
894 table_id,
895 new_read_version_sender,
896 is_replicated,
897 vnodes,
898 } => {
899 let pinned_version = self.recent_versions.load().latest_version().clone();
900 let instance_id = self.generate_instance_id();
901 let basic_read_version = Arc::new(RwLock::new(
902 HummockReadVersion::new_with_replication_option(
903 table_id,
904 instance_id,
905 pinned_version,
906 is_replicated,
907 vnodes,
908 ),
909 ));
910
911 debug!(
912 "new read version registered: table_id: {}, instance_id: {}",
913 table_id, instance_id
914 );
915
916 {
917 self.local_read_versions.insert(
918 instance_id,
919 table_id,
920 basic_read_version.clone(),
921 );
922 let mut read_version_mapping_guard = self.read_version_mapping.write();
923
924 read_version_mapping_guard
925 .entry(table_id)
926 .or_default()
927 .insert(instance_id, basic_read_version.clone());
928 }
929
930 match new_read_version_sender.send((
931 basic_read_version,
932 LocalInstanceGuard {
933 table_id,
934 instance_id,
935 event_sender: Some(self.hummock_event_tx.clone()),
936 },
937 )) {
938 Ok(_) => {}
939 Err((_, mut guard)) => {
940 warn!(
941 "RegisterReadVersion send fail table_id {:?} instance_is {:?}",
942 table_id, instance_id
943 );
944 guard.event_sender.take().expect("sender is just set");
945 self.destroy_read_version(instance_id);
946 }
947 }
948
949 self.table_cache_metadata
950 .update_streaming_table_vnodes(table_id, self.streaming_table_vnodes(table_id));
951 }
952
953 HummockEvent::DestroyReadVersion { instance_id } => {
954 let table_id = self
955 .local_read_versions
956 .get(&instance_id)
957 .unwrap_or_else(|| {
958 panic!("query nonexistent instance instance_id {instance_id}")
959 })
960 .0;
961 self.uploader.may_destroy_instance(instance_id);
962 self.destroy_read_version(instance_id);
963 self.table_cache_metadata
964 .update_streaming_table_vnodes(table_id, self.streaming_table_vnodes(table_id));
965 }
966 HummockEvent::GetMinUncommittedObjectId { result_tx } => {
967 let _ = result_tx
968 .send(self.uploader.min_uncommitted_object_id())
969 .inspect_err(|e| {
970 error!("unable to send get_min_uncommitted_sst_id result: {:?}", e);
971 });
972 }
973 HummockEvent::GetTableCacheRefillMonitorSnapshot { result_tx } => {
974 let _ = result_tx
975 .send(CacheRefiller::table_cache_refill_monitor_snapshot(
976 &self.table_cache_metadata,
977 ))
978 .inspect_err(|_| {
979 error!("unable to send table cache refill monitor snapshot result");
980 });
981 }
982 HummockEvent::RegisterVectorWriter {
983 table_id,
984 init_epoch,
985 } => self.uploader.register_vector_writer(table_id, init_epoch),
986 HummockEvent::VectorWriterSealEpoch {
987 table_id,
988 next_epoch,
989 add,
990 } => {
991 self.uploader
992 .vector_writer_seal_epoch(table_id, next_epoch, add);
993 }
994 HummockEvent::DropVectorWriter { table_id } => {
995 self.uploader.drop_vector_writer(table_id);
996 }
997 }
998 }
999
1000 fn destroy_read_version(&mut self, instance_id: LocalInstanceId) {
1001 {
1002 {
1003 debug!("read version deregister: instance_id: {}", instance_id);
1004 let (table_id, _) = self.local_read_versions.remove(instance_id);
1005 let mut read_version_mapping_guard = self.read_version_mapping.write();
1006 let entry = read_version_mapping_guard
1007 .get_mut(&table_id)
1008 .unwrap_or_else(|| {
1009 panic!(
1010 "DestroyHummockInstance table_id {} instance_id {} fail",
1011 table_id, instance_id
1012 )
1013 });
1014 entry.remove(&instance_id).unwrap_or_else(|| {
1015 panic!(
1016 "DestroyHummockInstance nonexistent instance table_id {} instance_id {}",
1017 table_id, instance_id
1018 )
1019 });
1020 if entry.is_empty() {
1021 read_version_mapping_guard.remove(&table_id);
1022 }
1023 }
1024 }
1025 }
1026
1027 fn generate_instance_id(&mut self) -> LocalInstanceId {
1028 self.last_instance_id += 1;
1029 self.last_instance_id
1030 }
1031}
1032
1033pub(super) fn send_sync_result(
1034 sender: oneshot::Sender<HummockResult<SyncedData>>,
1035 result: HummockResult<SyncedData>,
1036) {
1037 let _ = sender.send(result).inspect_err(|e| {
1038 error!("unable to send sync result. Err: {:?}", e);
1039 });
1040}
1041
1042impl SyncedData {
1043 pub fn into_sync_result(self) -> SyncResult {
1044 {
1045 let SyncedData {
1046 uploaded_ssts,
1047 table_watermarks,
1048 vector_index_adds,
1049 } = self;
1050 let mut sync_size = 0;
1051 let mut uncommitted_ssts = Vec::new();
1052 let mut old_value_ssts = Vec::new();
1053 for sst in uploaded_ssts {
1056 sync_size += sst.imm_size();
1057 uncommitted_ssts.extend(sst.sstable_infos().iter().cloned());
1058 old_value_ssts.extend(sst.old_value_sstable_infos().iter().cloned());
1059 }
1060 SyncResult {
1061 sync_size,
1062 uncommitted_ssts,
1063 table_watermarks,
1064 old_value_ssts,
1065 vector_index_adds,
1066 }
1067 }
1068 }
1069}
1070
1071#[cfg(test)]
1072mod tests {
1073 use std::collections::{HashMap, HashSet};
1074 use std::future::poll_fn;
1075 use std::sync::Arc;
1076 use std::task::Poll;
1077
1078 use futures::FutureExt;
1079 use parking_lot::Mutex;
1080 use risingwave_common::bitmap::Bitmap;
1081 use risingwave_common::catalog::TableId;
1082 use risingwave_common::config::Role;
1083 use risingwave_common::config::streaming::CacheRefillPolicy;
1084 use risingwave_common::hash::VirtualNode;
1085 use risingwave_common::util::epoch::{EpochExt, test_epoch};
1086 use risingwave_hummock_sdk::compaction_group::StaticCompactionGroupId;
1087 use risingwave_hummock_sdk::version::HummockVersion;
1088 use risingwave_pb::hummock::{PbHummockVersion, StateTableInfo};
1089 use risingwave_pb::meta::serving_table_vnode_mappings::PbServingTableVnodeMapping;
1090 use risingwave_pb::meta::table_cache_refill_policies::PbTableCacheRefillPolicy;
1091 use risingwave_pb::meta::table_cache_refill_policies::table_cache_refill_policy::PbCacheRefillPolicy;
1092 use risingwave_pb::meta::{
1093 PbServingTableVnodeMappings, PbTableRefillRuntimeConfig, TableCacheRefillPolicies,
1094 };
1095 use tokio::spawn;
1096 use tokio::sync::mpsc::unbounded_channel;
1097 use tokio::sync::oneshot;
1098
1099 use crate::hummock::HummockError;
1100 use crate::hummock::event_handler::hummock_event_handler::BufferTracker;
1101 use crate::hummock::event_handler::refiller::{CacheRefillConfig, CacheRefiller};
1102 use crate::hummock::event_handler::uploader::UploadTaskOutput;
1103 use crate::hummock::event_handler::uploader::test_utils::{
1104 TEST_TABLE_ID, gen_imm_inner, gen_imm_with_unlimit,
1105 prepare_uploader_order_test_spawn_task_fn,
1106 };
1107 use crate::hummock::event_handler::{
1108 HummockEvent, HummockEventHandler, HummockReadVersionRef, LocalInstanceGuard,
1109 };
1110 use crate::hummock::iterator::test_utils::mock_sstable_store;
1111 use crate::hummock::local_version::pinned_version::PinnedVersion;
1112 use crate::hummock::local_version::recent_versions::RecentVersions;
1113 use crate::hummock::table_cache_metadata::TableCacheMetadata;
1114 use crate::hummock::test_utils::default_opts_for_test;
1115 use crate::mem_table::ImmutableMemtable;
1116 use crate::monitor::HummockStateStoreMetrics;
1117 use crate::store::SealCurrentEpochOptions;
1118
1119 #[tokio::test]
1120 async fn test_version_updates_wait_for_foyer_and_publish_in_order() {
1121 use risingwave_hummock_sdk::version::HummockVersionDelta;
1122 use risingwave_pb::hummock::StateTableInfoDelta;
1123 use tokio::sync::Semaphore;
1124
1125 use super::HummockVersionUpdate;
1126 use crate::hummock::local_version::pinned_version::PinVersionAction::{Pin, Unpin};
1127
1128 let metrics = Arc::new(HummockStateStoreMetrics::unused());
1129 let opts = default_opts_for_test();
1130 let (spawn_upload_task, _) = prepare_uploader_order_test_spawn_task_fn(false);
1131 let (pin_tx, mut pin_rx) = unbounded_channel();
1132 let version = PinnedVersion::new(
1133 HummockVersion::from_rpc_protobuf(&PbHummockVersion {
1134 id: 1.into(),
1135 state_table_info: HashMap::from_iter([(
1136 TEST_TABLE_ID,
1137 StateTableInfo {
1138 committed_epoch: test_epoch(1),
1139 compaction_group_id: StaticCompactionGroupId::StateDefault,
1140 },
1141 )]),
1142 ..Default::default()
1143 }),
1144 pin_tx,
1145 );
1146 let permits = Arc::new([Semaphore::new(0), Semaphore::new(0), Semaphore::new(0)]);
1147 let started = Arc::new(std::sync::atomic::AtomicUsize::new(0));
1148 let (finished_tx, mut finished_rx) = unbounded_channel();
1149 let refill_config = CacheRefillConfig::from_storage_opts(&opts);
1150 let metadata = TableCacheMetadata::new(
1151 Role::Streaming,
1152 opts.cache_refill_table_cache_refill_default_policy,
1153 );
1154 let refiller = CacheRefiller::new(refill_config, mock_sstable_store().await)
1155 .with_foyer_spawn_task(Arc::new({
1156 let permits = permits.clone();
1157 let started = started.clone();
1158 move |_, _| {
1159 let index = started.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1160 let permits = permits.clone();
1161 let finished_tx = finished_tx.clone();
1162 spawn(async move {
1163 let _permit = permits[index].acquire().await.unwrap();
1164 finished_tx.send(index).unwrap();
1165 })
1166 }
1167 }));
1168 let mut handler = HummockEventHandler::new_inner(
1169 unbounded_channel().1,
1170 metrics.clone(),
1171 RecentVersions::new(version, 10, metrics.clone()),
1172 BufferTracker::from_storage_opts(&opts, &metrics),
1173 spawn_upload_task,
1174 refiller,
1175 metadata,
1176 );
1177 let (tx, rx) = oneshot::channel();
1178 handler.handle_hummock_event(HummockEvent::RegisterReadVersion {
1179 table_id: TEST_TABLE_ID,
1180 new_read_version_sender: tx,
1181 is_replicated: false,
1182 vnodes: Arc::new(Bitmap::ones(VirtualNode::COUNT_FOR_TEST)),
1183 });
1184 let (read_version, _guard) = rx.await.unwrap();
1185 let reader_snapshot = read_version.read().committed().clone();
1186 let mut version_updates_rx = handler.version_update_notifier_tx.subscribe();
1187 handler.handle_version_update(HummockVersionUpdate::PinnedVersion(Box::new(
1189 HummockVersion::from_rpc_protobuf(&PbHummockVersion {
1190 id: 2.into(),
1191 state_table_info: HashMap::from_iter([(
1192 TEST_TABLE_ID,
1193 StateTableInfo {
1194 committed_epoch: test_epoch(2),
1195 compaction_group_id: StaticCompactionGroupId::StateDefault,
1196 },
1197 )]),
1198 ..Default::default()
1199 }),
1200 )));
1201 handler.handle_version_update(HummockVersionUpdate::VersionDeltas(
1202 (3..=4)
1203 .map(|id| {
1204 let mut delta = HummockVersionDelta::default();
1205 delta.id = id.into();
1206 delta.prev_id = (id - 1).into();
1207 delta.state_table_info_delta.insert(
1208 TEST_TABLE_ID,
1209 StateTableInfoDelta {
1210 committed_epoch: test_epoch(id),
1211 compaction_group_id: StaticCompactionGroupId::StateDefault,
1212 },
1213 );
1214 delta
1215 })
1216 .collect(),
1217 ));
1218 assert_eq!(started.load(std::sync::atomic::Ordering::Relaxed), 2);
1219 for expected_id in [1, 2, 4] {
1220 assert!(matches!(pin_rx.try_recv(), Ok(Pin(id)) if id.as_raw_id() == expected_id));
1221 }
1222 assert!(pin_rx.try_recv().is_err());
1223 permits[1].add_permits(1);
1224 assert_eq!(finished_rx.recv().await, Some(1));
1225 assert!(
1226 handler
1227 .version_updates
1228 .next_events()
1229 .now_or_never()
1230 .is_none()
1231 );
1232 assert_eq!(handler.uploader.hummock_version().id().as_raw_id(), 1);
1233 assert_eq!(
1234 handler.version_update_notifier_tx.borrow().id().as_raw_id(),
1235 1
1236 );
1237 assert_eq!(read_version.read().committed().id().as_raw_id(), 1);
1238 assert!(!version_updates_rx.has_changed().unwrap());
1239 assert!(pin_rx.try_recv().is_err());
1240
1241 permits[0].add_permits(1);
1242 assert_eq!(finished_rx.recv().await, Some(0));
1243 let events = handler.version_updates.next_events().await;
1244 assert_eq!(
1245 events
1246 .iter()
1247 .map(|event| (
1248 event.prev_version_id.as_raw_id(),
1249 event.new_pinned_version.id().as_raw_id(),
1250 ))
1251 .collect::<Vec<_>>(),
1252 vec![(1, 2), (2, 4)]
1253 );
1254 assert!(pin_rx.try_recv().is_err());
1256 handler.publish_version_updates(events);
1257 assert!(version_updates_rx.has_changed().unwrap());
1258 assert_eq!(version_updates_rx.borrow_and_update().id().as_raw_id(), 4);
1259 drop(version_updates_rx);
1260 assert_eq!(read_version.read().committed().id().as_raw_id(), 4);
1261 assert!(pin_rx.try_recv().is_err());
1263 assert_eq!(handler.uploader.hummock_version().id().as_raw_id(), 4);
1264 assert_eq!(
1265 handler.version_update_notifier_tx.borrow().id().as_raw_id(),
1266 4
1267 );
1268 assert_eq!(
1269 handler
1270 .recent_versions
1271 .load()
1272 .latest_version()
1273 .id()
1274 .as_raw_id(),
1275 4
1276 );
1277 assert_eq!(
1278 handler
1279 .recent_versions
1280 .load()
1281 .get_safe_version(TEST_TABLE_ID, test_epoch(2))
1282 .unwrap()
1283 .id()
1284 .as_raw_id(),
1285 2
1286 );
1287
1288 let mut delta = HummockVersionDelta::default();
1289 delta.prev_id = 4.into();
1290 delta.id = 5.into();
1291 handler.handle_version_update(HummockVersionUpdate::VersionDeltas(vec![delta]));
1292 assert_eq!(started.load(std::sync::atomic::Ordering::Relaxed), 3);
1293 assert!(matches!(pin_rx.try_recv(), Ok(Pin(id)) if id.as_raw_id() == 5));
1294 assert!(pin_rx.try_recv().is_err());
1295 drop(handler);
1297 let unpinned_versions = (0..2)
1299 .map(|_| {
1300 let Unpin(id) = pin_rx.try_recv().unwrap() else {
1301 panic!("expected an unpin action");
1302 };
1303 id.as_raw_id()
1304 })
1305 .collect::<HashSet<_>>();
1306 assert_eq!(unpinned_versions, HashSet::from([2, 5]));
1307 assert!(pin_rx.try_recv().is_err());
1308 permits[2].add_permits(1);
1309 assert_eq!(finished_rx.recv().await, Some(2));
1310 drop(read_version);
1312 assert!(matches!(pin_rx.try_recv(), Ok(Unpin(id)) if id.as_raw_id() == 4));
1313 assert!(pin_rx.try_recv().is_err());
1314 drop(reader_snapshot);
1315 assert!(matches!(pin_rx.try_recv(), Ok(Unpin(id)) if id.as_raw_id() == 1));
1316 assert!(pin_rx.try_recv().is_err());
1317 }
1318
1319 #[test]
1320 fn test_runtime_config_fields_are_independent_full_replacements() {
1321 let old_table_id = TableId::new(233);
1322 let internal_table_id = TableId::new(234);
1323 let new_table_id = TableId::new(235);
1324 let old_vnodes = Bitmap::ones(VirtualNode::COUNT_FOR_TEST);
1325 let new_vnodes = Bitmap::from_range(VirtualNode::COUNT_FOR_TEST, 0..8);
1326 let storage_opt = default_opts_for_test();
1327 let mut metadata = TableCacheMetadata::new(
1328 Role::Both,
1329 storage_opt.cache_refill_table_cache_refill_default_policy,
1330 );
1331
1332 HummockEventHandler::apply_table_refill_runtime_config(
1333 &mut metadata,
1334 PbTableRefillRuntimeConfig {
1335 table_cache_refill_policies: Some(TableCacheRefillPolicies {
1336 table_policies: vec![PbTableCacheRefillPolicy {
1337 table_id: old_table_id.as_raw_id(),
1338 policy: PbCacheRefillPolicy::Serving as i32,
1339 }],
1340 internal_table_policies: vec![PbTableCacheRefillPolicy {
1341 table_id: internal_table_id.as_raw_id(),
1342 policy: PbCacheRefillPolicy::Both as i32,
1343 }],
1344 }),
1345 serving_table_vnode_mappings: Some(PbServingTableVnodeMappings {
1346 mappings: vec![PbServingTableVnodeMapping {
1347 table_id: old_table_id.as_raw_id(),
1348 bitmap: Some(old_vnodes.to_protobuf()),
1349 }],
1350 }),
1351 ..Default::default()
1352 },
1353 );
1354 let snapshot = CacheRefiller::table_cache_refill_monitor_snapshot(&metadata);
1355 assert_eq!(
1356 snapshot.policies,
1357 HashMap::from([
1358 (old_table_id, CacheRefillPolicy::Serving),
1359 (internal_table_id, CacheRefillPolicy::Both),
1360 ])
1361 );
1362 assert_eq!(
1363 snapshot.serving_table_vnode_mapping,
1364 HashMap::from([(old_table_id, old_vnodes)])
1365 );
1366
1367 HummockEventHandler::apply_table_refill_runtime_config(
1368 &mut metadata,
1369 PbTableRefillRuntimeConfig {
1370 serving_table_vnode_mappings: Some(PbServingTableVnodeMappings {
1371 mappings: vec![PbServingTableVnodeMapping {
1372 table_id: new_table_id.as_raw_id(),
1373 bitmap: Some(new_vnodes.to_protobuf()),
1374 }],
1375 }),
1376 ..Default::default()
1377 },
1378 );
1379 let snapshot = CacheRefiller::table_cache_refill_monitor_snapshot(&metadata);
1380 assert_eq!(
1381 snapshot.policies,
1382 HashMap::from([
1383 (old_table_id, CacheRefillPolicy::Serving),
1384 (internal_table_id, CacheRefillPolicy::Both),
1385 ])
1386 );
1387 assert_eq!(
1388 snapshot.serving_table_vnode_mapping,
1389 HashMap::from([(new_table_id, new_vnodes.clone())])
1390 );
1391
1392 HummockEventHandler::apply_table_refill_runtime_config(
1393 &mut metadata,
1394 PbTableRefillRuntimeConfig {
1395 table_cache_refill_policies: Some(TableCacheRefillPolicies::default()),
1396 ..Default::default()
1397 },
1398 );
1399 let snapshot = CacheRefiller::table_cache_refill_monitor_snapshot(&metadata);
1400 assert!(snapshot.policies.is_empty());
1401 assert_eq!(
1402 snapshot.serving_table_vnode_mapping,
1403 HashMap::from([(new_table_id, new_vnodes)])
1404 );
1405
1406 HummockEventHandler::apply_table_refill_runtime_config(
1407 &mut metadata,
1408 PbTableRefillRuntimeConfig {
1409 serving_table_vnode_mappings: Some(PbServingTableVnodeMappings::default()),
1410 ..Default::default()
1411 },
1412 );
1413 let snapshot = CacheRefiller::table_cache_refill_monitor_snapshot(&metadata);
1414 assert!(snapshot.policies.is_empty());
1415 assert!(snapshot.serving_table_vnode_mapping.is_empty());
1416 }
1417
1418 #[test]
1419 fn test_runtime_config_ignores_invalid_entries() {
1420 let table_id = TableId::new(233);
1421 assert!(
1422 super::table_cache_refill_policy_from_protobuf(PbTableCacheRefillPolicy {
1423 table_id: table_id.as_raw_id(),
1424 policy: i32::MAX,
1425 })
1426 .is_none()
1427 );
1428
1429 assert!(
1430 super::serving_table_vnode_mappings_to_map(PbServingTableVnodeMappings {
1431 mappings: vec![PbServingTableVnodeMapping {
1432 table_id: table_id.as_raw_id(),
1433 bitmap: None,
1434 }],
1435 })
1436 .is_empty()
1437 );
1438 }
1439
1440 #[tokio::test]
1441 async fn test_read_version_events_update_table_cache_metadata() {
1442 fn assert_vnodes(vnodes: &Bitmap, expected: std::ops::Range<usize>) {
1443 assert_eq!(vnodes.count_ones(), expected.len());
1444 assert!(expected.into_iter().all(|vnode| vnodes.is_set(vnode)));
1445 }
1446
1447 let table_id = TableId::new(233);
1448 let other_table_id = TableId::new(234);
1449 let (_observer_event_tx, observer_event_rx) = unbounded_channel();
1450 let metrics = Arc::new(HummockStateStoreMetrics::unused());
1451 let storage_opt = default_opts_for_test();
1452 let (spawn_upload_task, _) = prepare_uploader_order_test_spawn_task_fn(false);
1453 let pinned_version = PinnedVersion::new(
1454 HummockVersion::from_rpc_protobuf(&PbHummockVersion {
1455 id: 1.into(),
1456 ..Default::default()
1457 }),
1458 unbounded_channel().0,
1459 );
1460 let refill_config = CacheRefillConfig::from_storage_opts(&storage_opt);
1461 let metadata = TableCacheMetadata::new(
1462 Role::Streaming,
1463 storage_opt.cache_refill_table_cache_refill_default_policy,
1464 );
1465 let refiller = CacheRefiller::new(refill_config, mock_sstable_store().await);
1466 let mut event_handler = HummockEventHandler::new_inner(
1467 observer_event_rx,
1468 metrics.clone(),
1469 RecentVersions::new(pinned_version, 10, metrics.clone()),
1470 BufferTracker::from_storage_opts(&storage_opt, &metrics),
1471 spawn_upload_task,
1472 refiller,
1473 metadata,
1474 );
1475
1476 let (tx, rx) = oneshot::channel();
1477 event_handler.handle_hummock_event(HummockEvent::RegisterReadVersion {
1478 table_id,
1479 new_read_version_sender: tx,
1480 is_replicated: false,
1481 vnodes: Arc::new(Bitmap::from_range(VirtualNode::COUNT_FOR_TEST, 0..8)),
1482 });
1483 let (_read_version_1, mut guard_1) = rx.await.unwrap();
1484
1485 let (tx, rx) = oneshot::channel();
1486 event_handler.handle_hummock_event(HummockEvent::RegisterReadVersion {
1487 table_id,
1488 new_read_version_sender: tx,
1489 is_replicated: false,
1490 vnodes: Arc::new(Bitmap::from_range(VirtualNode::COUNT_FOR_TEST, 8..16)),
1491 });
1492 let (_read_version_2, mut guard_2) = rx.await.unwrap();
1493
1494 let (tx, rx) = oneshot::channel();
1495 event_handler.handle_hummock_event(HummockEvent::RegisterReadVersion {
1496 table_id: other_table_id,
1497 new_read_version_sender: tx,
1498 is_replicated: false,
1499 vnodes: Arc::new(Bitmap::from_range(VirtualNode::COUNT_FOR_TEST, 32..40)),
1500 });
1501 let (_other_read_version, mut other_guard) = rx.await.unwrap();
1502
1503 let snapshot =
1504 CacheRefiller::table_cache_refill_monitor_snapshot(&event_handler.table_cache_metadata);
1505 let vnodes = snapshot
1506 .streaming_table_vnode_mapping
1507 .get(&table_id)
1508 .unwrap();
1509 assert_vnodes(vnodes, 0..16);
1510 assert_vnodes(
1511 snapshot
1512 .streaming_table_vnode_mapping
1513 .get(&other_table_id)
1514 .unwrap(),
1515 32..40,
1516 );
1517
1518 guard_1.event_sender.take();
1519 event_handler.handle_hummock_event(HummockEvent::DestroyReadVersion {
1520 instance_id: guard_1.instance_id,
1521 });
1522 let snapshot =
1523 CacheRefiller::table_cache_refill_monitor_snapshot(&event_handler.table_cache_metadata);
1524 assert_vnodes(
1525 snapshot
1526 .streaming_table_vnode_mapping
1527 .get(&table_id)
1528 .unwrap(),
1529 8..16,
1530 );
1531
1532 guard_2.event_sender.take();
1533 event_handler.handle_hummock_event(HummockEvent::DestroyReadVersion {
1534 instance_id: guard_2.instance_id,
1535 });
1536 let snapshot =
1537 CacheRefiller::table_cache_refill_monitor_snapshot(&event_handler.table_cache_metadata);
1538 assert!(
1539 !snapshot
1540 .streaming_table_vnode_mapping
1541 .contains_key(&table_id)
1542 );
1543 assert_vnodes(
1544 snapshot
1545 .streaming_table_vnode_mapping
1546 .get(&other_table_id)
1547 .unwrap(),
1548 32..40,
1549 );
1550
1551 other_guard.event_sender.take();
1552 event_handler.handle_hummock_event(HummockEvent::DestroyReadVersion {
1553 instance_id: other_guard.instance_id,
1554 });
1555 }
1556
1557 #[tokio::test]
1558 async fn test_old_epoch_sync_fail() {
1559 let epoch0 = test_epoch(233);
1560
1561 let initial_version = PinnedVersion::new(
1562 HummockVersion::from_rpc_protobuf(&PbHummockVersion {
1563 id: 1.into(),
1564 state_table_info: HashMap::from_iter([(
1565 TEST_TABLE_ID,
1566 StateTableInfo {
1567 committed_epoch: epoch0,
1568 compaction_group_id: StaticCompactionGroupId::StateDefault,
1569 },
1570 )]),
1571 ..Default::default()
1572 }),
1573 unbounded_channel().0,
1574 );
1575
1576 let (_observer_event_tx, observer_event_rx) = unbounded_channel();
1577
1578 let epoch1 = epoch0.next_epoch();
1579 let epoch2 = epoch1.next_epoch();
1580 let (tx, rx) = oneshot::channel();
1581 let rx = Arc::new(Mutex::new(Some(rx)));
1582
1583 let storage_opt = default_opts_for_test();
1584 let metrics = Arc::new(HummockStateStoreMetrics::unused());
1585
1586 let refill_config = CacheRefillConfig::from_storage_opts(&storage_opt);
1587 let metadata = TableCacheMetadata::new(
1588 Role::None,
1589 storage_opt.cache_refill_table_cache_refill_default_policy,
1590 );
1591 let refiller = CacheRefiller::new(refill_config, mock_sstable_store().await);
1592 let event_handler = HummockEventHandler::new_inner(
1593 observer_event_rx,
1594 metrics.clone(),
1595 RecentVersions::new(initial_version.clone(), 10, metrics.clone()),
1596 BufferTracker::from_storage_opts(&storage_opt, &metrics),
1597 Arc::new(move |_, info| {
1598 assert_eq!(info.epochs.len(), 1);
1599 let epoch = info.epochs[0];
1600 match epoch {
1601 epoch if epoch == epoch1 => {
1602 let rx = rx.lock().take().unwrap();
1603 spawn(async move {
1604 rx.await.unwrap();
1605 Err(HummockError::other("fail"))
1606 })
1607 }
1608 epoch if epoch == epoch2 => spawn(async move {
1609 Ok(UploadTaskOutput {
1610 new_value_ssts: vec![],
1611 old_value_ssts: vec![],
1612 wait_poll_timer: None,
1613 })
1614 }),
1615 _ => unreachable!(),
1616 }
1617 }),
1618 refiller,
1619 metadata,
1620 );
1621
1622 let event_tx = event_handler.event_sender();
1623
1624 let send_event = |event| event_tx.send(event).unwrap();
1625
1626 let join_handle = spawn(event_handler.start_hummock_event_handler_worker());
1627
1628 let (read_version, guard) = {
1629 let (tx, rx) = oneshot::channel();
1630 send_event(HummockEvent::RegisterReadVersion {
1631 table_id: TEST_TABLE_ID,
1632 new_read_version_sender: tx,
1633 is_replicated: false,
1634 vnodes: Arc::new(Bitmap::ones(VirtualNode::COUNT_FOR_TEST)),
1635 });
1636 rx.await.unwrap()
1637 };
1638
1639 send_event(HummockEvent::StartEpoch {
1640 epoch: epoch1,
1641 table_ids: HashSet::from_iter([TEST_TABLE_ID]),
1642 });
1643
1644 read_version.write().init();
1645
1646 send_event(HummockEvent::InitEpoch {
1647 instance_id: guard.instance_id,
1648 init_epoch: epoch1,
1649 });
1650
1651 let (imm1, tracker1) = gen_imm_with_unlimit(epoch1);
1652 read_version.write().add_pending_imm(imm1.clone(), tracker1);
1653
1654 send_event(HummockEvent::ImmToUploader {
1655 instance_id: guard.instance_id,
1656 imms: read_version.write().start_upload_pending_imms(),
1657 });
1658
1659 send_event(HummockEvent::StartEpoch {
1660 epoch: epoch2,
1661 table_ids: HashSet::from_iter([TEST_TABLE_ID]),
1662 });
1663
1664 send_event(HummockEvent::LocalSealEpoch {
1665 instance_id: guard.instance_id,
1666 next_epoch: epoch2,
1667 opts: SealCurrentEpochOptions::for_test(),
1668 });
1669
1670 {
1671 let (imm2, tracker2) = gen_imm_with_unlimit(epoch2);
1672 let mut read_version = read_version.write();
1673 read_version.add_pending_imm(imm2, tracker2);
1674
1675 send_event(HummockEvent::ImmToUploader {
1676 instance_id: guard.instance_id,
1677 imms: read_version.start_upload_pending_imms(),
1678 });
1679 }
1680
1681 let epoch3 = epoch2.next_epoch();
1682 send_event(HummockEvent::StartEpoch {
1683 epoch: epoch3,
1684 table_ids: HashSet::from_iter([TEST_TABLE_ID]),
1685 });
1686 send_event(HummockEvent::LocalSealEpoch {
1687 instance_id: guard.instance_id,
1688 next_epoch: epoch3,
1689 opts: SealCurrentEpochOptions::for_test(),
1690 });
1691
1692 let (tx1, mut rx1) = oneshot::channel();
1693 send_event(HummockEvent::SyncEpoch {
1694 sync_result_sender: tx1,
1695 sync_table_epochs: vec![(epoch1, HashSet::from_iter([TEST_TABLE_ID]))],
1696 });
1697 assert!(poll_fn(|cx| Poll::Ready(rx1.poll_unpin(cx).is_pending())).await);
1698 let (tx2, mut rx2) = oneshot::channel();
1699 send_event(HummockEvent::SyncEpoch {
1700 sync_result_sender: tx2,
1701 sync_table_epochs: vec![(epoch2, HashSet::from_iter([TEST_TABLE_ID]))],
1702 });
1703 assert!(poll_fn(|cx| Poll::Ready(rx2.poll_unpin(cx).is_pending())).await);
1704
1705 tx.send(()).unwrap();
1706 rx1.await.unwrap().unwrap_err();
1707 rx2.await.unwrap().unwrap_err();
1708
1709 send_event(HummockEvent::Shutdown);
1710 join_handle.await.unwrap();
1711 }
1712
1713 #[tokio::test]
1714 async fn test_clear_one_table_preserves_other_table_uploads() {
1715 let table_id1 = TableId::new(1);
1716 let table_id2 = TableId::new(2);
1717 let epoch0 = test_epoch(233);
1718
1719 let initial_version = PinnedVersion::new(
1720 HummockVersion::from_rpc_protobuf(&PbHummockVersion {
1721 id: 1.into(),
1722 state_table_info: HashMap::from_iter([
1723 (
1724 table_id1,
1725 StateTableInfo {
1726 committed_epoch: epoch0,
1727 compaction_group_id: StaticCompactionGroupId::StateDefault,
1728 },
1729 ),
1730 (
1731 table_id2,
1732 StateTableInfo {
1733 committed_epoch: epoch0,
1734 compaction_group_id: StaticCompactionGroupId::StateDefault,
1735 },
1736 ),
1737 ]),
1738 ..Default::default()
1739 }),
1740 unbounded_channel().0,
1741 );
1742
1743 let (_observer_event_tx, observer_event_rx) = unbounded_channel();
1744
1745 let epoch1 = epoch0.next_epoch();
1746 let epoch2 = epoch1.next_epoch();
1747 let epoch3 = epoch2.next_epoch();
1748
1749 let imm_size = gen_imm_inner(TEST_TABLE_ID, epoch1, 0).size();
1750
1751 let buffer_tracker = BufferTracker::for_test_with_config(imm_size * 2 - 1, 1);
1753 let memory_limiter = buffer_tracker.get_memory_limiter().clone();
1754
1755 let gen_imm = |table_id, epoch, spill_offset| {
1756 let imm = gen_imm_inner(table_id, epoch, spill_offset);
1757 assert_eq!(imm.size(), imm_size);
1758 imm
1759 };
1760 let imm1_1 = gen_imm(table_id1, epoch1, 0);
1761 let imm1_2_1 = gen_imm(table_id1, epoch2, 0);
1762
1763 let storage_opt = default_opts_for_test();
1764 let metrics = Arc::new(HummockStateStoreMetrics::unused());
1765
1766 let (spawn_task, new_task_notifier) = prepare_uploader_order_test_spawn_task_fn(false);
1767
1768 let refill_config = CacheRefillConfig::from_storage_opts(&storage_opt);
1769 let metadata = TableCacheMetadata::new(
1770 Role::None,
1771 storage_opt.cache_refill_table_cache_refill_default_policy,
1772 );
1773 let refiller = CacheRefiller::new(refill_config, mock_sstable_store().await);
1774 let event_handler = HummockEventHandler::new_inner(
1775 observer_event_rx,
1776 metrics.clone(),
1777 RecentVersions::new(initial_version.clone(), 10, metrics.clone()),
1778 buffer_tracker,
1779 spawn_task,
1780 refiller,
1781 metadata,
1782 );
1783
1784 let event_tx = event_handler.event_sender();
1785
1786 let send_event = |event| event_tx.send(event).unwrap();
1787 let flush_event = || async {
1788 let (tx, rx) = oneshot::channel();
1789 send_event(HummockEvent::FlushEvent(tx));
1790 rx.await.unwrap();
1791 };
1792 let start_epoch = |table_id, epoch| {
1793 send_event(HummockEvent::StartEpoch {
1794 epoch,
1795 table_ids: HashSet::from_iter([table_id]),
1796 })
1797 };
1798 let init_epoch = |instance: &LocalInstanceGuard, init_epoch| {
1799 send_event(HummockEvent::InitEpoch {
1800 instance_id: instance.instance_id,
1801 init_epoch,
1802 })
1803 };
1804 let event_tx_clone = event_tx.clone();
1805 let write_imm = {
1806 let memory_limiter = memory_limiter.clone();
1807 move |read_version: &HummockReadVersionRef,
1808 instance: &LocalInstanceGuard,
1809 imm: &ImmutableMemtable| {
1810 let memory_limiter = memory_limiter.clone();
1811 let event_tx = event_tx_clone.clone();
1812 let read_version = read_version.clone();
1813 let imm = imm.clone();
1814 let instance_id = instance.instance_id;
1815 async move {
1816 let tracker = memory_limiter.require_memory(imm.size() as _).await;
1817 let mut read_version = read_version.write();
1818 read_version.add_pending_imm(imm.clone(), tracker);
1819
1820 event_tx
1821 .send(HummockEvent::ImmToUploader {
1822 instance_id,
1823 imms: read_version.start_upload_pending_imms(),
1824 })
1825 .unwrap();
1826 }
1827 }
1828 };
1829 let seal_epoch = |instance: &LocalInstanceGuard, next_epoch| {
1830 send_event(HummockEvent::LocalSealEpoch {
1831 instance_id: instance.instance_id,
1832 next_epoch,
1833 opts: SealCurrentEpochOptions::for_test(),
1834 })
1835 };
1836 let sync_epoch = |table_id, new_sync_epoch| {
1837 let (tx, rx) = oneshot::channel();
1838 send_event(HummockEvent::SyncEpoch {
1839 sync_result_sender: tx,
1840 sync_table_epochs: vec![(new_sync_epoch, HashSet::from_iter([table_id]))],
1841 });
1842 rx
1843 };
1844
1845 let join_handle = spawn(event_handler.start_hummock_event_handler_worker());
1846
1847 let (read_version1, guard1) = {
1848 let (tx, rx) = oneshot::channel();
1849 send_event(HummockEvent::RegisterReadVersion {
1850 table_id: table_id1,
1851 new_read_version_sender: tx,
1852 is_replicated: false,
1853 vnodes: Arc::new(Bitmap::ones(VirtualNode::COUNT_FOR_TEST)),
1854 });
1855 rx.await.unwrap()
1856 };
1857
1858 let (read_version2, guard2) = {
1859 let (tx, rx) = oneshot::channel();
1860 send_event(HummockEvent::RegisterReadVersion {
1861 table_id: table_id2,
1862 new_read_version_sender: tx,
1863 is_replicated: false,
1864 vnodes: Arc::new(Bitmap::ones(VirtualNode::COUNT_FOR_TEST)),
1865 });
1866 rx.await.unwrap()
1867 };
1868
1869 let (task1_1_finish_tx, task1_1_rx) = {
1871 start_epoch(table_id1, epoch1);
1872
1873 read_version1.write().init();
1874 init_epoch(&guard1, epoch1);
1875
1876 write_imm(&read_version1, &guard1, &imm1_1).await;
1877
1878 start_epoch(table_id1, epoch2);
1879
1880 seal_epoch(&guard1, epoch2);
1881
1882 let (wait_task_start, task_finish_tx) = new_task_notifier(HashMap::from_iter([(
1883 guard1.instance_id,
1884 vec![imm1_1.batch_id()],
1885 )]));
1886
1887 let mut rx = sync_epoch(table_id1, epoch1);
1888 wait_task_start.await;
1889 assert!(poll_fn(|cx| Poll::Ready(rx.poll_unpin(cx).is_pending())).await);
1890
1891 write_imm(&read_version1, &guard1, &imm1_2_1).await;
1892 flush_event().await;
1893
1894 (task_finish_tx, rx)
1895 };
1896 let (task1_2_finish_tx, _finish_txs) = {
1901 let mut finish_txs = vec![];
1902 let imm2_1_1 = gen_imm(table_id2, epoch1, 0);
1903 start_epoch(table_id2, epoch1);
1904 read_version2.write().init();
1905 init_epoch(&guard2, epoch1);
1906 let (wait_task_start, task1_2_finish_tx) = new_task_notifier(HashMap::from_iter([(
1907 guard1.instance_id,
1908 vec![imm1_2_1.batch_id()],
1909 )]));
1910 write_imm(&read_version2, &guard2, &imm2_1_1).await;
1911 wait_task_start.await;
1912
1913 let imm2_1_2 = gen_imm(table_id2, epoch1, 1);
1914 let (wait_task_start, finish_tx) = new_task_notifier(HashMap::from_iter([(
1915 guard2.instance_id,
1916 vec![imm2_1_2.batch_id(), imm2_1_1.batch_id()],
1917 )]));
1918 finish_txs.push(finish_tx);
1919 write_imm(&read_version2, &guard2, &imm2_1_2).await;
1920 wait_task_start.await;
1921
1922 let imm2_1_3 = gen_imm(table_id2, epoch1, 2);
1923 write_imm(&read_version2, &guard2, &imm2_1_3).await;
1924 start_epoch(table_id2, epoch2);
1925 seal_epoch(&guard2, epoch2);
1926 let (wait_task_start, finish_tx) = new_task_notifier(HashMap::from_iter([(
1927 guard2.instance_id,
1928 vec![imm2_1_3.batch_id()],
1929 )]));
1930 finish_txs.push(finish_tx);
1931 let _sync_rx = sync_epoch(table_id2, epoch1);
1932 wait_task_start.await;
1933
1934 let imm2_2_1 = gen_imm(table_id2, epoch2, 0);
1935 write_imm(&read_version2, &guard2, &imm2_2_1).await;
1936 flush_event().await;
1937 let imm2_2_2 = gen_imm(table_id2, epoch2, 1);
1938 write_imm(&read_version2, &guard2, &imm2_2_2).await;
1939 let (wait_task_start, finish_tx) = new_task_notifier(HashMap::from_iter([(
1940 guard2.instance_id,
1941 vec![imm2_2_2.batch_id(), imm2_2_1.batch_id()],
1942 )]));
1943 finish_txs.push(finish_tx);
1944 wait_task_start.await;
1945
1946 let imm2_2_3 = gen_imm(table_id2, epoch2, 2);
1947 write_imm(&read_version2, &guard2, &imm2_2_3).await;
1948
1949 drop(guard2);
1957 let (clear_tx, clear_rx) = oneshot::channel();
1958 send_event(HummockEvent::Clear(
1959 clear_tx,
1960 Some(HashSet::from_iter([table_id2])),
1961 ));
1962 clear_rx.await.unwrap();
1963 (task1_2_finish_tx, finish_txs)
1964 };
1965
1966 let imm1_2_2 = gen_imm(table_id1, epoch2, 1);
1967 write_imm(&read_version1, &guard1, &imm1_2_2).await;
1968 start_epoch(table_id1, epoch3);
1969 seal_epoch(&guard1, epoch3);
1970
1971 let (tx2, mut sync_rx2) = oneshot::channel();
1972 let (wait_task_start, task1_2_2_finish_tx) = new_task_notifier(HashMap::from_iter([(
1973 guard1.instance_id,
1974 vec![imm1_2_2.batch_id()],
1975 )]));
1976 send_event(HummockEvent::SyncEpoch {
1977 sync_result_sender: tx2,
1978 sync_table_epochs: vec![(epoch2, HashSet::from_iter([table_id1]))],
1979 });
1980 wait_task_start.await;
1981 assert!(poll_fn(|cx| Poll::Ready(sync_rx2.poll_unpin(cx).is_pending())).await);
1982
1983 task1_1_finish_tx.send(()).unwrap();
1984 let sync_data1 = task1_1_rx.await.unwrap().unwrap();
1985 assert!(!sync_data1.uploaded_ssts.is_empty());
1986 assert!(
1987 sync_data1
1988 .uploaded_ssts
1989 .iter()
1990 .all(|sst| sst.epochs().as_slice() == [epoch1])
1991 );
1992 task1_2_finish_tx.send(()).unwrap();
1993 assert!(poll_fn(|cx| Poll::Ready(sync_rx2.poll_unpin(cx).is_pending())).await);
1994 task1_2_2_finish_tx.send(()).unwrap();
1995 let sync_data2 = sync_rx2.await.unwrap().unwrap();
1996 assert!(!sync_data2.uploaded_ssts.is_empty());
1997 assert!(
1998 sync_data2
1999 .uploaded_ssts
2000 .iter()
2001 .all(|sst| sst.epochs().as_slice() == [epoch2])
2002 );
2003
2004 send_event(HummockEvent::Shutdown);
2005 join_handle.await.unwrap();
2006 }
2007}