Skip to main content

risingwave_frontend/catalog/
catalog_service.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::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/// [`CatalogReader`] can read catalog from local catalog and force the holder can not modify it.
54#[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        // Make this recursive so that one can get this guard in the same thread without fear.
64        self.0.read_arc_recursive()
65    }
66}
67
68/// [`CatalogWriter`] initiate DDL operations (create table/schema/database/function/connection).
69/// It will only send rpc to meta and get the catalog version as response.
70/// Then it will wait for the local catalog to be synced to the version, which is performed by
71/// [observer](`crate::observer::FrontendObserverNode`).
72#[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    /// Replace the source in the catalog.
249    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    // TODO: maybe here to pass a materialize plan node
363    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        // TODO: this is a dummy implementation for debugging only.
393        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}