1use std::collections::{HashMap, HashSet};
16use std::sync::Arc;
17
18use bytes::Bytes;
19use itertools::Itertools;
20use risingwave_common::catalog::TableId;
21use risingwave_common::config::Role;
22use risingwave_common::hash::VirtualNode;
23use risingwave_common::id::WorkerId;
24use risingwave_common_service::ObserverManager;
25use risingwave_hummock_sdk::compaction_group::StaticCompactionGroupId;
26use risingwave_hummock_sdk::key::TableKey;
27pub use risingwave_hummock_sdk::key::{gen_key_from_bytes, gen_key_from_str};
28use risingwave_hummock_sdk::vector_index::VectorIndexDelta;
29use risingwave_meta::controller::cluster::ClusterControllerRef;
30use risingwave_meta::hummock::test_utils::{
31 register_table_ids_to_compaction_group, setup_compute_env,
32};
33use risingwave_meta::hummock::{
34 CommitEpochInfo, HummockManagerRef, MockHummockMetaClient, NewTableFragmentInfo,
35};
36use risingwave_meta::manager::MetaSrvEnv;
37use risingwave_pb::catalog::{PbTable, Table};
38use risingwave_pb::hummock::vector_index_delta::PbVectorIndexInit;
39use risingwave_rpc_client::HummockMetaClient;
40use risingwave_storage::compaction_catalog_manager::{
41 CompactionCatalogManager, CompactionCatalogManagerRef,
42};
43use risingwave_storage::error::StorageResult;
44use risingwave_storage::hummock::HummockStorage;
45use risingwave_storage::hummock::backup_reader::BackupReader;
46use risingwave_storage::hummock::event_handler::{HummockObserverEvent, HummockVersionUpdate};
47use risingwave_storage::hummock::iterator::test_utils::mock_sstable_store;
48use risingwave_storage::hummock::local_version::pinned_version::PinnedVersion;
49use risingwave_storage::hummock::observer_manager::HummockObserverNode;
50use risingwave_storage::hummock::test_utils::*;
51use risingwave_storage::hummock::write_limiter::WriteLimiter;
52use risingwave_storage::storage_value::StorageValue;
53use risingwave_storage::store::*;
54use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel};
55
56use crate::mock_notification_client::get_notification_client_for_test;
57
58pub async fn prepare_first_valid_version(
59 env: MetaSrvEnv,
60 hummock_manager_ref: HummockManagerRef,
61 cluster_controller_ref: ClusterControllerRef,
62 worker_id: WorkerId,
63) -> (
64 PinnedVersion,
65 UnboundedSender<HummockObserverEvent>,
66 UnboundedReceiver<HummockObserverEvent>,
67) {
68 let (observer_event_tx, mut observer_event_rx) = unbounded_channel();
69
70 let notification_client = get_notification_client_for_test(
71 env,
72 hummock_manager_ref.clone(),
73 cluster_controller_ref,
74 worker_id,
75 )
76 .await;
77 let backup_manager = BackupReader::unused().await;
78 let write_limiter = WriteLimiter::unused();
79 let observer_manager = ObserverManager::new(
80 notification_client,
81 HummockObserverNode::new(
82 Role::None,
83 Arc::new(CompactionCatalogManager::default()),
84 backup_manager,
85 observer_event_tx.clone(),
86 write_limiter,
87 ),
88 )
89 .await;
90 observer_manager.start().await;
91 let hummock_version = match observer_event_rx.recv().await {
92 Some(HummockObserverEvent::VersionUpdate(HummockVersionUpdate::PinnedVersion(version))) => {
93 version
94 }
95 _ => unreachable!("should be full version"),
96 };
97
98 (
99 PinnedVersion::new(*hummock_version, unbounded_channel().0),
100 observer_event_tx,
101 observer_event_rx,
102 )
103}
104
105#[async_trait::async_trait]
106pub trait TestIngestBatch: LocalStateStore {
107 async fn ingest_batch(
108 &mut self,
109 kv_pairs: Vec<(TableKey<Bytes>, StorageValue)>,
110 ) -> StorageResult<usize>;
111}
112
113#[async_trait::async_trait]
114impl<S: LocalStateStore> TestIngestBatch for S {
115 async fn ingest_batch(
116 &mut self,
117 kv_pairs: Vec<(TableKey<Bytes>, StorageValue)>,
118 ) -> StorageResult<usize> {
119 for (key, value) in kv_pairs {
120 match value.user_value {
121 None => self.delete(key, Bytes::new())?,
122 Some(value) => self.insert(key, value, None)?,
123 }
124 }
125 self.flush().await
126 }
127}
128
129pub async fn with_hummock_storage(
130 table_id: TableId,
131) -> (HummockStorage, Arc<MockHummockMetaClient>) {
132 let sstable_store = mock_sstable_store().await;
133 let hummock_options = Arc::new(default_opts_for_test());
134 let (env, hummock_manager_ref, cluster_ctl_ref, worker_id) = setup_compute_env(8080).await;
135 let meta_client = Arc::new(MockHummockMetaClient::new(
136 hummock_manager_ref.clone(),
137 worker_id as _,
138 ));
139
140 let hummock_storage = HummockStorage::for_test(
141 hummock_options,
142 sstable_store,
143 meta_client.clone(),
144 get_notification_client_for_test(
145 env,
146 hummock_manager_ref.clone(),
147 cluster_ctl_ref,
148 worker_id,
149 )
150 .await,
151 )
152 .await
153 .unwrap();
154
155 register_tables_with_id_for_test(
156 hummock_storage.compaction_catalog_manager_ref(),
157 &hummock_manager_ref,
158 &[table_id],
159 )
160 .await;
161
162 (hummock_storage, meta_client)
163}
164
165pub fn update_filter_key_extractor_for_table_ids(
166 compaction_catalog_manager_ref: CompactionCatalogManagerRef,
167 table_ids: &[TableId],
168) {
169 for table_id in table_ids {
170 let mock_table = PbTable {
171 id: *table_id,
172 read_prefix_len_hint: 1,
176 maybe_vnode_count: Some(VirtualNode::COUNT_FOR_TEST as u32),
177 ..Default::default()
178 };
179 compaction_catalog_manager_ref.update(*table_id, mock_table);
180 }
181}
182
183pub async fn register_tables_with_id_for_test(
184 compaction_catalog_manager_ref: CompactionCatalogManagerRef,
185 hummock_manager_ref: &HummockManagerRef,
186 table_ids: &[TableId],
187) {
188 update_filter_key_extractor_for_table_ids(compaction_catalog_manager_ref, table_ids);
189 register_table_ids_to_compaction_group(
190 hummock_manager_ref,
191 table_ids,
192 StaticCompactionGroupId::StateDefault,
193 )
194 .await;
195}
196
197pub fn update_filter_key_extractor_for_tables(
198 compaction_catalog_manager_ref: CompactionCatalogManagerRef,
199 tables: &[PbTable],
200) {
201 for table in tables {
202 compaction_catalog_manager_ref.update(table.id, table.clone())
203 }
204}
205pub async fn register_tables_with_catalog_for_test(
206 compaction_catalog_manager_ref: CompactionCatalogManagerRef,
207 hummock_manager_ref: &HummockManagerRef,
208 tables: &[Table],
209) {
210 update_filter_key_extractor_for_tables(compaction_catalog_manager_ref, tables);
211 let table_ids = tables.iter().map(|t| t.id).collect_vec();
212 register_table_ids_to_compaction_group(
213 hummock_manager_ref,
214 &table_ids,
215 StaticCompactionGroupId::StateDefault,
216 )
217 .await;
218}
219
220pub struct HummockTestEnv {
221 pub storage: HummockStorage,
222 pub manager: HummockManagerRef,
223 pub meta_client: Arc<MockHummockMetaClient>,
224}
225
226impl HummockTestEnv {
227 async fn wait_version_sync(&self) {
228 self.storage
229 .wait_version(self.manager.get_current_version().await)
230 .await
231 }
232
233 pub async fn register_table_id(&self, table_id: TableId) {
234 register_tables_with_id_for_test(
235 self.storage.compaction_catalog_manager_ref(),
236 &self.manager,
237 &[table_id],
238 )
239 .await;
240 self.wait_version_sync().await;
241 }
242
243 pub async fn register_vector_index(
244 &self,
245 table_id: TableId,
246 init_epoch: u64,
247 init_config: PbVectorIndexInit,
248 ) {
249 self.manager
250 .commit_epoch(CommitEpochInfo {
251 sstables: vec![],
252 new_table_watermarks: Default::default(),
253 sst_to_context: Default::default(),
254 new_table_fragment_infos: vec![NewTableFragmentInfo {
255 table_ids: HashSet::from_iter([table_id]),
256 }],
257 change_log_delta: Default::default(),
258 vector_index_delta: HashMap::from_iter([(
259 table_id,
260 VectorIndexDelta::Init(init_config),
261 )]),
262 tables_to_commit: HashMap::from_iter([(table_id, init_epoch)]),
263 truncate_tables: HashSet::new(),
264 })
265 .await
266 .unwrap();
267 }
268
269 pub async fn register_table(&self, table: PbTable) {
270 register_tables_with_catalog_for_test(
271 self.storage.compaction_catalog_manager_ref(),
272 &self.manager,
273 &[table],
274 )
275 .await;
276 self.wait_version_sync().await;
277 }
278
279 pub async fn commit_epoch(&self, epoch: u64) {
282 let table_ids = self
283 .manager
284 .get_current_version()
285 .await
286 .state_table_info
287 .info()
288 .keys()
289 .cloned()
290 .collect();
291 let res = self
292 .storage
293 .seal_and_sync_epoch(epoch, table_ids)
294 .await
295 .unwrap();
296 self.meta_client.commit_epoch(epoch, res).await.unwrap();
297
298 self.wait_sync_committed_version().await;
299 }
300
301 pub async fn wait_sync_committed_version(&self) {
302 let version = self.manager.get_current_version().await;
303 self.storage.wait_version(version).await;
304 }
305}
306
307pub async fn prepare_hummock_test_env() -> HummockTestEnv {
308 let sstable_store = mock_sstable_store().await;
309 let hummock_options = Arc::new(default_opts_for_test());
310 let (env, hummock_manager_ref, cluster_ctl_ref, worker_id) = setup_compute_env(8080).await;
311
312 let hummock_meta_client = Arc::new(MockHummockMetaClient::new(
313 hummock_manager_ref.clone(),
314 worker_id as _,
315 ));
316
317 let notification_client = get_notification_client_for_test(
318 env,
319 hummock_manager_ref.clone(),
320 cluster_ctl_ref,
321 worker_id,
322 )
323 .await;
324
325 let storage = HummockStorage::for_test(
326 hummock_options,
327 sstable_store,
328 hummock_meta_client.clone(),
329 notification_client,
330 )
331 .await
332 .unwrap();
333
334 HummockTestEnv {
335 storage,
336 manager: hummock_manager_ref,
337 meta_client: hummock_meta_client,
338 }
339}