Skip to main content

risingwave_storage/hummock/event_handler/
hummock_event_handler.rs

1// Copyright 2022 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::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    /// Return true when the buffer size minus current upload task size is still greater than the
116    /// flush threshold.
117    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    /// A copy of `read_version_mapping` but owned by event handler
334    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    // Table refill runtime config applies by field presence for both snapshot
367    // and update notifications.
368    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
550// Handler for different events
551impl HummockEventHandler {
552    /// Applies the function to local read versions selected by instance ids.
553    /// Each read version is protected by its own write lock.
554    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                // check duplication on debug_mode
563                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                // Only print warn log when failed to acquire more than 10%
593                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        // Notify completion of the Clear event.
692        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            // Preserve Foyer's JoinError and detach-on-drop behavior at the adapter boundary.
734            let condition = async move { handle.await.unwrap() }.boxed();
735            self.version_updates.enqueue(event, condition);
736        }
737    }
738
739    /// Publishes a non-empty FIFO batch, keeping its targets pinned until all holders advance.
740    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            // The queue enforces the chain; watch readers only observe the batch's final version.
780            *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    /// Gracefully shutdown if returns `true`.
832    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            // The newly uploaded `sstable_infos` contains newer data. Therefore,
1054            // `newly_upload_ssts` at the front
1055            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        // A full snapshot and a multi-delta payload both enter through the production handler.
1188        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        // Dequeue transfers the target pins to `events`, including the intermediate version.
1255        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        // History retains the committing intermediate version after publication.
1262        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        // Main detaches already-started Foyer tasks when the handler shuts down.
1296        drop(handler);
1297        // Dropping history releases version 2; the unpublished version 5 is also released.
1298        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        // Reader-owned snapshots outlive the queue and handler, until their last holder drops.
1311        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        // The buffer can hold at most 1 imm. When a new imm is added, the previous one will be spilled, and the newly added one will be retained.
1752        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        // prepare data of table1
1870        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        // by now, the state in uploader of table_id1
1897        // unsync:  epoch2 -> [imm1_2]
1898        // syncing: epoch1 -> [imm1_1]
1899
1900        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            // by now, the state in uploader of table_id2
1950            // syncing: epoch1 -> spill: [imm2_1_2, imm2_1_1], sync: [imm2_1_3]
1951            // unsync: epoch2 -> spilling: [imm2_2_2, imm2_2_1], imm: [imm2_2_3]
1952            // the state in uploader of table_id1
1953            // unsync:  epoch2 -> spilling [imm1_2]
1954            // syncing: epoch1 -> [imm1_1]
1955
1956            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}