1use std::collections::{HashMap, HashSet};
16use std::sync::Arc;
17
18use anyhow::anyhow;
19use parking_lot::lock_api::ArcRwLockReadGuard;
20use parking_lot::{RawRwLock, RwLock};
21use risingwave_common::catalog::{
22 AlterDatabaseParam, CatalogVersion, FunctionId, IndexId, ObjectId,
23};
24use risingwave_common::id::{ConnectionId, JobId, SchemaId, SourceId, ViewId};
25use risingwave_common::system_param::AdaptiveParallelismStrategy;
26use risingwave_pb::catalog::{
27 PbComment, PbDatabase, PbFunction, PbIndex, PbSchema, PbSink, PbSource, PbSubscription,
28 PbTable, PbView,
29};
30use risingwave_pb::ddl_service::create_iceberg_table_request::{PbSinkJobInfo, PbTableJobInfo};
31use risingwave_pb::ddl_service::replace_job_plan::{
32 ReplaceJob, ReplaceMaterializedView, ReplaceSink, ReplaceSource, ReplaceTable,
33};
34use risingwave_pb::ddl_service::{
35 PbTableJobType, StreamingJobResourceType, TableJobType, WaitVersion, alter_name_request,
36 alter_owner_request, alter_set_schema_request, alter_swap_rename_request,
37 create_connection_request, streaming_job_resource_type,
38};
39use risingwave_pb::meta::PbTableParallelism;
40use risingwave_pb::stream_plan::StreamFragmentGraph;
41use risingwave_rpc_client::MetaClient;
42use tokio::sync::watch::Receiver;
43
44use super::root_catalog::Catalog;
45use super::{DatabaseId, SecretId, SinkId, SubscriptionId, TableId};
46use crate::error::Result;
47use crate::scheduler::HummockSnapshotManagerRef;
48use crate::session::current::notice_to_user;
49use crate::user::UserId;
50
51pub type CatalogReadGuard = ArcRwLockReadGuard<RawRwLock, Catalog>;
52
53#[derive(Clone)]
55pub struct CatalogReader(Arc<RwLock<Catalog>>);
56
57impl CatalogReader {
58 pub fn new(inner: Arc<RwLock<Catalog>>) -> Self {
59 CatalogReader(inner)
60 }
61
62 pub fn read_guard(&self) -> CatalogReadGuard {
63 self.0.read_arc_recursive()
65 }
66}
67
68#[async_trait::async_trait]
73pub trait CatalogWriter: Send + Sync {
74 async fn create_database(
75 &self,
76 db_name: &str,
77 owner: UserId,
78 resource_group: &str,
79 barrier_interval_ms: Option<u32>,
80 checkpoint_frequency: Option<u64>,
81 ) -> Result<()>;
82
83 async fn create_schema(
84 &self,
85 db_id: DatabaseId,
86 schema_name: &str,
87 owner: UserId,
88 ) -> Result<()>;
89
90 async fn create_view(&self, view: PbView, dependencies: HashSet<ObjectId>) -> Result<()>;
91
92 async fn create_materialized_view(
93 &self,
94 table: PbTable,
95 graph: StreamFragmentGraph,
96 dependencies: HashSet<ObjectId>,
97 resource_type: streaming_job_resource_type::ResourceType,
98 if_not_exists: bool,
99 refresh_interval_sec: Option<u64>,
100 ) -> Result<()>;
101
102 async fn replace_materialized_view(
103 &self,
104 table: PbTable,
105 graph: StreamFragmentGraph,
106 ) -> Result<()>;
107
108 async fn create_table(
109 &self,
110 source: Option<PbSource>,
111 table: PbTable,
112 graph: StreamFragmentGraph,
113 job_type: PbTableJobType,
114 if_not_exists: bool,
115 dependencies: HashSet<ObjectId>,
116 ) -> Result<()>;
117
118 async fn replace_table(
119 &self,
120 source: Option<PbSource>,
121 table: PbTable,
122 graph: StreamFragmentGraph,
123 job_type: TableJobType,
124 ) -> Result<()>;
125
126 async fn replace_source(&self, source: PbSource, graph: StreamFragmentGraph) -> Result<()>;
127
128 async fn create_index(
129 &self,
130 index: PbIndex,
131 table: PbTable,
132 graph: StreamFragmentGraph,
133 resource_type: streaming_job_resource_type::ResourceType,
134 if_not_exists: bool,
135 ) -> Result<()>;
136
137 async fn create_source(
138 &self,
139 source: PbSource,
140 graph: Option<StreamFragmentGraph>,
141 if_not_exists: bool,
142 ) -> Result<()>;
143
144 async fn create_sink(
145 &self,
146 sink: PbSink,
147 graph: StreamFragmentGraph,
148 dependencies: HashSet<ObjectId>,
149 resource_type: streaming_job_resource_type::ResourceType,
150 if_not_exists: bool,
151 since_timestamp_epoch: Option<u64>,
152 ) -> Result<()>;
153
154 async fn replace_sink(
155 &self,
156 old_sink_id: SinkId,
157 sink: PbSink,
158 graph: StreamFragmentGraph,
159 dependencies: HashSet<ObjectId>,
160 resource_type: streaming_job_resource_type::ResourceType,
161 ) -> Result<()>;
162
163 async fn create_subscription(&self, subscription: PbSubscription) -> Result<()>;
164
165 async fn create_function(&self, function: PbFunction) -> Result<()>;
166
167 async fn create_connection(
168 &self,
169 connection_name: String,
170 database_id: DatabaseId,
171 schema_id: SchemaId,
172 owner_id: UserId,
173 connection: create_connection_request::Payload,
174 ) -> Result<()>;
175
176 async fn create_secret(
177 &self,
178 secret_name: String,
179 database_id: DatabaseId,
180 schema_id: SchemaId,
181 owner_id: UserId,
182 payload: Vec<u8>,
183 ) -> Result<()>;
184
185 async fn comment_on(&self, comment: PbComment) -> Result<()>;
186
187 async fn drop_table(
188 &self,
189 source_id: Option<SourceId>,
190 table_id: TableId,
191 cascade: bool,
192 ) -> Result<()>;
193
194 async fn drop_materialized_view(&self, table_id: TableId, cascade: bool) -> Result<()>;
195
196 async fn drop_view(&self, view_id: ViewId, cascade: bool) -> Result<()>;
197
198 async fn drop_source(&self, source_id: SourceId, cascade: bool) -> Result<()>;
199
200 async fn reset_source(&self, source_id: SourceId) -> Result<()>;
201
202 async fn drop_sink(&self, sink_id: SinkId, cascade: bool) -> Result<()>;
203
204 async fn drop_subscription(&self, subscription_id: SubscriptionId, cascade: bool)
205 -> Result<()>;
206
207 async fn drop_database(&self, database_id: DatabaseId) -> Result<()>;
208
209 async fn drop_schema(&self, schema_id: SchemaId, cascade: bool) -> Result<()>;
210
211 async fn drop_index(&self, index_id: IndexId, cascade: bool) -> Result<()>;
212
213 async fn drop_function(&self, function_id: FunctionId, cascade: bool) -> Result<()>;
214
215 async fn drop_connection(&self, connection_id: ConnectionId, cascade: bool) -> Result<()>;
216
217 async fn drop_secret(&self, secret_id: SecretId, cascade: bool) -> Result<()>;
218
219 async fn alter_secret(
220 &self,
221 secret_id: SecretId,
222 secret_name: String,
223 database_id: DatabaseId,
224 schema_id: SchemaId,
225 owner_id: UserId,
226 payload: Vec<u8>,
227 ) -> Result<()>;
228
229 async fn alter_subscription_retention(
230 &self,
231 subscription_id: SubscriptionId,
232 retention_seconds: u64,
233 definition: String,
234 ) -> Result<()>;
235
236 async fn alter_name(
237 &self,
238 object_id: alter_name_request::Object,
239 object_name: &str,
240 ) -> Result<()>;
241
242 async fn alter_owner(
243 &self,
244 object: alter_owner_request::Object,
245 owner_id: UserId,
246 ) -> Result<()>;
247
248 async fn alter_source(&self, source: PbSource) -> Result<()>;
250
251 async fn alter_parallelism(
252 &self,
253 job_id: JobId,
254 parallelism: PbTableParallelism,
255 adaptive_parallelism_strategy: Option<AdaptiveParallelismStrategy>,
256 deferred: bool,
257 ) -> Result<()>;
258
259 async fn alter_backfill_parallelism(
260 &self,
261 job_id: JobId,
262 parallelism: Option<PbTableParallelism>,
263 adaptive_parallelism_strategy: Option<AdaptiveParallelismStrategy>,
264 deferred: bool,
265 ) -> Result<()>;
266
267 async fn alter_config(
268 &self,
269 job_id: JobId,
270 entries_to_add: HashMap<String, String>,
271 keys_to_remove: Vec<String>,
272 ) -> Result<()>;
273
274 async fn alter_resource_group(
275 &self,
276 job_id: JobId,
277 resource_group: Option<String>,
278 deferred: bool,
279 ) -> Result<()>;
280
281 async fn alter_database_resource_group(
282 &self,
283 database_id: DatabaseId,
284 resource_group: Option<String>,
285 deferred: bool,
286 ) -> Result<()>;
287
288 async fn alter_set_schema(
289 &self,
290 object: alter_set_schema_request::Object,
291 new_schema_id: SchemaId,
292 ) -> Result<()>;
293
294 async fn alter_swap_rename(&self, object: alter_swap_rename_request::Object) -> Result<()>;
295
296 async fn alter_database_param(
297 &self,
298 database_id: DatabaseId,
299 param: AlterDatabaseParam,
300 ) -> Result<()>;
301
302 async fn create_iceberg_table(
303 &self,
304 table_job_info: PbTableJobInfo,
305 sink_job_info: PbSinkJobInfo,
306 iceberg_source: PbSource,
307 if_not_exists: bool,
308 ) -> Result<()>;
309
310 async fn wait(&self, job_id: Option<JobId>) -> Result<()>;
311}
312
313#[derive(Clone)]
314pub struct CatalogWriterImpl {
315 meta_client: MetaClient,
316 catalog_updated_rx: Receiver<CatalogVersion>,
317 hummock_snapshot_manager: HummockSnapshotManagerRef,
318}
319
320#[async_trait::async_trait]
321impl CatalogWriter for CatalogWriterImpl {
322 async fn create_database(
323 &self,
324 db_name: &str,
325 owner: UserId,
326 resource_group: &str,
327 barrier_interval_ms: Option<u32>,
328 checkpoint_frequency: Option<u64>,
329 ) -> Result<()> {
330 let version = self
331 .meta_client
332 .create_database(PbDatabase {
333 name: db_name.to_owned(),
334 id: 0.into(),
335 owner,
336 resource_group: resource_group.to_owned(),
337 barrier_interval_ms,
338 checkpoint_frequency,
339 })
340 .await?;
341 self.wait_version(version).await
342 }
343
344 async fn create_schema(
345 &self,
346 db_id: DatabaseId,
347 schema_name: &str,
348 owner: UserId,
349 ) -> Result<()> {
350 let version = self
351 .meta_client
352 .create_schema(PbSchema {
353 id: 0.into(),
354 name: schema_name.to_owned(),
355 database_id: db_id,
356 owner,
357 })
358 .await?;
359 self.wait_version(version).await
360 }
361
362 async fn create_materialized_view(
364 &self,
365 table: PbTable,
366 graph: StreamFragmentGraph,
367 dependencies: HashSet<ObjectId>,
368 resource_type: streaming_job_resource_type::ResourceType,
369 if_not_exists: bool,
370 refresh_interval_sec: Option<u64>,
371 ) -> Result<()> {
372 let version = self
373 .meta_client
374 .create_materialized_view(
375 table,
376 graph,
377 dependencies,
378 resource_type,
379 if_not_exists,
380 refresh_interval_sec,
381 )
382 .await?;
383 self.wait_version(version).await?;
384 Ok(())
385 }
386
387 async fn replace_materialized_view(
388 &self,
389 table: PbTable,
390 graph: StreamFragmentGraph,
391 ) -> Result<()> {
392 notice_to_user(format!("table: {table:#?}"));
394 notice_to_user(format!("graph: {graph:#?}"));
395
396 let version = self
397 .meta_client
398 .replace_job(
399 graph,
400 ReplaceJob::ReplaceMaterializedView(ReplaceMaterializedView { table: Some(table) }),
401 )
402 .await?;
403
404 self.wait_version(version).await
405 }
406
407 async fn create_view(&self, view: PbView, dependencies: HashSet<ObjectId>) -> Result<()> {
408 let version = self.meta_client.create_view(view, dependencies).await?;
409 self.wait_version(version).await
410 }
411
412 async fn create_index(
413 &self,
414 index: PbIndex,
415 table: PbTable,
416 graph: StreamFragmentGraph,
417 resource_type: streaming_job_resource_type::ResourceType,
418 if_not_exists: bool,
419 ) -> Result<()> {
420 let version = self
421 .meta_client
422 .create_index(index, table, graph, resource_type, if_not_exists)
423 .await?;
424 self.wait_version(version).await
425 }
426
427 async fn create_table(
428 &self,
429 source: Option<PbSource>,
430 table: PbTable,
431 graph: StreamFragmentGraph,
432 job_type: PbTableJobType,
433 if_not_exists: bool,
434 dependencies: HashSet<ObjectId>,
435 ) -> Result<()> {
436 let version = self
437 .meta_client
438 .create_table(source, table, graph, job_type, if_not_exists, dependencies)
439 .await?;
440 self.wait_version(version).await
441 }
442
443 async fn replace_table(
444 &self,
445 source: Option<PbSource>,
446 table: PbTable,
447 graph: StreamFragmentGraph,
448 job_type: TableJobType,
449 ) -> Result<()> {
450 let version = self
451 .meta_client
452 .replace_job(
453 graph,
454 ReplaceJob::ReplaceTable(ReplaceTable {
455 source,
456 table: Some(table),
457 job_type: job_type as _,
458 }),
459 )
460 .await?;
461 self.wait_version(version).await
462 }
463
464 async fn replace_source(&self, source: PbSource, graph: StreamFragmentGraph) -> Result<()> {
465 let version = self
466 .meta_client
467 .replace_job(
468 graph,
469 ReplaceJob::ReplaceSource(ReplaceSource {
470 source: Some(source),
471 }),
472 )
473 .await?;
474 self.wait_version(version).await
475 }
476
477 async fn create_source(
478 &self,
479 source: PbSource,
480 graph: Option<StreamFragmentGraph>,
481 if_not_exists: bool,
482 ) -> Result<()> {
483 let version = self
484 .meta_client
485 .create_source(source, graph, if_not_exists)
486 .await?;
487 self.wait_version(version).await
488 }
489
490 async fn create_sink(
491 &self,
492 sink: PbSink,
493 graph: StreamFragmentGraph,
494 dependencies: HashSet<ObjectId>,
495 resource_type: streaming_job_resource_type::ResourceType,
496 if_not_exists: bool,
497 since_timestamp_epoch: Option<u64>,
498 ) -> Result<()> {
499 let version = self
500 .meta_client
501 .create_sink(
502 sink,
503 graph,
504 dependencies,
505 resource_type,
506 if_not_exists,
507 since_timestamp_epoch,
508 )
509 .await?;
510 self.wait_version(version).await
511 }
512
513 async fn replace_sink(
514 &self,
515 old_sink_id: SinkId,
516 sink: PbSink,
517 graph: StreamFragmentGraph,
518 dependencies: HashSet<ObjectId>,
519 resource_type: streaming_job_resource_type::ResourceType,
520 ) -> Result<()> {
521 let version = self
522 .meta_client
523 .replace_job(
524 graph,
525 ReplaceJob::ReplaceSink(ReplaceSink {
526 sink: Some(sink),
527 old_sink_id,
528 dependencies: dependencies.into_iter().collect(),
529 resource_type: Some(StreamingJobResourceType {
530 resource_type: Some(resource_type),
531 }),
532 }),
533 )
534 .await?;
535 self.wait_version(version).await
536 }
537
538 async fn create_subscription(&self, subscription: PbSubscription) -> Result<()> {
539 let version = self.meta_client.create_subscription(subscription).await?;
540 self.wait_version(version).await
541 }
542
543 async fn create_function(&self, function: PbFunction) -> Result<()> {
544 let version = self.meta_client.create_function(function).await?;
545 self.wait_version(version).await
546 }
547
548 async fn create_connection(
549 &self,
550 connection_name: String,
551 database_id: DatabaseId,
552 schema_id: SchemaId,
553 owner_id: UserId,
554 connection: create_connection_request::Payload,
555 ) -> Result<()> {
556 let version = self
557 .meta_client
558 .create_connection(
559 connection_name,
560 database_id,
561 schema_id,
562 owner_id,
563 connection,
564 )
565 .await?;
566 self.wait_version(version).await
567 }
568
569 async fn create_secret(
570 &self,
571 secret_name: String,
572 database_id: DatabaseId,
573 schema_id: SchemaId,
574 owner_id: UserId,
575 payload: Vec<u8>,
576 ) -> Result<()> {
577 let version = self
578 .meta_client
579 .create_secret(secret_name, database_id, schema_id, owner_id, payload)
580 .await?;
581 self.wait_version(version).await
582 }
583
584 async fn comment_on(&self, comment: PbComment) -> Result<()> {
585 let version = self.meta_client.comment_on(comment).await?;
586 self.wait_version(version).await
587 }
588
589 async fn drop_table(
590 &self,
591 source_id: Option<SourceId>,
592 table_id: TableId,
593 cascade: bool,
594 ) -> Result<()> {
595 let version = self
596 .meta_client
597 .drop_table(source_id, table_id, cascade)
598 .await?;
599 self.wait_version(version).await
600 }
601
602 async fn drop_materialized_view(&self, table_id: TableId, cascade: bool) -> Result<()> {
603 let version = self
604 .meta_client
605 .drop_materialized_view(table_id, cascade)
606 .await?;
607 self.wait_version(version).await
608 }
609
610 async fn drop_view(&self, view_id: ViewId, cascade: bool) -> Result<()> {
611 let version = self.meta_client.drop_view(view_id, cascade).await?;
612 self.wait_version(version).await
613 }
614
615 async fn drop_source(&self, source_id: SourceId, cascade: bool) -> Result<()> {
616 let version = self.meta_client.drop_source(source_id, cascade).await?;
617 self.wait_version(version).await
618 }
619
620 async fn reset_source(&self, source_id: SourceId) -> Result<()> {
621 let version = self.meta_client.reset_source(source_id).await?;
622 self.wait_version(version).await
623 }
624
625 async fn drop_sink(&self, sink_id: SinkId, cascade: bool) -> Result<()> {
626 let version = self.meta_client.drop_sink(sink_id, cascade).await?;
627 self.wait_version(version).await
628 }
629
630 async fn drop_subscription(
631 &self,
632 subscription_id: SubscriptionId,
633 cascade: bool,
634 ) -> Result<()> {
635 let version = self
636 .meta_client
637 .drop_subscription(subscription_id, cascade)
638 .await?;
639 self.wait_version(version).await
640 }
641
642 async fn drop_index(&self, index_id: IndexId, cascade: bool) -> Result<()> {
643 let version = self.meta_client.drop_index(index_id, cascade).await?;
644 self.wait_version(version).await
645 }
646
647 async fn drop_function(&self, function_id: FunctionId, cascade: bool) -> Result<()> {
648 let version = self.meta_client.drop_function(function_id, cascade).await?;
649 self.wait_version(version).await
650 }
651
652 async fn drop_schema(&self, schema_id: SchemaId, cascade: bool) -> Result<()> {
653 let version = self.meta_client.drop_schema(schema_id, cascade).await?;
654 self.wait_version(version).await
655 }
656
657 async fn drop_database(&self, database_id: DatabaseId) -> Result<()> {
658 let version = self.meta_client.drop_database(database_id).await?;
659 self.wait_version(version).await
660 }
661
662 async fn drop_connection(&self, connection_id: ConnectionId, cascade: bool) -> Result<()> {
663 let version = self
664 .meta_client
665 .drop_connection(connection_id, cascade)
666 .await?;
667 self.wait_version(version).await
668 }
669
670 async fn drop_secret(&self, secret_id: SecretId, cascade: bool) -> Result<()> {
671 let version = self.meta_client.drop_secret(secret_id, cascade).await?;
672 self.wait_version(version).await
673 }
674
675 async fn alter_name(
676 &self,
677 object_id: alter_name_request::Object,
678 object_name: &str,
679 ) -> Result<()> {
680 let version = self.meta_client.alter_name(object_id, object_name).await?;
681 self.wait_version(version).await
682 }
683
684 async fn alter_owner(
685 &self,
686 object: alter_owner_request::Object,
687 owner_id: UserId,
688 ) -> Result<()> {
689 let version = self.meta_client.alter_owner(object, owner_id).await?;
690 self.wait_version(version).await
691 }
692
693 async fn alter_set_schema(
694 &self,
695 object: alter_set_schema_request::Object,
696 new_schema_id: SchemaId,
697 ) -> Result<()> {
698 let version = self
699 .meta_client
700 .alter_set_schema(object, new_schema_id)
701 .await?;
702 self.wait_version(version).await
703 }
704
705 async fn alter_source(&self, source: PbSource) -> Result<()> {
706 let version = self.meta_client.alter_source(source).await?;
707 self.wait_version(version).await
708 }
709
710 async fn alter_parallelism(
711 &self,
712 job_id: JobId,
713 parallelism: PbTableParallelism,
714 adaptive_parallelism_strategy: Option<AdaptiveParallelismStrategy>,
715 deferred: bool,
716 ) -> Result<()> {
717 self.meta_client
718 .alter_parallelism(job_id, parallelism, adaptive_parallelism_strategy, deferred)
719 .await?;
720 Ok(())
721 }
722
723 async fn alter_backfill_parallelism(
724 &self,
725 job_id: JobId,
726 parallelism: Option<PbTableParallelism>,
727 adaptive_parallelism_strategy: Option<AdaptiveParallelismStrategy>,
728 deferred: bool,
729 ) -> Result<()> {
730 self.meta_client
731 .alter_backfill_parallelism(
732 job_id,
733 parallelism,
734 adaptive_parallelism_strategy,
735 deferred,
736 )
737 .await?;
738 Ok(())
739 }
740
741 async fn alter_config(
742 &self,
743 job_id: JobId,
744 entries_to_add: HashMap<String, String>,
745 keys_to_remove: Vec<String>,
746 ) -> Result<()> {
747 self.meta_client
748 .alter_streaming_job_config(job_id, entries_to_add, keys_to_remove)
749 .await?;
750 Ok(())
751 }
752
753 async fn alter_swap_rename(&self, object: alter_swap_rename_request::Object) -> Result<()> {
754 let version = self.meta_client.alter_swap_rename(object).await?;
755 self.wait_version(version).await
756 }
757
758 async fn alter_secret(
759 &self,
760 secret_id: SecretId,
761 secret_name: String,
762 database_id: DatabaseId,
763 schema_id: SchemaId,
764 owner_id: UserId,
765 payload: Vec<u8>,
766 ) -> Result<()> {
767 let version = self
768 .meta_client
769 .alter_secret(
770 secret_id,
771 secret_name,
772 database_id,
773 schema_id,
774 owner_id,
775 payload,
776 )
777 .await?;
778 self.wait_version(version).await
779 }
780
781 async fn alter_subscription_retention(
782 &self,
783 subscription_id: SubscriptionId,
784 retention_seconds: u64,
785 definition: String,
786 ) -> Result<()> {
787 let version = self
788 .meta_client
789 .alter_subscription_retention(subscription_id, retention_seconds, definition)
790 .await?;
791 self.wait_version(version).await
792 }
793
794 async fn alter_resource_group(
795 &self,
796 job_id: JobId,
797 resource_group: Option<String>,
798 deferred: bool,
799 ) -> Result<()> {
800 self.meta_client
801 .alter_resource_group(job_id, resource_group, deferred)
802 .await
803 .map_err(|e| anyhow!(e))?;
804
805 Ok(())
806 }
807
808 async fn alter_database_resource_group(
809 &self,
810 database_id: DatabaseId,
811 resource_group: Option<String>,
812 deferred: bool,
813 ) -> Result<()> {
814 let version = self
815 .meta_client
816 .alter_database_resource_group(database_id, resource_group, deferred)
817 .await
818 .map_err(|e| anyhow!(e))?;
819 self.wait_version(version).await
820 }
821
822 async fn alter_database_param(
823 &self,
824 database_id: DatabaseId,
825 param: AlterDatabaseParam,
826 ) -> Result<()> {
827 let version = self
828 .meta_client
829 .alter_database_param(database_id, param)
830 .await
831 .map_err(|e| anyhow!(e))?;
832 self.wait_version(version).await
833 }
834
835 async fn create_iceberg_table(
836 &self,
837 table_job_info: PbTableJobInfo,
838 sink_job_info: PbSinkJobInfo,
839 iceberg_source: PbSource,
840 if_not_exists: bool,
841 ) -> Result<()> {
842 let version = Box::pin(self.meta_client.create_iceberg_table(
843 table_job_info,
844 sink_job_info,
845 iceberg_source,
846 if_not_exists,
847 ))
848 .await?;
849 self.wait_version(version).await
850 }
851
852 async fn wait(&self, job_id: Option<JobId>) -> Result<()> {
853 let version = self
854 .meta_client
855 .wait(job_id)
856 .await
857 .map_err(|e| anyhow!(e))?;
858 self.wait_version(version).await
859 }
860}
861
862impl CatalogWriterImpl {
863 pub fn new(
864 meta_client: MetaClient,
865 catalog_updated_rx: Receiver<CatalogVersion>,
866 hummock_snapshot_manager: HummockSnapshotManagerRef,
867 ) -> Self {
868 Self {
869 meta_client,
870 catalog_updated_rx,
871 hummock_snapshot_manager,
872 }
873 }
874
875 async fn wait_version(&self, version: WaitVersion) -> Result<()> {
876 let mut rx = self.catalog_updated_rx.clone();
877 while *rx.borrow_and_update() < version.catalog_version {
878 rx.changed().await.map_err(|e| anyhow!(e))?;
879 }
880 self.hummock_snapshot_manager
881 .wait(version.hummock_version_id)
882 .await;
883 Ok(())
884 }
885}