1#![cfg(any(test, feature = "test"))]
16
17use std::collections::{BTreeSet, HashMap};
18use std::sync::{Arc, LazyLock};
19use std::time::Duration;
20
21use bytes::Bytes;
22use itertools::Itertools;
23use risingwave_common::catalog::{TableId, TableOption};
24use risingwave_common::util::epoch::test_epoch;
25use risingwave_hummock_sdk::key::key_with_epoch;
26use risingwave_hummock_sdk::key_range::KeyRange;
27use risingwave_hummock_sdk::level::Levels;
28use risingwave_hummock_sdk::sstable_info::{SstableInfo, SstableInfoInner};
29use risingwave_hummock_sdk::table_watermark::TableWatermarks;
30use risingwave_hummock_sdk::version::{HummockVersion, HummockVersionStateTableInfo};
31use risingwave_hummock_sdk::{
32 CompactionGroupId, HummockEpoch, HummockSstableObjectId, LocalSstableInfo, SyncResult,
33};
34use risingwave_meta_model::WorkerId;
35use risingwave_pb::common::worker_node::Property;
36use risingwave_pb::common::{HostAddress, WorkerType};
37use risingwave_pb::hummock::CompactionConfig;
38use risingwave_pb::hummock::compact_task::TaskStatus;
39use risingwave_rpc_client::HummockMetaClient;
40
41use crate::controller::catalog::CatalogController;
42use crate::controller::cluster::{ClusterController, ClusterControllerRef};
43use crate::hummock::compaction::compaction_config::CompactionConfigBuilder;
44use crate::hummock::compaction::in_progress_compaction::InProgressCompactionView;
45use crate::hummock::compaction::selector::{LocalSelectorStatistic, default_compaction_selector};
46use crate::hummock::compaction::{CompactionDeveloperConfig, CompactionSelectorContext};
47use crate::hummock::level_handler::LevelHandler;
48pub use crate::hummock::manager::CommitEpochInfo;
49use crate::hummock::model::CompactionGroup;
50use crate::hummock::{CompactorManager, HummockManager, HummockManagerRef};
51use crate::manager::MetaSrvEnv;
52use crate::rpc::metrics::MetaMetrics;
53
54static EMPTY_IN_PROGRESS_COMPACTION_VIEW: LazyLock<InProgressCompactionView> =
55 LazyLock::new(InProgressCompactionView::default);
56
57#[cfg(test)]
59pub(crate) async fn advance_time(duration: Duration) {
60 #[cfg(not(madsim))]
61 tokio::time::advance(duration).await;
62 #[cfg(madsim)]
63 tokio::time::advance(duration);
64}
65
66pub fn to_local_sstable_info(ssts: &[SstableInfo]) -> Vec<LocalSstableInfo> {
67 ssts.iter()
68 .map(|sst| LocalSstableInfo::for_test(sst.clone()))
69 .collect_vec()
70}
71
72pub async fn add_test_tables(
78 hummock_manager: &HummockManager,
79 hummock_meta_client: Arc<dyn HummockMetaClient>,
80 compaction_group_id: CompactionGroupId,
81) -> Vec<Vec<SstableInfo>> {
82 use risingwave_common::util::epoch::EpochExt;
85
86 let mut epoch = test_epoch(1);
87 let sstable_ids = get_sst_ids(hummock_manager, 3).await;
88 let test_tables = generate_test_sstables_with_table_id(epoch, 1, sstable_ids);
89 register_sstable_infos_to_compaction_group(hummock_manager, &test_tables, compaction_group_id)
90 .await;
91 let test_local_tables = to_local_sstable_info(&test_tables);
92 hummock_meta_client
93 .commit_epoch(
94 epoch,
95 SyncResult {
96 uncommitted_ssts: test_local_tables,
97 ..Default::default()
98 },
99 )
100 .await
101 .unwrap();
102
103 let test_tables_2 = generate_test_tables(epoch, get_sst_ids(hummock_manager, 1).await);
105 register_sstable_infos_to_compaction_group(
106 hummock_manager,
107 &test_tables_2,
108 compaction_group_id,
109 )
110 .await;
111 let mut compact_task = hummock_manager
112 .get_compact_task(compaction_group_id, &mut *default_compaction_selector())
113 .await
114 .unwrap()
115 .unwrap();
116 assert_eq!(
117 compact_task
118 .input_ssts
119 .iter()
120 .map(|i| i.table_infos.len())
121 .sum::<usize>(),
122 3
123 );
124
125 compact_task.target_level = 6;
126 hummock_manager
127 .report_compact_task_for_test(
128 compact_task.task_id,
129 Some(compact_task),
130 TaskStatus::Success,
131 test_tables_2.clone(),
132 None,
133 )
134 .await
135 .unwrap();
136 epoch.inc_epoch();
138 let test_tables_3 = generate_test_tables(epoch, get_sst_ids(hummock_manager, 1).await);
139 register_sstable_infos_to_compaction_group(
140 hummock_manager,
141 &test_tables_3,
142 compaction_group_id,
143 )
144 .await;
145 let test_local_tables_3 = to_local_sstable_info(&test_tables_3);
146 hummock_meta_client
147 .commit_epoch(
148 epoch,
149 SyncResult {
150 uncommitted_ssts: test_local_tables_3,
151 ..Default::default()
152 },
153 )
154 .await
155 .unwrap();
156 vec![test_tables, test_tables_2, test_tables_3]
157}
158
159pub fn generate_test_sstables_with_table_id(
160 epoch: u64,
161 table_id: impl Into<TableId>,
162 sst_ids: Vec<u64>,
163) -> Vec<SstableInfo> {
164 let table_id = table_id.into();
165 let mut sst_info = vec![];
166 for (i, sst_id) in sst_ids.into_iter().enumerate() {
167 let object_size = 2;
168 sst_info.push(
169 SstableInfoInner {
170 object_id: sst_id.into(),
171 sst_id: sst_id.into(),
172 key_range: KeyRange {
173 left: Bytes::from(key_with_epoch(
174 format!("{:03}\0\0_key_test_{:05}", table_id, i + 1)
175 .as_bytes()
176 .to_vec(),
177 epoch,
178 )),
179 right: Bytes::from(key_with_epoch(
180 format!("{:03}\0\0_key_test_{:05}", table_id, (i + 1) * 10)
181 .as_bytes()
182 .to_vec(),
183 epoch,
184 )),
185 right_exclusive: false,
186 },
187 file_size: object_size,
188 table_ids: vec![table_id],
189 uncompressed_file_size: object_size,
190 max_epoch: epoch,
191 sst_size: object_size,
192 ..Default::default()
193 }
194 .into(),
195 );
196 }
197 sst_info
198}
199
200pub fn generate_test_tables(epoch: u64, sst_ids: Vec<u64>) -> Vec<SstableInfo> {
201 let mut sst_info = vec![];
202 for (i, sst_id) in sst_ids.into_iter().enumerate() {
203 let object_size = 2;
204 sst_info.push(
205 SstableInfoInner {
206 object_id: sst_id.into(),
207 sst_id: sst_id.into(),
208 key_range: KeyRange {
209 left: Bytes::from(iterator_test_key_of_epoch(sst_id, i + 1, epoch)),
210 right: Bytes::from(iterator_test_key_of_epoch(sst_id, (i + 1) * 10, epoch)),
211 right_exclusive: false,
212 },
213 file_size: object_size,
214 table_ids: vec![(sst_id as u32).into(), (sst_id as u32 * 10000).into()],
215 uncompressed_file_size: object_size,
216 max_epoch: epoch,
217 sst_size: object_size,
218 ..Default::default()
219 }
220 .into(),
221 );
222 }
223 sst_info
224}
225
226pub async fn register_sstable_infos_to_compaction_group(
227 compaction_group_manager_ref: &HummockManager,
228 sstable_infos: &[SstableInfo],
229 compaction_group_id: CompactionGroupId,
230) {
231 let table_ids = sstable_infos
232 .iter()
233 .flat_map(|sstable_info| &sstable_info.table_ids)
234 .sorted()
235 .dedup()
236 .copied()
237 .collect_vec();
238 register_table_ids_to_compaction_group(
239 compaction_group_manager_ref,
240 &table_ids,
241 compaction_group_id,
242 )
243 .await;
244}
245
246pub async fn register_table_ids_to_compaction_group(
247 hummock_manager_ref: &HummockManager,
248 table_ids: &[impl Into<TableId> + Copy],
249 compaction_group_id: CompactionGroupId,
250) {
251 hummock_manager_ref
252 .register_table_ids_for_test(
253 &table_ids
254 .iter()
255 .map(|table_id| (*table_id, compaction_group_id))
256 .collect_vec(),
257 )
258 .await
259 .unwrap();
260}
261
262pub async fn unregister_table_ids_from_compaction_group(
263 hummock_manager_ref: &HummockManager,
264 table_ids: &[impl Into<TableId> + Copy],
265) {
266 hummock_manager_ref
267 .unregister_table_ids(table_ids.iter().map(|table_id| (*table_id).into()))
268 .await
269 .unwrap();
270}
271
272pub fn iterator_test_key_of_epoch(table: u64, idx: usize, ts: HummockEpoch) -> Vec<u8> {
274 key_with_epoch(
276 format!("{:03}\0\0_key_test_{:05}", table, idx)
277 .as_bytes()
278 .to_vec(),
279 ts,
280 )
281}
282
283pub fn get_sorted_object_ids(sstables: &[SstableInfo]) -> Vec<HummockSstableObjectId> {
284 sstables
285 .iter()
286 .map(|table| table.object_id)
287 .sorted()
288 .collect_vec()
289}
290
291pub fn get_sorted_committed_object_ids(
292 hummock_version: &HummockVersion,
293 compaction_group_id: CompactionGroupId,
294) -> Vec<HummockSstableObjectId> {
295 let levels = match hummock_version.levels.get(&compaction_group_id) {
296 Some(levels) => levels,
297 None => return vec![],
298 };
299 levels
300 .levels
301 .iter()
302 .chain(levels.l0.sub_levels.iter())
303 .flat_map(|levels| levels.table_infos.iter().map(|info| info.object_id))
304 .sorted()
305 .collect_vec()
306}
307
308pub async fn setup_compute_env_with_config(
309 port: i32,
310 config: CompactionConfig,
311) -> (
312 MetaSrvEnv,
313 HummockManagerRef,
314 ClusterControllerRef,
315 WorkerId,
316) {
317 setup_compute_env_with_metric(port, config, None).await
318}
319
320pub async fn setup_compute_env_with_metric(
321 port: i32,
322 config: CompactionConfig,
323 meta_metric: Option<MetaMetrics>,
324) -> (
325 MetaSrvEnv,
326 HummockManagerRef,
327 ClusterControllerRef,
328 WorkerId,
329) {
330 let env = MetaSrvEnv::for_test().await;
331 let cluster_ctl = Arc::new(
332 ClusterController::new(env.clone(), Duration::from_secs(1))
333 .await
334 .unwrap(),
335 );
336 let catalog_ctl = Arc::new(CatalogController::new(env.clone()).await.unwrap());
337
338 let compactor_manager = Arc::new(CompactorManager::for_test());
339
340 let (compactor_streams_change_tx, _compactor_streams_change_rx) =
341 tokio::sync::mpsc::unbounded_channel();
342
343 let hummock_manager = HummockManager::with_config(
344 env.clone(),
345 cluster_ctl.clone(),
346 catalog_ctl,
347 Arc::new(meta_metric.unwrap_or_default()),
348 compactor_manager,
349 config,
350 compactor_streams_change_tx,
351 )
352 .await;
353
354 let fake_host_address = HostAddress {
355 host: "127.0.0.1".to_owned(),
356 port,
357 };
358 let fake_parallelism = 4;
359 let worker_id = cluster_ctl
360 .add_worker(
361 WorkerType::ComputeNode,
362 fake_host_address,
363 Property {
364 is_streaming: true,
365 is_serving: true,
366 parallelism: fake_parallelism as _,
367 ..Default::default()
368 },
369 Default::default(),
370 )
371 .await
372 .unwrap();
373 (env, hummock_manager, cluster_ctl, worker_id)
374}
375
376pub async fn setup_compute_env(
377 port: i32,
378) -> (
379 MetaSrvEnv,
380 HummockManagerRef,
381 ClusterControllerRef,
382 WorkerId,
383) {
384 let config = CompactionConfigBuilder::new()
385 .level0_tier_compact_file_number(1)
386 .level0_max_compact_file_number(130)
387 .level0_sub_level_compact_level_count(1)
388 .level0_overlapping_sub_level_compact_level_count(1)
389 .build();
390 setup_compute_env_with_config(port, config).await
391}
392
393pub async fn get_sst_ids(hummock_manager: &HummockManager, number: u32) -> Vec<u64> {
394 let range = hummock_manager.get_new_object_ids(number).await.unwrap();
395 (range.start_id..range.end_id)
396 .map(|id| id.as_raw_id())
397 .collect_vec()
398}
399
400pub async fn add_ssts(
401 epoch: HummockEpoch,
402 hummock_manager: &HummockManager,
403 hummock_meta_client: Arc<dyn HummockMetaClient>,
404) -> Vec<SstableInfo> {
405 let table_ids = get_sst_ids(hummock_manager, 3).await;
406 let test_tables = generate_test_sstables_with_table_id(test_epoch(epoch), 1, table_ids);
407 let ssts = to_local_sstable_info(&test_tables);
408 hummock_meta_client
409 .commit_epoch(
410 epoch,
411 SyncResult {
412 uncommitted_ssts: ssts,
413 ..Default::default()
414 },
415 )
416 .await
417 .unwrap();
418 test_tables
419}
420
421pub fn compaction_selector_context<'a>(
422 group: &'a CompactionGroup,
423 levels: &'a Levels,
424 member_table_ids: &'a BTreeSet<TableId>,
425 level_handlers: &'a mut [LevelHandler],
426 selector_stats: &'a mut LocalSelectorStatistic,
427 table_id_to_options: &'a HashMap<TableId, TableOption>,
428 developer_config: Arc<CompactionDeveloperConfig>,
429 table_watermarks: &'a HashMap<TableId, Arc<TableWatermarks>>,
430 state_table_info: &'a HummockVersionStateTableInfo,
431) -> CompactionSelectorContext<'a> {
432 CompactionSelectorContext {
433 group,
434 levels,
435 member_table_ids,
436 level_handlers,
437 selector_stats,
438 table_id_to_options,
439 developer_config,
440 table_watermarks,
441 state_table_info,
442 in_progress_compactions: &EMPTY_IN_PROGRESS_COMPACTION_VIEW,
443 }
444}
445
446pub async fn get_compaction_group_id_by_table_id(
447 hummock_manager_ref: HummockManagerRef,
448 table_id: impl Into<TableId>,
449) -> CompactionGroupId {
450 let version = hummock_manager_ref.get_current_version().await;
451 let mapping = version.state_table_info.build_table_compaction_group_id();
452 *mapping.get(&table_id.into()).unwrap()
453}