Skip to main content

risingwave_rpc_client/
meta_client.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::{BTreeMap, HashMap, HashSet};
16use std::fmt::{Debug, Display};
17use std::sync::Arc;
18use std::sync::atomic::AtomicBool;
19use std::sync::atomic::Ordering::Relaxed;
20use std::thread;
21use std::time::{Duration, SystemTime};
22
23use anyhow::{Context, anyhow};
24use async_trait::async_trait;
25use cluster_limit_service_client::ClusterLimitServiceClient;
26use either::Either;
27use futures::stream::BoxStream;
28use list_rate_limits_response::RateLimitInfo;
29use lru::LruCache;
30use replace_job_plan::ReplaceJob;
31use risingwave_common::catalog::{
32    AlterDatabaseParam, FunctionId, IndexId, ObjectId, SecretId, TableId,
33};
34use risingwave_common::config::{MAX_CONNECTION_WINDOW_SIZE, MetaConfig};
35use risingwave_common::hash::WorkerSlotMapping;
36use risingwave_common::id::{
37    ConnectionId, DatabaseId, JobId, SchemaId, SinkId, SubscriptionId, UserId, ViewId, WorkerId,
38};
39use risingwave_common::monitor::EndpointExt;
40use risingwave_common::system_param::AdaptiveParallelismStrategy;
41use risingwave_common::system_param::reader::SystemParamsReader;
42use risingwave_common::telemetry::report::TelemetryInfoFetcher;
43use risingwave_common::util::addr::HostAddr;
44use risingwave_common::util::meta_addr::MetaAddressStrategy;
45use risingwave_common::util::resource_util::cpu::total_cpu_available;
46use risingwave_common::util::resource_util::hostname;
47use risingwave_common::util::resource_util::memory::system_memory_available_bytes;
48use risingwave_common::util::retry::exponential_backoff;
49use risingwave_common::util::version::current_rw_version;
50use risingwave_error::bail;
51use risingwave_error::tonic::ErrorIsFromTonicServerImpl;
52use risingwave_hummock_sdk::change_log::{TableChangeLog, TableChangeLogs};
53use risingwave_hummock_sdk::version::{HummockVersion, HummockVersionDelta};
54use risingwave_hummock_sdk::{
55    CompactionGroupId, HummockEpoch, HummockVersionId, ObjectIdRange, SyncResult,
56};
57use risingwave_pb::backup_service::backup_service_client::BackupServiceClient;
58use risingwave_pb::backup_service::*;
59use risingwave_pb::catalog::{
60    Connection, PbComment, PbDatabase, PbFunction, PbIndex, PbSchema, PbSink, PbSource,
61    PbSubscription, PbTable, PbView, Table,
62};
63use risingwave_pb::cloud_service::cloud_service_client::CloudServiceClient;
64use risingwave_pb::cloud_service::*;
65use risingwave_pb::common::worker_node::Property;
66use risingwave_pb::common::{HostAddress, OptionalUint32, OptionalUint64, WorkerNode, WorkerType};
67use risingwave_pb::configured_monitor_service_client;
68use risingwave_pb::connector_service::sink_coordination_service_client::SinkCoordinationServiceClient;
69use risingwave_pb::ddl_service::alter_owner_request::Object;
70use risingwave_pb::ddl_service::create_iceberg_table_request::{PbSinkJobInfo, PbTableJobInfo};
71use risingwave_pb::ddl_service::ddl_service_client::DdlServiceClient;
72use risingwave_pb::ddl_service::*;
73use risingwave_pb::hummock::compact_task::TaskStatus;
74use risingwave_pb::hummock::get_compaction_score_response::PickerInfo;
75use risingwave_pb::hummock::get_table_change_logs_request::PbTableFilter;
76use risingwave_pb::hummock::hummock_manager_service_client::HummockManagerServiceClient;
77use risingwave_pb::hummock::rise_ctl_update_compaction_config_request::mutable_config::MutableConfig;
78use risingwave_pb::hummock::subscribe_compaction_event_request::Register;
79use risingwave_pb::hummock::write_limits::WriteLimit;
80use risingwave_pb::hummock::*;
81use risingwave_pb::iceberg_compaction::subscribe_iceberg_compaction_event_request::Register as IcebergRegister;
82use risingwave_pb::iceberg_compaction::{
83    SubscribeIcebergCompactionEventRequest, SubscribeIcebergCompactionEventResponse,
84    subscribe_iceberg_compaction_event_request,
85};
86use risingwave_pb::id::{ActorId, FragmentId, HummockSstableId, IcebergCompactionTaskId, SourceId};
87use risingwave_pb::meta::alter_connector_props_request::{
88    AlterConnectorPropsObject, AlterIcebergTableIds, ExtraOptions,
89};
90use risingwave_pb::meta::cancel_creating_jobs_request::PbJobs;
91use risingwave_pb::meta::cluster_service_client::ClusterServiceClient;
92use risingwave_pb::meta::event_log_service_client::EventLogServiceClient;
93use risingwave_pb::meta::heartbeat_service_client::HeartbeatServiceClient;
94use risingwave_pb::meta::hosted_iceberg_catalog_service_client::HostedIcebergCatalogServiceClient;
95use risingwave_pb::meta::list_actor_splits_response::ActorSplit;
96use risingwave_pb::meta::list_actor_states_response::ActorState;
97use risingwave_pb::meta::list_cdc_progress_response::PbCdcProgress;
98use risingwave_pb::meta::list_iceberg_tables_response::IcebergTable;
99use risingwave_pb::meta::list_refresh_table_states_response::RefreshTableState;
100use risingwave_pb::meta::list_streaming_job_states_response::StreamingJobState;
101use risingwave_pb::meta::list_table_fragments_response::TableFragmentInfo;
102use risingwave_pb::meta::meta_member_service_client::MetaMemberServiceClient;
103use risingwave_pb::meta::notification_service_client::NotificationServiceClient;
104use risingwave_pb::meta::scale_service_client::ScaleServiceClient;
105use risingwave_pb::meta::serving_service_client::ServingServiceClient;
106use risingwave_pb::meta::session_param_service_client::SessionParamServiceClient;
107use risingwave_pb::meta::stream_manager_service_client::StreamManagerServiceClient;
108use risingwave_pb::meta::system_params_service_client::SystemParamsServiceClient;
109use risingwave_pb::meta::telemetry_info_service_client::TelemetryInfoServiceClient;
110use risingwave_pb::meta::{FragmentDistribution, *};
111use risingwave_pb::monitor_service::monitor_service_client::MonitorServiceClient;
112use risingwave_pb::monitor_service::stack_trace_request::ActorTracesFormat;
113use risingwave_pb::monitor_service::{StackTraceRequest, StackTraceResponse};
114use risingwave_pb::secret::PbSecretRef;
115use risingwave_pb::stream_plan::StreamFragmentGraph;
116use risingwave_pb::user::alter_default_privilege_request::Operation as AlterDefaultPrivilegeOperation;
117use risingwave_pb::user::update_user_request::UpdateField;
118use risingwave_pb::user::user_service_client::UserServiceClient;
119use risingwave_pb::user::*;
120use thiserror_ext::AsReport;
121use tokio::sync::mpsc::{Receiver, UnboundedSender, unbounded_channel};
122use tokio::sync::oneshot::Sender;
123use tokio::sync::{RwLock, mpsc, oneshot};
124use tokio::task::JoinHandle;
125use tokio::time::{self};
126use tokio_retry::strategy::jitter;
127use tokio_stream::wrappers::UnboundedReceiverStream;
128use tonic::transport::Endpoint;
129use tonic::{Code, Request, Streaming};
130
131use crate::channel::{Channel, WrappedChannelExt};
132use crate::error::{Result, RpcError};
133use crate::hummock_meta_client::{
134    CompactionEventItem, HummockMetaClient, HummockMetaClientChangeLogInfo,
135    IcebergCompactionEventItem,
136};
137use crate::meta_rpc_client_method_impl;
138
139/// Client to meta server. Cloning the instance is lightweight.
140#[derive(Clone, Debug)]
141pub struct MetaClient {
142    worker_id: WorkerId,
143    worker_type: WorkerType,
144    host_addr: HostAddr,
145    inner: GrpcMetaClient,
146    meta_config: Arc<MetaConfig>,
147    cluster_id: String,
148    shutting_down: Arc<AtomicBool>,
149}
150
151impl MetaClient {
152    pub fn worker_id(&self) -> WorkerId {
153        self.worker_id
154    }
155
156    pub fn host_addr(&self) -> &HostAddr {
157        &self.host_addr
158    }
159
160    pub fn worker_type(&self) -> WorkerType {
161        self.worker_type
162    }
163
164    pub fn cluster_id(&self) -> &str {
165        &self.cluster_id
166    }
167
168    /// Subscribe to notification from meta.
169    pub async fn subscribe(
170        &self,
171        subscribe_type: SubscribeType,
172    ) -> Result<Streaming<SubscribeResponse>> {
173        let request = SubscribeRequest {
174            subscribe_type: subscribe_type as i32,
175            host: Some(self.host_addr.to_protobuf()),
176            worker_id: self.worker_id(),
177        };
178
179        let retry_strategy = GrpcMetaClient::retry_strategy_to_bound(
180            Duration::from_secs(self.meta_config.max_heartbeat_interval_secs as u64),
181            true,
182        );
183
184        tokio_retry::Retry::spawn(retry_strategy, || async {
185            let request = request.clone();
186            self.inner.subscribe(request).await
187        })
188        .await
189    }
190
191    pub async fn create_connection(
192        &self,
193        connection_name: String,
194        database_id: DatabaseId,
195        schema_id: SchemaId,
196        owner_id: UserId,
197        req: create_connection_request::Payload,
198    ) -> Result<WaitVersion> {
199        let request = CreateConnectionRequest {
200            name: connection_name,
201            database_id,
202            schema_id,
203            owner_id,
204            payload: Some(req),
205        };
206        let resp = self.inner.create_connection(request).await?;
207        Ok(resp
208            .version
209            .ok_or_else(|| anyhow!("wait version not set"))?)
210    }
211
212    pub async fn create_secret(
213        &self,
214        secret_name: String,
215        database_id: DatabaseId,
216        schema_id: SchemaId,
217        owner_id: UserId,
218        value: Vec<u8>,
219    ) -> Result<WaitVersion> {
220        let request = CreateSecretRequest {
221            name: secret_name,
222            database_id,
223            schema_id,
224            owner_id,
225            value,
226        };
227        let resp = self.inner.create_secret(request).await?;
228        Ok(resp
229            .version
230            .ok_or_else(|| anyhow!("wait version not set"))?)
231    }
232
233    pub async fn list_connections(&self, _name: Option<&str>) -> Result<Vec<Connection>> {
234        let request = ListConnectionsRequest {};
235        let resp = self.inner.list_connections(request).await?;
236        Ok(resp.connections)
237    }
238
239    pub async fn drop_connection(
240        &self,
241        connection_id: ConnectionId,
242        cascade: bool,
243    ) -> Result<WaitVersion> {
244        let request = DropConnectionRequest {
245            connection_id,
246            cascade,
247        };
248        let resp = self.inner.drop_connection(request).await?;
249        Ok(resp
250            .version
251            .ok_or_else(|| anyhow!("wait version not set"))?)
252    }
253
254    pub async fn drop_secret(&self, secret_id: SecretId, cascade: bool) -> Result<WaitVersion> {
255        let request = DropSecretRequest { secret_id, cascade };
256        let resp = self.inner.drop_secret(request).await?;
257        Ok(resp
258            .version
259            .ok_or_else(|| anyhow!("wait version not set"))?)
260    }
261
262    /// Register the current node to the cluster and set the corresponding worker id.
263    ///
264    /// Retry if there's connection issue with the meta node. Exit the process if the registration fails.
265    pub async fn register_new(
266        addr_strategy: MetaAddressStrategy,
267        worker_type: WorkerType,
268        addr: &HostAddr,
269        property: Property,
270        meta_config: Arc<MetaConfig>,
271    ) -> (Self, SystemParamsReader) {
272        let ret =
273            Self::register_new_inner(addr_strategy, worker_type, addr, property, meta_config).await;
274
275        match ret {
276            Ok(ret) => ret,
277            Err(err) => {
278                tracing::error!(error = %err.as_report(), "failed to register worker, exiting...");
279                std::process::exit(1);
280            }
281        }
282    }
283
284    async fn register_new_inner(
285        addr_strategy: MetaAddressStrategy,
286        worker_type: WorkerType,
287        addr: &HostAddr,
288        property: Property,
289        meta_config: Arc<MetaConfig>,
290    ) -> Result<(Self, SystemParamsReader)> {
291        tracing::info!("register meta client using strategy: {}", addr_strategy);
292
293        // Retry until reaching `max_heartbeat_interval_secs`
294        let retry_strategy = GrpcMetaClient::retry_strategy_to_bound(
295            Duration::from_secs(meta_config.max_heartbeat_interval_secs as u64),
296            true,
297        );
298
299        let init_result: Result<_> = tokio_retry::RetryIf::spawn(
300            retry_strategy,
301            || async {
302                let grpc_meta_client =
303                    GrpcMetaClient::new(&addr_strategy, meta_config.clone()).await?;
304
305                let add_worker_resp = grpc_meta_client
306                    .add_worker_node(AddWorkerNodeRequest {
307                        worker_type: worker_type as i32,
308                        host: Some(addr.to_protobuf()),
309                        property: Some(property.clone()),
310                        resource: Some(risingwave_pb::common::worker_node::Resource {
311                            rw_version: current_rw_version(),
312                            total_memory_bytes: system_memory_available_bytes() as _,
313                            total_cpu_cores: total_cpu_available() as _,
314                            hostname: hostname(),
315                        }),
316                    })
317                    .await
318                    .context("failed to add worker node")?;
319
320                let system_params_resp = grpc_meta_client
321                    .get_system_params(GetSystemParamsRequest {})
322                    .await
323                    .context("failed to get initial system params")?;
324
325                Ok((add_worker_resp, system_params_resp, grpc_meta_client))
326            },
327            // Only retry if there's any transient connection issue.
328            // If the error is from our implementation or business, do not retry it.
329            |e: &RpcError| !e.is_from_tonic_server_impl(),
330        )
331        .await;
332
333        let (add_worker_resp, system_params_resp, grpc_meta_client) = init_result?;
334        let worker_id = add_worker_resp
335            .node_id
336            .expect("AddWorkerNodeResponse::node_id is empty");
337
338        let meta_client = Self {
339            worker_id,
340            worker_type,
341            host_addr: addr.clone(),
342            inner: grpc_meta_client,
343            meta_config: meta_config.clone(),
344            cluster_id: add_worker_resp.cluster_id,
345            shutting_down: Arc::new(false.into()),
346        };
347
348        static REPORT_PANIC: std::sync::Once = std::sync::Once::new();
349        REPORT_PANIC.call_once(|| {
350            let meta_client_clone = meta_client.clone();
351            std::panic::update_hook(move |default_hook, info| {
352                // Try to report panic event to meta node.
353                meta_client_clone.try_add_panic_event_blocking(info, None);
354                default_hook(info);
355            });
356        });
357
358        Ok((meta_client, system_params_resp.params.unwrap().into()))
359    }
360
361    /// Activate the current node in cluster to confirm it's ready to serve.
362    pub async fn activate(&self, addr: &HostAddr) -> Result<()> {
363        let request = ActivateWorkerNodeRequest {
364            host: Some(addr.to_protobuf()),
365            node_id: self.worker_id,
366        };
367        let retry_strategy = GrpcMetaClient::retry_strategy_to_bound(
368            Duration::from_secs(self.meta_config.max_heartbeat_interval_secs as u64),
369            true,
370        );
371        tokio_retry::Retry::spawn(retry_strategy, || async {
372            let request = request.clone();
373            self.inner.activate_worker_node(request).await
374        })
375        .await?;
376
377        Ok(())
378    }
379
380    /// Send heartbeat signal to meta service.
381    pub async fn send_heartbeat(&self) -> Result<()> {
382        let request = HeartbeatRequest {
383            node_id: self.worker_id,
384        };
385        let resp = self.inner.heartbeat(request).await?;
386        if let Some(status) = resp.status
387            && status.code() == risingwave_pb::common::status::Code::UnknownWorker
388        {
389            // Ignore the error if we're already shutting down.
390            // Otherwise, exit the process.
391            if !self.shutting_down.load(Relaxed) {
392                tracing::error!(message = status.message, "worker expired");
393                std::process::exit(1);
394            }
395        }
396        Ok(())
397    }
398
399    pub async fn create_database(&self, db: PbDatabase) -> Result<WaitVersion> {
400        let request = CreateDatabaseRequest { db: Some(db) };
401        let resp = self.inner.create_database(request).await?;
402        // TODO: handle error in `resp.status` here
403        Ok(resp
404            .version
405            .ok_or_else(|| anyhow!("wait version not set"))?)
406    }
407
408    pub async fn create_schema(&self, schema: PbSchema) -> Result<WaitVersion> {
409        let request = CreateSchemaRequest {
410            schema: Some(schema),
411        };
412        let resp = self.inner.create_schema(request).await?;
413        // TODO: handle error in `resp.status` here
414        Ok(resp
415            .version
416            .ok_or_else(|| anyhow!("wait version not set"))?)
417    }
418
419    pub async fn create_materialized_view(
420        &self,
421        table: PbTable,
422        graph: StreamFragmentGraph,
423        dependencies: HashSet<ObjectId>,
424        resource_type: streaming_job_resource_type::ResourceType,
425        if_not_exists: bool,
426        refresh_interval_sec: Option<u64>,
427    ) -> Result<WaitVersion> {
428        let request = CreateMaterializedViewRequest {
429            materialized_view: Some(table),
430            fragment_graph: Some(graph),
431            resource_type: Some(PbStreamingJobResourceType {
432                resource_type: Some(resource_type),
433            }),
434            dependencies: dependencies.into_iter().collect(),
435            if_not_exists,
436            refresh_interval_sec,
437        };
438        let resp = self.inner.create_materialized_view(request).await?;
439        // TODO: handle error in `resp.status` here
440        Ok(resp
441            .version
442            .ok_or_else(|| anyhow!("wait version not set"))?)
443    }
444
445    pub async fn drop_materialized_view(
446        &self,
447        table_id: TableId,
448        cascade: bool,
449    ) -> Result<WaitVersion> {
450        let request = DropMaterializedViewRequest { table_id, cascade };
451
452        let resp = self.inner.drop_materialized_view(request).await?;
453        Ok(resp
454            .version
455            .ok_or_else(|| anyhow!("wait version not set"))?)
456    }
457
458    pub async fn create_source(
459        &self,
460        source: PbSource,
461        graph: Option<StreamFragmentGraph>,
462        if_not_exists: bool,
463    ) -> Result<WaitVersion> {
464        let request = CreateSourceRequest {
465            source: Some(source),
466            fragment_graph: graph,
467            if_not_exists,
468        };
469
470        let resp = self.inner.create_source(request).await?;
471        Ok(resp
472            .version
473            .ok_or_else(|| anyhow!("wait version not set"))?)
474    }
475
476    pub async fn create_sink(
477        &self,
478        sink: PbSink,
479        graph: StreamFragmentGraph,
480        dependencies: HashSet<ObjectId>,
481        resource_type: streaming_job_resource_type::ResourceType,
482        if_not_exists: bool,
483        since_timestamp_epoch: Option<u64>,
484    ) -> Result<WaitVersion> {
485        let request = CreateSinkRequest {
486            sink: Some(sink),
487            fragment_graph: Some(graph),
488            dependencies: dependencies.into_iter().collect(),
489            if_not_exists,
490            resource_type: Some(PbStreamingJobResourceType {
491                resource_type: Some(resource_type),
492            }),
493            since_timestamp_epoch,
494        };
495
496        let resp = self.inner.create_sink(request).await?;
497        Ok(resp
498            .version
499            .ok_or_else(|| anyhow!("wait version not set"))?)
500    }
501
502    pub async fn create_subscription(&self, subscription: PbSubscription) -> Result<WaitVersion> {
503        let request = CreateSubscriptionRequest {
504            subscription: Some(subscription),
505        };
506
507        let resp = self.inner.create_subscription(request).await?;
508        Ok(resp
509            .version
510            .ok_or_else(|| anyhow!("wait version not set"))?)
511    }
512
513    pub async fn create_function(&self, function: PbFunction) -> Result<WaitVersion> {
514        let request = CreateFunctionRequest {
515            function: Some(function),
516        };
517        let resp = self.inner.create_function(request).await?;
518        Ok(resp
519            .version
520            .ok_or_else(|| anyhow!("wait version not set"))?)
521    }
522
523    pub async fn create_table(
524        &self,
525        source: Option<PbSource>,
526        table: PbTable,
527        graph: StreamFragmentGraph,
528        job_type: PbTableJobType,
529        if_not_exists: bool,
530        dependencies: HashSet<ObjectId>,
531    ) -> Result<WaitVersion> {
532        let request = CreateTableRequest {
533            materialized_view: Some(table),
534            fragment_graph: Some(graph),
535            source,
536            job_type: job_type as _,
537            if_not_exists,
538            dependencies: dependencies.into_iter().collect(),
539        };
540        let resp = self.inner.create_table(request).await?;
541        // TODO: handle error in `resp.status` here
542        Ok(resp
543            .version
544            .ok_or_else(|| anyhow!("wait version not set"))?)
545    }
546
547    pub async fn comment_on(&self, comment: PbComment) -> Result<WaitVersion> {
548        let request = CommentOnRequest {
549            comment: Some(comment),
550        };
551        let resp = self.inner.comment_on(request).await?;
552        Ok(resp
553            .version
554            .ok_or_else(|| anyhow!("wait version not set"))?)
555    }
556
557    pub async fn alter_name(
558        &self,
559        object: alter_name_request::Object,
560        name: &str,
561    ) -> Result<WaitVersion> {
562        let request = AlterNameRequest {
563            object: Some(object),
564            new_name: name.to_owned(),
565        };
566        let resp = self.inner.alter_name(request).await?;
567        Ok(resp
568            .version
569            .ok_or_else(|| anyhow!("wait version not set"))?)
570    }
571
572    pub async fn alter_owner(&self, object: Object, owner_id: UserId) -> Result<WaitVersion> {
573        let request = AlterOwnerRequest {
574            object: Some(object),
575            owner_id,
576        };
577        let resp = self.inner.alter_owner(request).await?;
578        Ok(resp
579            .version
580            .ok_or_else(|| anyhow!("wait version not set"))?)
581    }
582
583    pub async fn alter_subscription_retention(
584        &self,
585        subscription_id: SubscriptionId,
586        retention_seconds: u64,
587        definition: String,
588    ) -> Result<WaitVersion> {
589        let request = AlterSubscriptionRetentionRequest {
590            subscription_id,
591            retention_seconds,
592            definition,
593        };
594        let resp = self.inner.alter_subscription_retention(request).await?;
595        Ok(resp
596            .version
597            .ok_or_else(|| anyhow!("wait version not set"))?)
598    }
599
600    pub async fn alter_database_param(
601        &self,
602        database_id: DatabaseId,
603        param: AlterDatabaseParam,
604    ) -> Result<WaitVersion> {
605        let request = match param {
606            AlterDatabaseParam::BarrierIntervalMs(barrier_interval_ms) => {
607                let barrier_interval_ms = OptionalUint32 {
608                    value: barrier_interval_ms,
609                };
610                AlterDatabaseParamRequest {
611                    database_id,
612                    param: Some(alter_database_param_request::Param::BarrierIntervalMs(
613                        barrier_interval_ms,
614                    )),
615                }
616            }
617            AlterDatabaseParam::CheckpointFrequency(checkpoint_frequency) => {
618                let checkpoint_frequency = OptionalUint64 {
619                    value: checkpoint_frequency,
620                };
621                AlterDatabaseParamRequest {
622                    database_id,
623                    param: Some(alter_database_param_request::Param::CheckpointFrequency(
624                        checkpoint_frequency,
625                    )),
626                }
627            }
628        };
629        let resp = self.inner.alter_database_param(request).await?;
630        Ok(resp
631            .version
632            .ok_or_else(|| anyhow!("wait version not set"))?)
633    }
634
635    pub async fn alter_set_schema(
636        &self,
637        object: alter_set_schema_request::Object,
638        new_schema_id: SchemaId,
639    ) -> Result<WaitVersion> {
640        let request = AlterSetSchemaRequest {
641            new_schema_id,
642            object: Some(object),
643        };
644        let resp = self.inner.alter_set_schema(request).await?;
645        Ok(resp
646            .version
647            .ok_or_else(|| anyhow!("wait version not set"))?)
648    }
649
650    pub async fn alter_source(&self, source: PbSource) -> Result<WaitVersion> {
651        let request = AlterSourceRequest {
652            source: Some(source),
653        };
654        let resp = self.inner.alter_source(request).await?;
655        Ok(resp
656            .version
657            .ok_or_else(|| anyhow!("wait version not set"))?)
658    }
659
660    pub async fn alter_parallelism(
661        &self,
662        job_id: JobId,
663        parallelism: PbTableParallelism,
664        adaptive_parallelism_strategy: Option<AdaptiveParallelismStrategy>,
665        deferred: bool,
666    ) -> Result<()> {
667        let request = AlterParallelismRequest {
668            table_id: job_id,
669            parallelism: Some(parallelism),
670            deferred,
671            adaptive_parallelism_strategy: adaptive_parallelism_strategy
672                .map(|strategy| strategy.to_string()),
673        };
674
675        self.inner.alter_parallelism(request).await?;
676        Ok(())
677    }
678
679    pub async fn alter_backfill_parallelism(
680        &self,
681        job_id: JobId,
682        parallelism: Option<PbTableParallelism>,
683        adaptive_parallelism_strategy: Option<AdaptiveParallelismStrategy>,
684        deferred: bool,
685    ) -> Result<()> {
686        let request = AlterBackfillParallelismRequest {
687            table_id: job_id,
688            parallelism,
689            deferred,
690            adaptive_parallelism_strategy: adaptive_parallelism_strategy
691                .map(|strategy| strategy.to_string()),
692        };
693
694        self.inner.alter_backfill_parallelism(request).await?;
695        Ok(())
696    }
697
698    pub async fn alter_streaming_job_config(
699        &self,
700        job_id: JobId,
701        entries_to_add: HashMap<String, String>,
702        keys_to_remove: Vec<String>,
703    ) -> Result<()> {
704        let request = AlterStreamingJobConfigRequest {
705            job_id,
706            entries_to_add,
707            keys_to_remove,
708        };
709
710        self.inner.alter_streaming_job_config(request).await?;
711        Ok(())
712    }
713
714    pub async fn alter_fragment_parallelism(
715        &self,
716        fragment_ids: Vec<FragmentId>,
717        parallelism: Option<PbTableParallelism>,
718    ) -> Result<()> {
719        let request = AlterFragmentParallelismRequest {
720            fragment_ids,
721            parallelism,
722        };
723
724        self.inner.alter_fragment_parallelism(request).await?;
725        Ok(())
726    }
727
728    pub async fn alter_cdc_table_backfill_parallelism(
729        &self,
730        table_id: JobId,
731        parallelism: PbTableParallelism,
732    ) -> Result<()> {
733        let request = AlterCdcTableBackfillParallelismRequest {
734            table_id,
735            parallelism: Some(parallelism),
736        };
737        self.inner
738            .alter_cdc_table_backfill_parallelism(request)
739            .await?;
740        Ok(())
741    }
742
743    pub async fn alter_resource_group(
744        &self,
745        job_id: JobId,
746        resource_group: Option<String>,
747        deferred: bool,
748    ) -> Result<()> {
749        let request = AlterResourceGroupRequest {
750            job_id,
751            resource_group,
752            deferred,
753        };
754
755        self.inner.alter_resource_group(request).await?;
756        Ok(())
757    }
758
759    pub async fn alter_database_resource_group(
760        &self,
761        database_id: DatabaseId,
762        resource_group: Option<String>,
763        deferred: bool,
764    ) -> Result<WaitVersion> {
765        let request = AlterDatabaseResourceGroupRequest {
766            database_id,
767            resource_group,
768            deferred,
769        };
770
771        let resp = self.inner.alter_database_resource_group(request).await?;
772        Ok(resp
773            .version
774            .ok_or_else(|| anyhow!("wait version not set"))?)
775    }
776
777    pub async fn alter_swap_rename(
778        &self,
779        object: alter_swap_rename_request::Object,
780    ) -> Result<WaitVersion> {
781        let request = AlterSwapRenameRequest {
782            object: Some(object),
783        };
784        let resp = self.inner.alter_swap_rename(request).await?;
785        Ok(resp
786            .version
787            .ok_or_else(|| anyhow!("wait version not set"))?)
788    }
789
790    pub async fn alter_secret(
791        &self,
792        secret_id: SecretId,
793        secret_name: String,
794        database_id: DatabaseId,
795        schema_id: SchemaId,
796        owner_id: UserId,
797        value: Vec<u8>,
798    ) -> Result<WaitVersion> {
799        let request = AlterSecretRequest {
800            secret_id,
801            name: secret_name,
802            database_id,
803            schema_id,
804            owner_id,
805            value,
806        };
807        let resp = self.inner.alter_secret(request).await?;
808        Ok(resp
809            .version
810            .ok_or_else(|| anyhow!("wait version not set"))?)
811    }
812
813    pub async fn replace_job(
814        &self,
815        graph: StreamFragmentGraph,
816        replace_job: ReplaceJob,
817    ) -> Result<WaitVersion> {
818        let request = ReplaceJobPlanRequest {
819            plan: Some(ReplaceJobPlan {
820                fragment_graph: Some(graph),
821                replace_job: Some(replace_job),
822            }),
823        };
824        let resp = self.inner.replace_job_plan(request).await?;
825        // TODO: handle error in `resp.status` here
826        Ok(resp
827            .version
828            .ok_or_else(|| anyhow!("wait version not set"))?)
829    }
830
831    pub async fn auto_schema_change(&self, schema_change: SchemaChangeEnvelope) -> Result<()> {
832        let request = AutoSchemaChangeRequest {
833            schema_change: Some(schema_change),
834        };
835        let _ = self.inner.auto_schema_change(request).await?;
836        Ok(())
837    }
838
839    /// Block until the pk-index sink `sink_id`'s database has committed through `epoch`, returning
840    /// the coordinator's committed iceberg snapshot id (`None` if no snapshot committed yet).
841    pub async fn wait_iceberg_pk_index_sink_epoch(
842        &self,
843        sink_id: SinkId,
844        epoch: u64,
845    ) -> Result<Option<i64>> {
846        let request = WaitIcebergPkIndexSinkEpochRequest { sink_id, epoch };
847        let resp = self.inner.wait_iceberg_pk_index_sink_epoch(request).await?;
848        Ok(resp.snapshot_id)
849    }
850
851    pub async fn create_view(
852        &self,
853        view: PbView,
854        dependencies: HashSet<ObjectId>,
855    ) -> Result<WaitVersion> {
856        let request = CreateViewRequest {
857            view: Some(view),
858            dependencies: dependencies.into_iter().collect(),
859        };
860        let resp = self.inner.create_view(request).await?;
861        // TODO: handle error in `resp.status` here
862        Ok(resp
863            .version
864            .ok_or_else(|| anyhow!("wait version not set"))?)
865    }
866
867    pub async fn create_index(
868        &self,
869        index: PbIndex,
870        table: PbTable,
871        graph: StreamFragmentGraph,
872        resource_type: streaming_job_resource_type::ResourceType,
873        if_not_exists: bool,
874    ) -> Result<WaitVersion> {
875        let request = CreateIndexRequest {
876            index: Some(index),
877            index_table: Some(table),
878            fragment_graph: Some(graph),
879            if_not_exists,
880            resource_type: Some(PbStreamingJobResourceType {
881                resource_type: Some(resource_type),
882            }),
883        };
884        let resp = self.inner.create_index(request).await?;
885        // TODO: handle error in `resp.status` here
886        Ok(resp
887            .version
888            .ok_or_else(|| anyhow!("wait version not set"))?)
889    }
890
891    pub async fn drop_table(
892        &self,
893        source_id: Option<SourceId>,
894        table_id: TableId,
895        cascade: bool,
896    ) -> Result<WaitVersion> {
897        let request = DropTableRequest {
898            source_id: source_id.map(risingwave_pb::ddl_service::drop_table_request::SourceId::Id),
899            table_id,
900            cascade,
901        };
902
903        let resp = self.inner.drop_table(request).await?;
904        Ok(resp
905            .version
906            .ok_or_else(|| anyhow!("wait version not set"))?)
907    }
908
909    pub async fn compact_iceberg_table(&self, sink_id: SinkId) -> Result<IcebergCompactionTaskId> {
910        let request = CompactIcebergTableRequest { sink_id };
911        let resp = self.inner.compact_iceberg_table(request).await?;
912        Ok(resp.task_id)
913    }
914
915    pub async fn rewrite_iceberg_table_manifests(&self, sink_id: SinkId) -> Result<()> {
916        let request = RewriteIcebergTableManifestsRequest { sink_id };
917        let _resp = self.inner.rewrite_iceberg_table_manifests(request).await?;
918        Ok(())
919    }
920
921    pub async fn expire_iceberg_table_snapshots(&self, sink_id: SinkId) -> Result<()> {
922        let request = ExpireIcebergTableSnapshotsRequest { sink_id };
923        let _resp = self.inner.expire_iceberg_table_snapshots(request).await?;
924        Ok(())
925    }
926
927    pub async fn remove_iceberg_table_orphan_files(
928        &self,
929        sink_id: SinkId,
930    ) -> Result<RemoveIcebergTableOrphanFilesResponse> {
931        let request = RemoveIcebergTableOrphanFilesRequest { sink_id };
932        self.inner.remove_iceberg_table_orphan_files(request).await
933    }
934
935    pub async fn drop_view(&self, view_id: ViewId, cascade: bool) -> Result<WaitVersion> {
936        let request = DropViewRequest { view_id, cascade };
937        let resp = self.inner.drop_view(request).await?;
938        Ok(resp
939            .version
940            .ok_or_else(|| anyhow!("wait version not set"))?)
941    }
942
943    pub async fn drop_source(&self, source_id: SourceId, cascade: bool) -> Result<WaitVersion> {
944        let request = DropSourceRequest { source_id, cascade };
945        let resp = self.inner.drop_source(request).await?;
946        Ok(resp
947            .version
948            .ok_or_else(|| anyhow!("wait version not set"))?)
949    }
950
951    pub async fn reset_source(&self, source_id: SourceId) -> Result<WaitVersion> {
952        let request = ResetSourceRequest { source_id };
953        let resp = self.inner.reset_source(request).await?;
954        Ok(resp
955            .version
956            .ok_or_else(|| anyhow!("wait version not set"))?)
957    }
958
959    pub async fn drop_sink(&self, sink_id: SinkId, cascade: bool) -> Result<WaitVersion> {
960        let request = DropSinkRequest { sink_id, cascade };
961        let resp = self.inner.drop_sink(request).await?;
962        Ok(resp
963            .version
964            .ok_or_else(|| anyhow!("wait version not set"))?)
965    }
966
967    pub async fn drop_subscription(
968        &self,
969        subscription_id: SubscriptionId,
970        cascade: bool,
971    ) -> Result<WaitVersion> {
972        let request = DropSubscriptionRequest {
973            subscription_id,
974            cascade,
975        };
976        let resp = self.inner.drop_subscription(request).await?;
977        Ok(resp
978            .version
979            .ok_or_else(|| anyhow!("wait version not set"))?)
980    }
981
982    pub async fn drop_index(&self, index_id: IndexId, cascade: bool) -> Result<WaitVersion> {
983        let request = DropIndexRequest { index_id, cascade };
984        let resp = self.inner.drop_index(request).await?;
985        Ok(resp
986            .version
987            .ok_or_else(|| anyhow!("wait version not set"))?)
988    }
989
990    pub async fn drop_function(
991        &self,
992        function_id: FunctionId,
993        cascade: bool,
994    ) -> Result<WaitVersion> {
995        let request = DropFunctionRequest {
996            function_id,
997            cascade,
998        };
999        let resp = self.inner.drop_function(request).await?;
1000        Ok(resp
1001            .version
1002            .ok_or_else(|| anyhow!("wait version not set"))?)
1003    }
1004
1005    pub async fn drop_database(&self, database_id: DatabaseId) -> Result<WaitVersion> {
1006        let request = DropDatabaseRequest { database_id };
1007        let resp = self.inner.drop_database(request).await?;
1008        Ok(resp
1009            .version
1010            .ok_or_else(|| anyhow!("wait version not set"))?)
1011    }
1012
1013    pub async fn drop_schema(&self, schema_id: SchemaId, cascade: bool) -> Result<WaitVersion> {
1014        let request = DropSchemaRequest { schema_id, cascade };
1015        let resp = self.inner.drop_schema(request).await?;
1016        Ok(resp
1017            .version
1018            .ok_or_else(|| anyhow!("wait version not set"))?)
1019    }
1020
1021    // TODO: using UserInfoVersion instead as return type.
1022    pub async fn create_user(&self, user: UserInfo) -> Result<u64> {
1023        let request = CreateUserRequest { user: Some(user) };
1024        let resp = self.inner.create_user(request).await?;
1025        Ok(resp.version)
1026    }
1027
1028    pub async fn drop_user(&self, user_id: UserId) -> Result<u64> {
1029        let request = DropUserRequest { user_id };
1030        let resp = self.inner.drop_user(request).await?;
1031        Ok(resp.version)
1032    }
1033
1034    pub async fn update_user(
1035        &self,
1036        user: UserInfo,
1037        update_fields: Vec<UpdateField>,
1038    ) -> Result<u64> {
1039        let request = UpdateUserRequest {
1040            user: Some(user),
1041            update_fields: update_fields
1042                .into_iter()
1043                .map(|field| field as i32)
1044                .collect::<Vec<_>>(),
1045        };
1046        let resp = self.inner.update_user(request).await?;
1047        Ok(resp.version)
1048    }
1049
1050    pub async fn grant_privilege(
1051        &self,
1052        user_ids: Vec<UserId>,
1053        privileges: Vec<GrantPrivilege>,
1054        with_grant_option: bool,
1055        granted_by: UserId,
1056    ) -> Result<u64> {
1057        let request = GrantPrivilegeRequest {
1058            user_ids,
1059            privileges,
1060            with_grant_option,
1061            granted_by,
1062        };
1063        let resp = self.inner.grant_privilege(request).await?;
1064        Ok(resp.version)
1065    }
1066
1067    pub async fn revoke_privilege(
1068        &self,
1069        user_ids: Vec<UserId>,
1070        privileges: Vec<GrantPrivilege>,
1071        granted_by: UserId,
1072        revoke_by: UserId,
1073        revoke_grant_option: bool,
1074        cascade: bool,
1075    ) -> Result<u64> {
1076        let request = RevokePrivilegeRequest {
1077            user_ids,
1078            privileges,
1079            granted_by,
1080            revoke_by,
1081            revoke_grant_option,
1082            cascade,
1083        };
1084        let resp = self.inner.revoke_privilege(request).await?;
1085        Ok(resp.version)
1086    }
1087
1088    pub async fn alter_default_privilege(
1089        &self,
1090        user_ids: Vec<UserId>,
1091        database_id: DatabaseId,
1092        schema_ids: Vec<SchemaId>,
1093        operation: AlterDefaultPrivilegeOperation,
1094        granted_by: UserId,
1095    ) -> Result<()> {
1096        let request = AlterDefaultPrivilegeRequest {
1097            user_ids,
1098            database_id,
1099            schema_ids,
1100            operation: Some(operation),
1101            granted_by,
1102        };
1103        self.inner.alter_default_privilege(request).await?;
1104        Ok(())
1105    }
1106
1107    /// Unregister the current node from the cluster.
1108    pub async fn unregister(&self) -> Result<()> {
1109        let request = DeleteWorkerNodeRequest {
1110            host: Some(self.host_addr.to_protobuf()),
1111        };
1112        self.inner.delete_worker_node(request).await?;
1113        self.shutting_down.store(true, Relaxed);
1114        Ok(())
1115    }
1116
1117    /// Try to unregister the current worker from the cluster with best effort. Log the result.
1118    pub async fn try_unregister(&self) {
1119        match self.unregister().await {
1120            Ok(_) => {
1121                tracing::info!(
1122                    worker_id = %self.worker_id(),
1123                    "successfully unregistered from meta service",
1124                )
1125            }
1126            Err(e) => {
1127                tracing::warn!(
1128                    error = %e.as_report(),
1129                    worker_id = %self.worker_id(),
1130                    "failed to unregister from meta service",
1131                );
1132            }
1133        }
1134    }
1135
1136    pub async fn list_worker_nodes(
1137        &self,
1138        worker_type: Option<WorkerType>,
1139    ) -> Result<Vec<WorkerNode>> {
1140        let request = ListAllNodesRequest {
1141            worker_type: worker_type.map(Into::into),
1142            include_starting_nodes: true,
1143        };
1144        let resp = self.inner.list_all_nodes(request).await?;
1145        Ok(resp.nodes)
1146    }
1147
1148    /// Starts a heartbeat worker.
1149    pub fn start_heartbeat_loop(
1150        meta_client: MetaClient,
1151        min_interval: Duration,
1152    ) -> (JoinHandle<()>, Sender<()>) {
1153        let (shutdown_tx, mut shutdown_rx) = tokio::sync::oneshot::channel();
1154        let join_handle = tokio::spawn(async move {
1155            let mut min_interval_ticker = tokio::time::interval(min_interval);
1156            loop {
1157                tokio::select! {
1158                    biased;
1159                    // Shutdown
1160                    _ = &mut shutdown_rx => {
1161                        tracing::info!("Heartbeat loop is stopped");
1162                        return;
1163                    }
1164                    // Wait for interval
1165                    _ = min_interval_ticker.tick() => {},
1166                }
1167                tracing::debug!(target: "events::meta::client_heartbeat", "heartbeat");
1168                match tokio::time::timeout(
1169                    // TODO: decide better min_interval for timeout
1170                    min_interval * 3,
1171                    meta_client.send_heartbeat(),
1172                )
1173                .await
1174                {
1175                    Ok(Ok(_)) => {}
1176                    Ok(Err(err)) => {
1177                        tracing::warn!(error = %err.as_report(), "Failed to send_heartbeat");
1178                    }
1179                    Err(_) => {
1180                        tracing::warn!("Failed to send_heartbeat: timeout");
1181                    }
1182                }
1183            }
1184        });
1185        (join_handle, shutdown_tx)
1186    }
1187
1188    pub async fn risectl_list_state_tables(&self) -> Result<Vec<PbTable>> {
1189        let request = RisectlListStateTablesRequest {};
1190        let resp = self.inner.risectl_list_state_tables(request).await?;
1191        Ok(resp.tables)
1192    }
1193
1194    pub async fn risectl_resume_backfill(
1195        &self,
1196        request: RisectlResumeBackfillRequest,
1197    ) -> Result<()> {
1198        self.inner.risectl_resume_backfill(request).await?;
1199        Ok(())
1200    }
1201
1202    pub async fn flush(&self, database_id: DatabaseId) -> Result<HummockVersionId> {
1203        let request = FlushRequest { database_id };
1204        let resp = self.inner.flush(request).await?;
1205        Ok(resp.hummock_version_id)
1206    }
1207
1208    pub async fn wait(&self, job_id: Option<JobId>) -> Result<WaitVersion> {
1209        let request = WaitRequest { job_id };
1210        let resp = self.inner.wait(request).await?;
1211        Ok(resp
1212            .version
1213            .ok_or_else(|| anyhow!("wait version not set"))?)
1214    }
1215
1216    pub async fn recover(&self) -> Result<()> {
1217        let request = RecoverRequest {};
1218        self.inner.recover(request).await?;
1219        Ok(())
1220    }
1221
1222    pub async fn cancel_creating_jobs(&self, jobs: PbJobs) -> Result<Vec<u32>> {
1223        let request = CancelCreatingJobsRequest { jobs: Some(jobs) };
1224        let resp = self.inner.cancel_creating_jobs(request).await?;
1225        Ok(resp.canceled_jobs)
1226    }
1227
1228    pub async fn list_table_fragments(
1229        &self,
1230        job_ids: &[JobId],
1231    ) -> Result<HashMap<JobId, TableFragmentInfo>> {
1232        let request = ListTableFragmentsRequest {
1233            table_ids: job_ids.to_vec(),
1234        };
1235        let resp = self.inner.list_table_fragments(request).await?;
1236        Ok(resp.table_fragments)
1237    }
1238
1239    pub async fn list_streaming_job_states(&self) -> Result<Vec<StreamingJobState>> {
1240        let resp = self
1241            .inner
1242            .list_streaming_job_states(ListStreamingJobStatesRequest {})
1243            .await?;
1244        Ok(resp.states)
1245    }
1246
1247    pub async fn list_fragment_distributions(
1248        &self,
1249        include_node: bool,
1250    ) -> Result<Vec<FragmentDistribution>> {
1251        let resp = self
1252            .inner
1253            .list_fragment_distribution(ListFragmentDistributionRequest {
1254                include_node: Some(include_node),
1255            })
1256            .await?;
1257        Ok(resp.distributions)
1258    }
1259
1260    pub async fn list_creating_fragment_distribution(&self) -> Result<Vec<FragmentDistribution>> {
1261        let resp = self
1262            .inner
1263            .list_creating_fragment_distribution(ListCreatingFragmentDistributionRequest {
1264                include_node: Some(true),
1265            })
1266            .await?;
1267        Ok(resp.distributions)
1268    }
1269
1270    pub async fn get_fragment_by_id(
1271        &self,
1272        fragment_id: FragmentId,
1273    ) -> Result<Option<FragmentDistribution>> {
1274        let resp = self
1275            .inner
1276            .get_fragment_by_id(GetFragmentByIdRequest { fragment_id })
1277            .await?;
1278        Ok(resp.distribution)
1279    }
1280
1281    pub async fn get_fragment_vnodes(
1282        &self,
1283        fragment_id: FragmentId,
1284    ) -> Result<Vec<(ActorId, Vec<u32>)>> {
1285        let resp = self
1286            .inner
1287            .get_fragment_vnodes(GetFragmentVnodesRequest { fragment_id })
1288            .await?;
1289        Ok(resp
1290            .actor_vnodes
1291            .into_iter()
1292            .map(|actor| (actor.actor_id, actor.vnode_indices))
1293            .collect())
1294    }
1295
1296    pub async fn get_actor_vnodes(&self, actor_id: ActorId) -> Result<Vec<u32>> {
1297        let resp = self
1298            .inner
1299            .get_actor_vnodes(GetActorVnodesRequest { actor_id })
1300            .await?;
1301        Ok(resp.vnode_indices)
1302    }
1303
1304    pub async fn list_actor_states(&self) -> Result<Vec<ActorState>> {
1305        let resp = self
1306            .inner
1307            .list_actor_states(ListActorStatesRequest {})
1308            .await?;
1309        Ok(resp.states)
1310    }
1311
1312    pub async fn list_actor_splits(&self) -> Result<Vec<ActorSplit>> {
1313        let resp = self
1314            .inner
1315            .list_actor_splits(ListActorSplitsRequest {})
1316            .await?;
1317
1318        Ok(resp.actor_splits)
1319    }
1320
1321    pub async fn pause(&self) -> Result<PauseResponse> {
1322        let request = PauseRequest {};
1323        let resp = self.inner.pause(request).await?;
1324        Ok(resp)
1325    }
1326
1327    pub async fn resume(&self) -> Result<ResumeResponse> {
1328        let request = ResumeRequest {};
1329        let resp = self.inner.resume(request).await?;
1330        Ok(resp)
1331    }
1332
1333    pub async fn apply_throttle(
1334        &self,
1335        throttle_target: PbThrottleTarget,
1336        throttle_type: risingwave_pb::common::PbThrottleType,
1337        id: u32,
1338        rate: Option<u32>,
1339    ) -> Result<ApplyThrottleResponse> {
1340        let request = ApplyThrottleRequest {
1341            throttle_target: throttle_target as i32,
1342            throttle_type: throttle_type as i32,
1343            id,
1344            rate,
1345        };
1346        let resp = self.inner.apply_throttle(request).await?;
1347        Ok(resp)
1348    }
1349
1350    pub async fn get_cluster_recovery_status(&self) -> Result<RecoveryStatus> {
1351        let resp = self
1352            .inner
1353            .get_cluster_recovery_status(GetClusterRecoveryStatusRequest {})
1354            .await?;
1355        Ok(resp.get_status().unwrap())
1356    }
1357
1358    pub async fn get_cluster_info(&self) -> Result<GetClusterInfoResponse> {
1359        let request = GetClusterInfoRequest {};
1360        let resp = self.inner.get_cluster_info(request).await?;
1361        Ok(resp)
1362    }
1363
1364    pub async fn reschedule(
1365        &self,
1366        worker_reschedules: HashMap<u32, PbWorkerReschedule>,
1367        revision: u64,
1368        resolve_no_shuffle_upstream: bool,
1369    ) -> Result<(bool, u64)> {
1370        let request = RescheduleRequest {
1371            revision,
1372            resolve_no_shuffle_upstream,
1373            worker_reschedules,
1374        };
1375        let resp = self.inner.reschedule(request).await?;
1376        Ok((resp.success, resp.revision))
1377    }
1378
1379    pub async fn risectl_get_pinned_versions_summary(
1380        &self,
1381    ) -> Result<RiseCtlGetPinnedVersionsSummaryResponse> {
1382        let request = RiseCtlGetPinnedVersionsSummaryRequest {};
1383        self.inner
1384            .rise_ctl_get_pinned_versions_summary(request)
1385            .await
1386    }
1387
1388    pub async fn risectl_get_checkpoint_hummock_version(
1389        &self,
1390    ) -> Result<RiseCtlGetCheckpointVersionResponse> {
1391        let request = RiseCtlGetCheckpointVersionRequest {};
1392        self.inner.rise_ctl_get_checkpoint_version(request).await
1393    }
1394
1395    pub async fn risectl_pause_hummock_version_checkpoint(
1396        &self,
1397    ) -> Result<RiseCtlPauseVersionCheckpointResponse> {
1398        let request = RiseCtlPauseVersionCheckpointRequest {};
1399        self.inner.rise_ctl_pause_version_checkpoint(request).await
1400    }
1401
1402    pub async fn risectl_resume_hummock_version_checkpoint(
1403        &self,
1404    ) -> Result<RiseCtlResumeVersionCheckpointResponse> {
1405        let request = RiseCtlResumeVersionCheckpointRequest {};
1406        self.inner.rise_ctl_resume_version_checkpoint(request).await
1407    }
1408
1409    pub async fn init_metadata_for_replay(
1410        &self,
1411        tables: Vec<PbTable>,
1412        compaction_groups: Vec<CompactionGroupInfo>,
1413    ) -> Result<()> {
1414        let req = InitMetadataForReplayRequest {
1415            tables,
1416            compaction_groups,
1417        };
1418        let _resp = self.inner.init_metadata_for_replay(req).await?;
1419        Ok(())
1420    }
1421
1422    pub async fn replay_version_delta(
1423        &self,
1424        version_delta: HummockVersionDelta,
1425    ) -> Result<(HummockVersion, Vec<CompactionGroupId>)> {
1426        let req = ReplayVersionDeltaRequest {
1427            version_delta: Some(version_delta.into()),
1428        };
1429        let resp = self.inner.replay_version_delta(req).await?;
1430        Ok((
1431            HummockVersion::from_rpc_protobuf(&resp.version.unwrap()),
1432            resp.modified_compaction_groups,
1433        ))
1434    }
1435
1436    pub async fn list_version_deltas(
1437        &self,
1438        start_id: HummockVersionId,
1439        num_limit: u32,
1440        committed_epoch_limit: HummockEpoch,
1441    ) -> Result<Vec<HummockVersionDelta>> {
1442        let req = ListVersionDeltasRequest {
1443            start_id,
1444            num_limit,
1445            committed_epoch_limit,
1446        };
1447        Ok(self
1448            .inner
1449            .list_version_deltas(req)
1450            .await?
1451            .version_deltas
1452            .unwrap()
1453            .version_deltas
1454            .iter()
1455            .map(HummockVersionDelta::from_rpc_protobuf)
1456            .collect())
1457    }
1458
1459    pub async fn trigger_compaction_deterministic(
1460        &self,
1461        version_id: HummockVersionId,
1462        compaction_groups: Vec<CompactionGroupId>,
1463    ) -> Result<()> {
1464        let req = TriggerCompactionDeterministicRequest {
1465            version_id,
1466            compaction_groups,
1467        };
1468        self.inner.trigger_compaction_deterministic(req).await?;
1469        Ok(())
1470    }
1471
1472    pub async fn disable_commit_epoch(&self) -> Result<HummockVersion> {
1473        let req = DisableCommitEpochRequest {};
1474        Ok(HummockVersion::from_rpc_protobuf(
1475            &self
1476                .inner
1477                .disable_commit_epoch(req)
1478                .await?
1479                .current_version
1480                .unwrap(),
1481        ))
1482    }
1483
1484    pub async fn get_assigned_compact_task_num(&self) -> Result<usize> {
1485        let req = GetAssignedCompactTaskNumRequest {};
1486        let resp = self.inner.get_assigned_compact_task_num(req).await?;
1487        Ok(resp.num_tasks as usize)
1488    }
1489
1490    pub async fn risectl_list_compaction_group(&self) -> Result<Vec<CompactionGroupInfo>> {
1491        let req = RiseCtlListCompactionGroupRequest {};
1492        let resp = self.inner.rise_ctl_list_compaction_group(req).await?;
1493        Ok(resp.compaction_groups)
1494    }
1495
1496    pub async fn risectl_update_compaction_config(
1497        &self,
1498        compaction_groups: &[CompactionGroupId],
1499        configs: &[MutableConfig],
1500    ) -> Result<()> {
1501        let req = RiseCtlUpdateCompactionConfigRequest {
1502            compaction_group_ids: compaction_groups.to_vec(),
1503            configs: configs
1504                .iter()
1505                .map(
1506                    |c| rise_ctl_update_compaction_config_request::MutableConfig {
1507                        mutable_config: Some(c.clone()),
1508                    },
1509                )
1510                .collect(),
1511        };
1512        let _resp = self.inner.rise_ctl_update_compaction_config(req).await?;
1513        Ok(())
1514    }
1515
1516    pub async fn backup_meta(&self, remarks: Option<String>) -> Result<u64> {
1517        let req = BackupMetaRequest { remarks };
1518        let resp = self.inner.backup_meta(req).await?;
1519        Ok(resp.job_id)
1520    }
1521
1522    pub async fn get_backup_job_status(&self, job_id: u64) -> Result<(BackupJobStatus, String)> {
1523        let req = GetBackupJobStatusRequest { job_id };
1524        let resp = self.inner.get_backup_job_status(req).await?;
1525        Ok((resp.job_status(), resp.message))
1526    }
1527
1528    pub async fn delete_meta_snapshot(&self, snapshot_ids: &[u64]) -> Result<()> {
1529        let req = DeleteMetaSnapshotRequest {
1530            snapshot_ids: snapshot_ids.to_vec(),
1531        };
1532        let _resp = self.inner.delete_meta_snapshot(req).await?;
1533        Ok(())
1534    }
1535
1536    pub async fn get_meta_snapshot_manifest(&self) -> Result<MetaSnapshotManifest> {
1537        let req = GetMetaSnapshotManifestRequest {};
1538        let resp = self.inner.get_meta_snapshot_manifest(req).await?;
1539        Ok(resp.manifest.expect("should exist"))
1540    }
1541
1542    pub async fn get_telemetry_info(&self) -> Result<TelemetryInfoResponse> {
1543        let req = GetTelemetryInfoRequest {};
1544        let resp = self.inner.get_telemetry_info(req).await?;
1545        Ok(resp)
1546    }
1547
1548    pub async fn get_meta_store_endpoint(&self) -> Result<String> {
1549        let req = GetMetaStoreInfoRequest {};
1550        let resp = self.inner.get_meta_store_info(req).await?;
1551        Ok(resp.meta_store_endpoint)
1552    }
1553
1554    pub async fn alter_sink_props(
1555        &self,
1556        sink_id: SinkId,
1557        changed_props: BTreeMap<String, String>,
1558        changed_secret_refs: BTreeMap<String, PbSecretRef>,
1559        connector_conn_ref: Option<ConnectionId>,
1560    ) -> Result<()> {
1561        let req = AlterConnectorPropsRequest {
1562            object_id: sink_id.as_raw_id(),
1563            changed_props: changed_props.into_iter().collect(),
1564            changed_secret_refs: changed_secret_refs.into_iter().collect(),
1565            connector_conn_ref,
1566            object_type: AlterConnectorPropsObject::Sink as i32,
1567            extra_options: None,
1568        };
1569        let _resp = self.inner.alter_connector_props(req).await?;
1570        Ok(())
1571    }
1572
1573    pub async fn alter_iceberg_table_props(
1574        &self,
1575        table_id: TableId,
1576        sink_id: SinkId,
1577        source_id: SourceId,
1578        changed_props: BTreeMap<String, String>,
1579        changed_secret_refs: BTreeMap<String, PbSecretRef>,
1580        connector_conn_ref: Option<ConnectionId>,
1581    ) -> Result<()> {
1582        let req = AlterConnectorPropsRequest {
1583            object_id: table_id.as_raw_id(),
1584            changed_props: changed_props.into_iter().collect(),
1585            changed_secret_refs: changed_secret_refs.into_iter().collect(),
1586            connector_conn_ref,
1587            object_type: AlterConnectorPropsObject::IcebergTable as i32,
1588            extra_options: Some(ExtraOptions::AlterIcebergTableIds(AlterIcebergTableIds {
1589                sink_id,
1590                source_id,
1591            })),
1592        };
1593        let _resp = self.inner.alter_connector_props(req).await?;
1594        Ok(())
1595    }
1596
1597    pub async fn alter_source_connector_props(
1598        &self,
1599        source_id: SourceId,
1600        changed_props: BTreeMap<String, String>,
1601        changed_secret_refs: BTreeMap<String, PbSecretRef>,
1602        connector_conn_ref: Option<ConnectionId>,
1603    ) -> Result<()> {
1604        let req = AlterConnectorPropsRequest {
1605            object_id: source_id.as_raw_id(),
1606            changed_props: changed_props.into_iter().collect(),
1607            changed_secret_refs: changed_secret_refs.into_iter().collect(),
1608            connector_conn_ref,
1609            object_type: AlterConnectorPropsObject::Source as i32,
1610            extra_options: None,
1611        };
1612        let _resp = self.inner.alter_connector_props(req).await?;
1613        Ok(())
1614    }
1615
1616    pub async fn alter_connection_connector_props(
1617        &self,
1618        connection_id: u32,
1619        changed_props: BTreeMap<String, String>,
1620        changed_secret_refs: BTreeMap<String, PbSecretRef>,
1621    ) -> Result<()> {
1622        let req = AlterConnectorPropsRequest {
1623            object_id: connection_id,
1624            changed_props: changed_props.into_iter().collect(),
1625            changed_secret_refs: changed_secret_refs.into_iter().collect(),
1626            connector_conn_ref: None, // Connections don't reference other connections
1627            object_type: AlterConnectorPropsObject::Connection as i32,
1628            extra_options: None,
1629        };
1630        let _resp = self.inner.alter_connector_props(req).await?;
1631        Ok(())
1632    }
1633
1634    /// Orchestrated source property update with pause/update/resume workflow.
1635    /// This is the "safe" version that pauses sources before updating and resumes after.
1636    pub async fn alter_source_properties_safe(
1637        &self,
1638        source_id: SourceId,
1639        changed_props: BTreeMap<String, String>,
1640        changed_secret_refs: BTreeMap<String, PbSecretRef>,
1641        reset_splits: bool,
1642    ) -> Result<()> {
1643        let req = AlterSourcePropertiesSafeRequest {
1644            source_id: source_id.as_raw_id(),
1645            changed_props: changed_props.into_iter().collect(),
1646            changed_secret_refs: changed_secret_refs.into_iter().collect(),
1647            options: Some(PropertyUpdateOptions { reset_splits }),
1648        };
1649        let _resp = self.inner.alter_source_properties_safe(req).await?;
1650        Ok(())
1651    }
1652
1653    /// Reset source split assignments (UNSAFE - admin only).
1654    /// This clears persisted split metadata and triggers re-discovery.
1655    pub async fn reset_source_splits(&self, source_id: SourceId) -> Result<()> {
1656        let req = ResetSourceSplitsRequest {
1657            source_id: source_id.as_raw_id(),
1658        };
1659        let _resp = self.inner.reset_source_splits(req).await?;
1660        Ok(())
1661    }
1662
1663    /// Inject specific offsets into source splits (UNSAFE - admin only).
1664    /// This can cause data duplication or loss depending on the correctness of the provided offsets.
1665    pub async fn inject_source_offsets(
1666        &self,
1667        source_id: SourceId,
1668        split_offsets: HashMap<String, String>,
1669    ) -> Result<Vec<String>> {
1670        let req = InjectSourceOffsetsRequest {
1671            source_id: source_id.as_raw_id(),
1672            split_offsets,
1673        };
1674        let resp = self.inner.inject_source_offsets(req).await?;
1675        Ok(resp.applied_split_ids)
1676    }
1677
1678    pub async fn set_system_param(
1679        &self,
1680        param: String,
1681        value: Option<String>,
1682    ) -> Result<Option<SystemParamsReader>> {
1683        let req = SetSystemParamRequest { param, value };
1684        let resp = self.inner.set_system_param(req).await?;
1685        Ok(resp.params.map(SystemParamsReader::from))
1686    }
1687
1688    pub async fn clear_file_cache(
1689        &self,
1690        clear_meta_cache: bool,
1691        clear_data_cache: bool,
1692    ) -> Result<()> {
1693        self.inner
1694            .clear_file_cache(ClearFileCacheRequest {
1695                clear_meta_cache,
1696                clear_data_cache,
1697            })
1698            .await?;
1699        Ok(())
1700    }
1701
1702    pub async fn get_session_params(&self) -> Result<String> {
1703        let req = GetSessionParamsRequest {};
1704        let resp = self.inner.get_session_params(req).await?;
1705        Ok(resp.params)
1706    }
1707
1708    pub async fn set_session_param(&self, param: String, value: Option<String>) -> Result<String> {
1709        let req = SetSessionParamRequest { param, value };
1710        let resp = self.inner.set_session_param(req).await?;
1711        Ok(resp.param)
1712    }
1713
1714    pub async fn get_ddl_progress(&self) -> Result<Vec<DdlProgress>> {
1715        let req = GetDdlProgressRequest {};
1716        let resp = self.inner.get_ddl_progress(req).await?;
1717        Ok(resp.ddl_progress)
1718    }
1719
1720    pub async fn split_compaction_group(
1721        &self,
1722        group_id: CompactionGroupId,
1723        table_ids_to_new_group: &[TableId],
1724        partition_vnode_count: u32,
1725    ) -> Result<CompactionGroupId> {
1726        let req = SplitCompactionGroupRequest {
1727            group_id,
1728            table_ids: table_ids_to_new_group.to_vec(),
1729            partition_vnode_count,
1730        };
1731        let resp = self.inner.split_compaction_group(req).await?;
1732        Ok(resp.new_group_id)
1733    }
1734
1735    pub async fn get_tables(
1736        &self,
1737        table_ids: Vec<TableId>,
1738        include_dropped_tables: bool,
1739    ) -> Result<HashMap<TableId, Table>> {
1740        let req = GetTablesRequest {
1741            table_ids,
1742            include_dropped_tables,
1743        };
1744        let resp = self.inner.get_tables(req).await?;
1745        Ok(resp.tables)
1746    }
1747
1748    pub async fn list_serving_vnode_mappings(
1749        &self,
1750    ) -> Result<HashMap<FragmentId, (JobId, WorkerSlotMapping)>> {
1751        let req = GetServingVnodeMappingsRequest {};
1752        let resp = self.inner.get_serving_vnode_mappings(req).await?;
1753        let mappings = resp
1754            .worker_slot_mappings
1755            .into_iter()
1756            .map(|p| {
1757                (
1758                    p.fragment_id,
1759                    (
1760                        resp.fragment_to_table
1761                            .get(&p.fragment_id)
1762                            .cloned()
1763                            .unwrap_or_default(),
1764                        WorkerSlotMapping::from_protobuf(p.mapping.as_ref().unwrap()),
1765                    ),
1766                )
1767            })
1768            .collect();
1769        Ok(mappings)
1770    }
1771
1772    pub async fn warm_up_table_cache(
1773        &self,
1774        table_id: TableId,
1775        concurrency: u32,
1776    ) -> Result<(u64, u32)> {
1777        let response = self
1778            .inner
1779            .warm_up_table_cache(WarmUpTableCacheRequest {
1780                table_id,
1781                concurrency,
1782            })
1783            .await?;
1784        Ok((response.key_count, response.worker_count))
1785    }
1786
1787    pub async fn risectl_list_compaction_status(
1788        &self,
1789    ) -> Result<(
1790        Vec<CompactStatus>,
1791        Vec<CompactTaskAssignment>,
1792        Vec<CompactTaskProgress>,
1793    )> {
1794        let req = RiseCtlListCompactionStatusRequest {};
1795        let resp = self.inner.rise_ctl_list_compaction_status(req).await?;
1796        Ok((
1797            resp.compaction_statuses,
1798            resp.task_assignment,
1799            resp.task_progress,
1800        ))
1801    }
1802
1803    pub async fn get_compaction_score(
1804        &self,
1805        compaction_group_id: CompactionGroupId,
1806    ) -> Result<Vec<PickerInfo>> {
1807        let req = GetCompactionScoreRequest {
1808            compaction_group_id,
1809        };
1810        let resp = self.inner.get_compaction_score(req).await?;
1811        Ok(resp.scores)
1812    }
1813
1814    pub async fn risectl_rebuild_table_stats(&self) -> Result<()> {
1815        let req = RiseCtlRebuildTableStatsRequest {};
1816        let _resp = self.inner.rise_ctl_rebuild_table_stats(req).await?;
1817        Ok(())
1818    }
1819
1820    pub async fn list_branched_object(&self) -> Result<Vec<BranchedObject>> {
1821        let req = ListBranchedObjectRequest {};
1822        let resp = self.inner.list_branched_object(req).await?;
1823        Ok(resp.branched_objects)
1824    }
1825
1826    pub async fn list_active_write_limit(&self) -> Result<HashMap<CompactionGroupId, WriteLimit>> {
1827        let req = ListActiveWriteLimitRequest {};
1828        let resp = self.inner.list_active_write_limit(req).await?;
1829        Ok(resp.write_limits)
1830    }
1831
1832    pub async fn list_hummock_meta_config(&self) -> Result<HashMap<String, String>> {
1833        let req = ListHummockMetaConfigRequest {};
1834        let resp = self.inner.list_hummock_meta_config(req).await?;
1835        Ok(resp.configs)
1836    }
1837
1838    pub async fn get_table_change_logs(
1839        &self,
1840        epoch_only: bool,
1841        start_epoch_inclusive: Option<u64>,
1842        end_epoch_inclusive: Option<u64>,
1843        table_ids: Option<HashSet<TableId>>,
1844        exclude_empty: bool,
1845        limit: Option<u32>,
1846    ) -> Result<TableChangeLogs> {
1847        let req = GetTableChangeLogsRequest {
1848            epoch_only,
1849            start_epoch_inclusive,
1850            end_epoch_inclusive,
1851            table_ids: table_ids.map(|iter| PbTableFilter {
1852                table_ids: iter.into_iter().collect::<Vec<_>>(),
1853            }),
1854            exclude_empty,
1855            limit,
1856        };
1857        let resp = self.inner.get_table_change_logs(req).await?;
1858        Ok(resp
1859            .table_change_logs
1860            .into_iter()
1861            .map(|(id, change_log)| (TableId::new(id), TableChangeLog::from_protobuf(&change_log)))
1862            .collect())
1863    }
1864
1865    pub async fn delete_worker_node(&self, worker: HostAddress) -> Result<()> {
1866        let _resp = self
1867            .inner
1868            .delete_worker_node(DeleteWorkerNodeRequest { host: Some(worker) })
1869            .await?;
1870
1871        Ok(())
1872    }
1873
1874    pub async fn rw_cloud_validate_source(
1875        &self,
1876        source_type: SourceType,
1877        source_config: HashMap<String, String>,
1878    ) -> Result<RwCloudValidateSourceResponse> {
1879        let req = RwCloudValidateSourceRequest {
1880            source_type: source_type.into(),
1881            source_config,
1882        };
1883        let resp = self.inner.rw_cloud_validate_source(req).await?;
1884        Ok(resp)
1885    }
1886
1887    pub async fn sink_coordinate_client(&self) -> SinkCoordinationRpcClient {
1888        self.inner.core.read().await.sink_coordinate_client.clone()
1889    }
1890
1891    pub async fn list_compact_task_assignment(&self) -> Result<Vec<CompactTaskAssignment>> {
1892        let req = ListCompactTaskAssignmentRequest {};
1893        let resp = self.inner.list_compact_task_assignment(req).await?;
1894        Ok(resp.task_assignment)
1895    }
1896
1897    pub async fn list_event_log(&self) -> Result<Vec<EventLog>> {
1898        let req = ListEventLogRequest::default();
1899        let resp = self.inner.list_event_log(req).await?;
1900        Ok(resp.event_logs)
1901    }
1902
1903    pub async fn list_compact_task_progress(&self) -> Result<Vec<CompactTaskProgress>> {
1904        let req = ListCompactTaskProgressRequest {};
1905        let resp = self.inner.list_compact_task_progress(req).await?;
1906        Ok(resp.task_progress)
1907    }
1908
1909    #[cfg(madsim)]
1910    pub fn try_add_panic_event_blocking(
1911        &self,
1912        panic_info: impl Display,
1913        timeout_millis: Option<u64>,
1914    ) {
1915    }
1916
1917    /// If `timeout_millis` is None, default is used.
1918    #[cfg(not(madsim))]
1919    pub fn try_add_panic_event_blocking(
1920        &self,
1921        panic_info: impl Display,
1922        timeout_millis: Option<u64>,
1923    ) {
1924        let event = event_log::EventWorkerNodePanic {
1925            worker_id: self.worker_id,
1926            worker_type: self.worker_type.into(),
1927            host_addr: Some(self.host_addr.to_protobuf()),
1928            panic_info: format!("{panic_info}"),
1929        };
1930        let grpc_meta_client = self.inner.clone();
1931        let _ = thread::spawn(move || {
1932            let rt = tokio::runtime::Builder::new_current_thread()
1933                .enable_all()
1934                .build()
1935                .unwrap();
1936            let req = AddEventLogRequest {
1937                event: Some(add_event_log_request::Event::WorkerNodePanic(event)),
1938            };
1939            rt.block_on(async {
1940                let _ = tokio::time::timeout(
1941                    Duration::from_millis(timeout_millis.unwrap_or(1000)),
1942                    grpc_meta_client.add_event_log(req),
1943                )
1944                .await;
1945            });
1946        })
1947        .join();
1948    }
1949
1950    pub async fn add_sink_fail_evet(
1951        &self,
1952        sink_id: SinkId,
1953        sink_name: String,
1954        connector: String,
1955        error: String,
1956    ) -> Result<()> {
1957        let event = event_log::EventSinkFail {
1958            sink_id,
1959            sink_name,
1960            connector,
1961            error,
1962        };
1963        let req = AddEventLogRequest {
1964            event: Some(add_event_log_request::Event::SinkFail(event)),
1965        };
1966        self.inner.add_event_log(req).await?;
1967        Ok(())
1968    }
1969
1970    pub async fn add_cdc_auto_schema_change_fail_event(
1971        &self,
1972        source_id: SourceId,
1973        table_name: String,
1974        cdc_table_id: String,
1975        upstream_ddl: String,
1976        fail_info: String,
1977    ) -> Result<()> {
1978        let event = event_log::EventAutoSchemaChangeFail {
1979            table_id: source_id.as_cdc_table_id(),
1980            table_name,
1981            cdc_table_id,
1982            upstream_ddl,
1983            fail_info,
1984        };
1985        let req = AddEventLogRequest {
1986            event: Some(add_event_log_request::Event::AutoSchemaChangeFail(event)),
1987        };
1988        self.inner.add_event_log(req).await?;
1989        Ok(())
1990    }
1991
1992    pub async fn cancel_compact_task(&self, task_id: u64, task_status: TaskStatus) -> Result<bool> {
1993        let req = CancelCompactTaskRequest {
1994            task_id,
1995            task_status: task_status as _,
1996        };
1997        let resp = self.inner.cancel_compact_task(req).await?;
1998        Ok(resp.ret)
1999    }
2000
2001    pub async fn get_version_by_epoch(
2002        &self,
2003        epoch: HummockEpoch,
2004        table_id: TableId,
2005    ) -> Result<PbHummockVersion> {
2006        let req = GetVersionByEpochRequest { epoch, table_id };
2007        let resp = self.inner.get_version_by_epoch(req).await?;
2008        Ok(resp.version.unwrap())
2009    }
2010
2011    pub async fn get_cluster_limits(
2012        &self,
2013    ) -> Result<Vec<risingwave_common::util::cluster_limit::ClusterLimit>> {
2014        let req = GetClusterLimitsRequest {};
2015        let resp = self.inner.get_cluster_limits(req).await?;
2016        Ok(resp.active_limits.into_iter().map(|l| l.into()).collect())
2017    }
2018
2019    pub async fn merge_compaction_group(
2020        &self,
2021        left_group_id: CompactionGroupId,
2022        right_group_id: CompactionGroupId,
2023    ) -> Result<()> {
2024        let req = MergeCompactionGroupRequest {
2025            left_group_id,
2026            right_group_id,
2027        };
2028        self.inner.merge_compaction_group(req).await?;
2029        Ok(())
2030    }
2031
2032    /// List all rate limits for sources and backfills
2033    pub async fn list_rate_limits(&self) -> Result<Vec<RateLimitInfo>> {
2034        let request = ListRateLimitsRequest {};
2035        let resp = self.inner.list_rate_limits(request).await?;
2036        Ok(resp.rate_limits)
2037    }
2038
2039    pub async fn list_cdc_progress(&self) -> Result<HashMap<JobId, PbCdcProgress>> {
2040        let request = ListCdcProgressRequest {};
2041        let resp = self.inner.list_cdc_progress(request).await?;
2042        Ok(resp.cdc_progress)
2043    }
2044
2045    pub async fn list_refresh_table_states(&self) -> Result<Vec<RefreshTableState>> {
2046        let request = ListRefreshTableStatesRequest {};
2047        let resp = self.inner.list_refresh_table_states(request).await?;
2048        Ok(resp.states)
2049    }
2050
2051    pub async fn list_iceberg_compaction_status(
2052        &self,
2053    ) -> Result<Vec<list_iceberg_compaction_status_response::IcebergCompactionStatus>> {
2054        let request = ListIcebergCompactionStatusRequest {};
2055        let resp = self.inner.list_iceberg_compaction_status(request).await?;
2056        Ok(resp.statuses)
2057    }
2058
2059    pub async fn list_sink_log_store_tables(
2060        &self,
2061    ) -> Result<Vec<list_sink_log_store_tables_response::SinkLogStoreTable>> {
2062        let request = ListSinkLogStoreTablesRequest {};
2063        let resp = self.inner.list_sink_log_store_tables(request).await?;
2064        Ok(resp.tables)
2065    }
2066
2067    pub async fn create_iceberg_table(
2068        &self,
2069        table_job_info: PbTableJobInfo,
2070        sink_job_info: PbSinkJobInfo,
2071        iceberg_source: PbSource,
2072        if_not_exists: bool,
2073    ) -> Result<WaitVersion> {
2074        let request = CreateIcebergTableRequest {
2075            table_info: Some(table_job_info),
2076            sink_info: Some(sink_job_info),
2077            iceberg_source: Some(iceberg_source),
2078            if_not_exists,
2079        };
2080
2081        let resp = Box::pin(self.inner.create_iceberg_table(request)).await?;
2082        Ok(resp
2083            .version
2084            .ok_or_else(|| anyhow!("wait version not set"))?)
2085    }
2086
2087    pub async fn list_hosted_iceberg_tables(&self) -> Result<Vec<IcebergTable>> {
2088        let request = ListIcebergTablesRequest {};
2089        let resp = self.inner.list_iceberg_tables(request).await?;
2090        Ok(resp.iceberg_tables)
2091    }
2092
2093    /// Get the await tree of all nodes in the cluster.
2094    pub async fn get_cluster_stack_trace(
2095        &self,
2096        actor_traces_format: ActorTracesFormat,
2097    ) -> Result<StackTraceResponse> {
2098        let request = StackTraceRequest {
2099            actor_traces_format: actor_traces_format as i32,
2100        };
2101        let resp = self.inner.stack_trace(request).await?;
2102        Ok(resp)
2103    }
2104
2105    pub async fn set_sync_log_store_aligned(&self, job_id: JobId, aligned: bool) -> Result<()> {
2106        let request = SetSyncLogStoreAlignedRequest { job_id, aligned };
2107        self.inner.set_sync_log_store_aligned(request).await?;
2108        Ok(())
2109    }
2110
2111    pub async fn refresh(&self, request: RefreshRequest) -> Result<RefreshResponse> {
2112        self.inner.refresh(request).await
2113    }
2114
2115    pub async fn list_unmigrated_tables(&self) -> Result<HashMap<TableId, String>> {
2116        let request = ListUnmigratedTablesRequest {};
2117        let resp = self.inner.list_unmigrated_tables(request).await?;
2118        Ok(resp
2119            .tables
2120            .into_iter()
2121            .map(|table| (table.table_id, table.table_name))
2122            .collect())
2123    }
2124}
2125
2126#[async_trait]
2127impl HummockMetaClient for MetaClient {
2128    async fn unpin_version_before(&self, unpin_version_before: HummockVersionId) -> Result<()> {
2129        let req = UnpinVersionBeforeRequest {
2130            context_id: self.worker_id(),
2131            unpin_version_before,
2132        };
2133        self.inner.unpin_version_before(req).await?;
2134        Ok(())
2135    }
2136
2137    async fn get_current_version(&self) -> Result<HummockVersion> {
2138        let req = GetCurrentVersionRequest::default();
2139        Ok(HummockVersion::from_rpc_protobuf(
2140            &self
2141                .inner
2142                .get_current_version(req)
2143                .await?
2144                .current_version
2145                .unwrap(),
2146        ))
2147    }
2148
2149    async fn get_new_object_ids(&self, number: u32) -> Result<ObjectIdRange> {
2150        let resp = self
2151            .inner
2152            .get_new_object_ids(GetNewObjectIdsRequest { number })
2153            .await?;
2154        Ok(ObjectIdRange::new(resp.start_id, resp.end_id))
2155    }
2156
2157    async fn commit_epoch_with_change_log(
2158        &self,
2159        _epoch: HummockEpoch,
2160        _sync_result: SyncResult,
2161        _change_log_info: Option<HummockMetaClientChangeLogInfo>,
2162    ) -> Result<()> {
2163        panic!("Only meta service can commit_epoch in production.")
2164    }
2165
2166    async fn trigger_manual_compaction(
2167        &self,
2168        compaction_group_id: CompactionGroupId,
2169        table_id: JobId,
2170        level: u32,
2171        target_level: Option<u32>,
2172        sst_ids: Vec<HummockSstableId>,
2173        exclusive: bool,
2174    ) -> Result<bool> {
2175        // TODO: support key_range parameter
2176        let req = TriggerManualCompactionRequest {
2177            compaction_group_id,
2178            table_id,
2179            // if table_id not exist, manual_compaction will include all the sst
2180            // without check internal_table_id
2181            level,
2182            target_level,
2183            sst_ids,
2184            exclusive: Some(exclusive),
2185            ..Default::default()
2186        };
2187
2188        let resp = self.inner.trigger_manual_compaction(req).await?;
2189        Ok(resp.should_retry.unwrap_or(false))
2190    }
2191
2192    async fn trigger_full_gc(
2193        &self,
2194        sst_retention_time_sec: u64,
2195        prefix: Option<String>,
2196    ) -> Result<()> {
2197        self.inner
2198            .trigger_full_gc(TriggerFullGcRequest {
2199                sst_retention_time_sec,
2200                prefix,
2201            })
2202            .await?;
2203        Ok(())
2204    }
2205
2206    async fn subscribe_compaction_event(
2207        &self,
2208    ) -> Result<(
2209        UnboundedSender<SubscribeCompactionEventRequest>,
2210        BoxStream<'static, CompactionEventItem>,
2211    )> {
2212        let (request_sender, request_receiver) =
2213            unbounded_channel::<SubscribeCompactionEventRequest>();
2214        request_sender
2215            .send(SubscribeCompactionEventRequest {
2216                event: Some(subscribe_compaction_event_request::Event::Register(
2217                    Register {
2218                        context_id: self.worker_id(),
2219                    },
2220                )),
2221                create_at: SystemTime::now()
2222                    .duration_since(SystemTime::UNIX_EPOCH)
2223                    .expect("Clock may have gone backwards")
2224                    .as_millis() as u64,
2225            })
2226            .context("Failed to subscribe compaction event")?;
2227
2228        let stream = self
2229            .inner
2230            .subscribe_compaction_event(Request::new(UnboundedReceiverStream::new(
2231                request_receiver,
2232            )))
2233            .await?;
2234
2235        Ok((request_sender, Box::pin(stream)))
2236    }
2237
2238    async fn get_version_by_epoch(
2239        &self,
2240        epoch: HummockEpoch,
2241        table_id: TableId,
2242    ) -> Result<PbHummockVersion> {
2243        self.get_version_by_epoch(epoch, table_id).await
2244    }
2245
2246    async fn subscribe_iceberg_compaction_event(
2247        &self,
2248    ) -> Result<(
2249        UnboundedSender<SubscribeIcebergCompactionEventRequest>,
2250        BoxStream<'static, IcebergCompactionEventItem>,
2251    )> {
2252        let (request_sender, request_receiver) =
2253            unbounded_channel::<SubscribeIcebergCompactionEventRequest>();
2254        request_sender
2255            .send(SubscribeIcebergCompactionEventRequest {
2256                event: Some(subscribe_iceberg_compaction_event_request::Event::Register(
2257                    IcebergRegister {
2258                        context_id: self.worker_id(),
2259                    },
2260                )),
2261                create_at: SystemTime::now()
2262                    .duration_since(SystemTime::UNIX_EPOCH)
2263                    .expect("Clock may have gone backwards")
2264                    .as_millis() as u64,
2265            })
2266            .context("Failed to subscribe compaction event")?;
2267
2268        let stream = self
2269            .inner
2270            .subscribe_iceberg_compaction_event(Request::new(UnboundedReceiverStream::new(
2271                request_receiver,
2272            )))
2273            .await?;
2274
2275        Ok((request_sender, Box::pin(stream)))
2276    }
2277
2278    async fn get_table_change_logs(
2279        &self,
2280        epoch_only: bool,
2281        start_epoch_inclusive: Option<u64>,
2282        end_epoch_inclusive: Option<u64>,
2283        table_ids: Option<HashSet<TableId>>,
2284        exclude_empty: bool,
2285        limit: Option<u32>,
2286    ) -> Result<TableChangeLogs> {
2287        self.get_table_change_logs(
2288            epoch_only,
2289            start_epoch_inclusive,
2290            end_epoch_inclusive,
2291            table_ids,
2292            exclude_empty,
2293            limit,
2294        )
2295        .await
2296    }
2297}
2298
2299#[async_trait]
2300impl TelemetryInfoFetcher for MetaClient {
2301    async fn fetch_telemetry_info(&self) -> std::result::Result<Option<String>, String> {
2302        let resp = self
2303            .get_telemetry_info()
2304            .await
2305            .map_err(|e| e.to_report_string())?;
2306        let tracking_id = resp.get_tracking_id().ok();
2307        Ok(tracking_id.map(|id| id.to_owned()))
2308    }
2309}
2310
2311pub type SinkCoordinationRpcClient = SinkCoordinationServiceClient<Channel>;
2312
2313#[derive(Debug, Clone)]
2314struct GrpcMetaClientCore {
2315    cluster_client: ClusterServiceClient<Channel>,
2316    meta_member_client: MetaMemberServiceClient<Channel>,
2317    heartbeat_client: HeartbeatServiceClient<Channel>,
2318    ddl_client: DdlServiceClient<Channel>,
2319    hummock_client: HummockManagerServiceClient<Channel>,
2320    notification_client: NotificationServiceClient<Channel>,
2321    stream_client: StreamManagerServiceClient<Channel>,
2322    user_client: UserServiceClient<Channel>,
2323    scale_client: ScaleServiceClient<Channel>,
2324    backup_client: BackupServiceClient<Channel>,
2325    telemetry_client: TelemetryInfoServiceClient<Channel>,
2326    system_params_client: SystemParamsServiceClient<Channel>,
2327    session_params_client: SessionParamServiceClient<Channel>,
2328    serving_client: ServingServiceClient<Channel>,
2329    cloud_client: CloudServiceClient<Channel>,
2330    sink_coordinate_client: SinkCoordinationRpcClient,
2331    event_log_client: EventLogServiceClient<Channel>,
2332    cluster_limit_client: ClusterLimitServiceClient<Channel>,
2333    hosted_iceberg_catalog_service_client: HostedIcebergCatalogServiceClient<Channel>,
2334    monitor_client: MonitorServiceClient<Channel>,
2335}
2336
2337impl GrpcMetaClientCore {
2338    pub(crate) fn new(channel: Channel) -> Self {
2339        let cluster_client = ClusterServiceClient::new(channel.clone());
2340        let meta_member_client = MetaMemberClient::new(channel.clone());
2341        let heartbeat_client = HeartbeatServiceClient::new(channel.clone());
2342        let ddl_client =
2343            DdlServiceClient::new(channel.clone()).max_decoding_message_size(usize::MAX);
2344        let hummock_client =
2345            HummockManagerServiceClient::new(channel.clone()).max_decoding_message_size(usize::MAX);
2346        // Accept zstd-compressed notifications: the initial `MetaSnapshot` on `Subscribe`
2347        // carries the full hummock version, and a single uncompressed gRPC message larger
2348        // than `i32::MAX` bytes is silently reset by hyper/h2 and can never be delivered.
2349        let notification_client = NotificationServiceClient::new(channel.clone())
2350            .accept_compressed(tonic::codec::CompressionEncoding::Zstd)
2351            .max_decoding_message_size(usize::MAX);
2352        let stream_client =
2353            StreamManagerServiceClient::new(channel.clone()).max_decoding_message_size(usize::MAX);
2354        let user_client = UserServiceClient::new(channel.clone());
2355        let scale_client =
2356            ScaleServiceClient::new(channel.clone()).max_decoding_message_size(usize::MAX);
2357        let backup_client = BackupServiceClient::new(channel.clone());
2358        let telemetry_client =
2359            TelemetryInfoServiceClient::new(channel.clone()).max_decoding_message_size(usize::MAX);
2360        let system_params_client = SystemParamsServiceClient::new(channel.clone());
2361        let session_params_client = SessionParamServiceClient::new(channel.clone());
2362        let serving_client = ServingServiceClient::new(channel.clone());
2363        let cloud_client = CloudServiceClient::new(channel.clone());
2364        let sink_coordinate_client = SinkCoordinationServiceClient::new(channel.clone())
2365            .max_decoding_message_size(usize::MAX);
2366        let event_log_client = EventLogServiceClient::new(channel.clone());
2367        let cluster_limit_client = ClusterLimitServiceClient::new(channel.clone());
2368        let hosted_iceberg_catalog_service_client =
2369            HostedIcebergCatalogServiceClient::new(channel.clone());
2370        let monitor_client = configured_monitor_service_client(MonitorServiceClient::new(channel));
2371
2372        GrpcMetaClientCore {
2373            cluster_client,
2374            meta_member_client,
2375            heartbeat_client,
2376            ddl_client,
2377            hummock_client,
2378            notification_client,
2379            stream_client,
2380            user_client,
2381            scale_client,
2382            backup_client,
2383            telemetry_client,
2384            system_params_client,
2385            session_params_client,
2386            serving_client,
2387            cloud_client,
2388            sink_coordinate_client,
2389            event_log_client,
2390            cluster_limit_client,
2391            hosted_iceberg_catalog_service_client,
2392            monitor_client,
2393        }
2394    }
2395}
2396
2397/// Client to meta server. Cloning the instance is lightweight.
2398///
2399/// It is a wrapper of tonic client. See [`crate::meta_rpc_client_method_impl`].
2400#[derive(Debug, Clone)]
2401struct GrpcMetaClient {
2402    member_monitor_event_sender: mpsc::Sender<Sender<Result<()>>>,
2403    core: Arc<RwLock<GrpcMetaClientCore>>,
2404}
2405
2406type MetaMemberClient = MetaMemberServiceClient<Channel>;
2407
2408struct MetaMemberGroup {
2409    members: LruCache<http::Uri, Option<MetaMemberClient>>,
2410}
2411
2412struct MetaMemberManagement {
2413    core_ref: Arc<RwLock<GrpcMetaClientCore>>,
2414    members: Either<MetaMemberClient, MetaMemberGroup>,
2415    current_leader: http::Uri,
2416    meta_config: Arc<MetaConfig>,
2417}
2418
2419impl MetaMemberManagement {
2420    const META_MEMBER_REFRESH_PERIOD: Duration = Duration::from_secs(5);
2421
2422    fn host_address_to_uri(addr: HostAddress) -> http::Uri {
2423        format!("http://{}:{}", addr.host, addr.port)
2424            .parse()
2425            .unwrap()
2426    }
2427
2428    async fn recreate_core(&self, channel: Channel) {
2429        let mut core = self.core_ref.write().await;
2430        *core = GrpcMetaClientCore::new(channel);
2431    }
2432
2433    async fn refresh_members(&mut self) -> Result<()> {
2434        let leader_addr = match self.members.as_mut() {
2435            Either::Left(client) => {
2436                let resp = client
2437                    .to_owned()
2438                    .members(MembersRequest {})
2439                    .await
2440                    .map_err(RpcError::from_meta_status)?;
2441                let resp = resp.into_inner();
2442                resp.members.into_iter().find(|member| member.is_leader)
2443            }
2444            Either::Right(member_group) => {
2445                let mut fetched_members = None;
2446
2447                for (addr, client) in &mut member_group.members {
2448                    let members: Result<_> = try {
2449                        let mut client = match client {
2450                            Some(cached_client) => cached_client.to_owned(),
2451                            None => {
2452                                let endpoint = GrpcMetaClient::addr_to_endpoint(addr.clone());
2453                                let channel = GrpcMetaClient::connect_to_endpoint(endpoint)
2454                                    .await
2455                                    .context("failed to create client")
2456                                    .map_err(RpcError::from)?;
2457                                let new_client: MetaMemberClient =
2458                                    MetaMemberServiceClient::new(channel);
2459                                *client = Some(new_client.clone());
2460
2461                                new_client
2462                            }
2463                        };
2464
2465                        let resp = client
2466                            .members(MembersRequest {})
2467                            .await
2468                            .context("failed to fetch members")
2469                            .map_err(RpcError::from)?;
2470
2471                        resp.into_inner().members
2472                    };
2473
2474                    let fetched = members.is_ok();
2475                    fetched_members = Some(members);
2476                    if fetched {
2477                        break;
2478                    }
2479                }
2480
2481                let members = fetched_members
2482                    .context("no member available in the list")?
2483                    .context("could not refresh members")?;
2484
2485                // find new leader
2486                let mut leader = None;
2487                for member in members {
2488                    if member.is_leader {
2489                        leader = Some(member.clone());
2490                    }
2491
2492                    let addr = Self::host_address_to_uri(member.address.unwrap());
2493                    // We don't clean any expired addrs here to deal with some extreme situations.
2494                    if !member_group.members.contains(&addr) {
2495                        tracing::info!("new meta member joined: {}", addr);
2496                        member_group.members.put(addr, None);
2497                    }
2498                }
2499
2500                leader
2501            }
2502        };
2503
2504        if let Some(leader) = leader_addr {
2505            let discovered_leader = Self::host_address_to_uri(leader.address.unwrap());
2506
2507            if discovered_leader != self.current_leader {
2508                tracing::info!("new meta leader {} discovered", discovered_leader);
2509
2510                let retry_strategy = GrpcMetaClient::retry_strategy_to_bound(
2511                    Duration::from_secs(self.meta_config.meta_leader_lease_secs),
2512                    false,
2513                );
2514
2515                let channel = tokio_retry::Retry::spawn(retry_strategy, || async {
2516                    let endpoint = GrpcMetaClient::addr_to_endpoint(discovered_leader.clone());
2517                    GrpcMetaClient::connect_to_endpoint(endpoint).await
2518                })
2519                .await?;
2520
2521                self.recreate_core(channel).await;
2522                self.current_leader = discovered_leader;
2523            }
2524        }
2525
2526        Ok(())
2527    }
2528}
2529
2530impl GrpcMetaClient {
2531    // See `Endpoint::http2_keep_alive_interval`
2532    const ENDPOINT_KEEP_ALIVE_INTERVAL_SEC: u64 = 60;
2533    // See `Endpoint::keep_alive_timeout`
2534    const ENDPOINT_KEEP_ALIVE_TIMEOUT_SEC: u64 = 60;
2535    // Retry base interval in ms for connecting to meta server.
2536    const INIT_RETRY_BASE_INTERVAL_MS: u64 = 10;
2537    // Max retry times for connecting to meta server.
2538    const INIT_RETRY_MAX_INTERVAL_MS: u64 = 2000;
2539
2540    fn start_meta_member_monitor(
2541        &self,
2542        init_leader_addr: http::Uri,
2543        members: Either<MetaMemberClient, MetaMemberGroup>,
2544        force_refresh_receiver: Receiver<Sender<Result<()>>>,
2545        meta_config: Arc<MetaConfig>,
2546    ) -> Result<()> {
2547        let core_ref: Arc<RwLock<GrpcMetaClientCore>> = self.core.clone();
2548        let current_leader = init_leader_addr;
2549
2550        let enable_period_tick = matches!(members, Either::Right(_));
2551
2552        let member_management = MetaMemberManagement {
2553            core_ref,
2554            members,
2555            current_leader,
2556            meta_config,
2557        };
2558
2559        let mut force_refresh_receiver = force_refresh_receiver;
2560
2561        tokio::spawn(async move {
2562            let mut member_management = member_management;
2563            let mut ticker = time::interval(MetaMemberManagement::META_MEMBER_REFRESH_PERIOD);
2564
2565            loop {
2566                let event: Option<Sender<Result<()>>> = if enable_period_tick {
2567                    tokio::select! {
2568                        _ = ticker.tick() => None,
2569                        result_sender = force_refresh_receiver.recv() => {
2570                            if result_sender.is_none() {
2571                                break;
2572                            }
2573
2574                            result_sender
2575                        },
2576                    }
2577                } else {
2578                    let result_sender = force_refresh_receiver.recv().await;
2579
2580                    if result_sender.is_none() {
2581                        break;
2582                    }
2583
2584                    result_sender
2585                };
2586
2587                let tick_result = member_management.refresh_members().await;
2588                if let Err(e) = tick_result.as_ref() {
2589                    tracing::warn!(error = %e.as_report(),  "refresh meta member client failed");
2590                }
2591
2592                if let Some(sender) = event {
2593                    // ignore resp
2594                    let _resp = sender.send(tick_result);
2595                }
2596            }
2597        });
2598
2599        Ok(())
2600    }
2601
2602    async fn force_refresh_leader(&self) -> Result<()> {
2603        let (sender, receiver) = oneshot::channel();
2604
2605        self.member_monitor_event_sender
2606            .send(sender)
2607            .await
2608            .map_err(|e| anyhow!(e))?;
2609
2610        receiver.await.map_err(|e| anyhow!(e))?
2611    }
2612
2613    /// Connect to the meta server from `addrs`.
2614    pub async fn new(strategy: &MetaAddressStrategy, config: Arc<MetaConfig>) -> Result<Self> {
2615        let (channel, addr) = match strategy {
2616            MetaAddressStrategy::LoadBalance(addr) => {
2617                Self::try_build_rpc_channel(vec![addr.clone()]).await
2618            }
2619            MetaAddressStrategy::List(addrs) => Self::try_build_rpc_channel(addrs.clone()).await,
2620        }?;
2621        let (force_refresh_sender, force_refresh_receiver) = mpsc::channel(1);
2622        let client = GrpcMetaClient {
2623            member_monitor_event_sender: force_refresh_sender,
2624            core: Arc::new(RwLock::new(GrpcMetaClientCore::new(channel))),
2625        };
2626
2627        let meta_member_client = client.core.read().await.meta_member_client.clone();
2628        let members = match strategy {
2629            MetaAddressStrategy::LoadBalance(_) => Either::Left(meta_member_client),
2630            MetaAddressStrategy::List(addrs) => {
2631                let mut members = LruCache::new(20.try_into().unwrap());
2632                for addr in addrs {
2633                    members.put(addr.clone(), None);
2634                }
2635                members.put(addr.clone(), Some(meta_member_client));
2636
2637                Either::Right(MetaMemberGroup { members })
2638            }
2639        };
2640
2641        client.start_meta_member_monitor(addr, members, force_refresh_receiver, config.clone())?;
2642
2643        client.force_refresh_leader().await?;
2644
2645        Ok(client)
2646    }
2647
2648    fn addr_to_endpoint(addr: http::Uri) -> Endpoint {
2649        Endpoint::from(addr).initial_connection_window_size(MAX_CONNECTION_WINDOW_SIZE)
2650    }
2651
2652    pub(crate) async fn try_build_rpc_channel(
2653        addrs: impl IntoIterator<Item = http::Uri>,
2654    ) -> Result<(Channel, http::Uri)> {
2655        let endpoints: Vec<_> = addrs
2656            .into_iter()
2657            .map(|addr| (Self::addr_to_endpoint(addr.clone()), addr))
2658            .collect();
2659
2660        let mut last_error = None;
2661
2662        for (endpoint, addr) in endpoints {
2663            match Self::connect_to_endpoint(endpoint).await {
2664                Ok(channel) => {
2665                    tracing::info!("Connect to meta server {} successfully", addr);
2666                    return Ok((channel, addr));
2667                }
2668                Err(e) => {
2669                    tracing::warn!(
2670                        error = %e.as_report(),
2671                        "Failed to connect to meta server {}, trying again",
2672                        addr,
2673                    );
2674                    last_error = Some(e);
2675                }
2676            }
2677        }
2678
2679        if let Some(last_error) = last_error {
2680            Err(anyhow::anyhow!(last_error)
2681                .context("failed to connect to all meta servers")
2682                .into())
2683        } else {
2684            bail!("no meta server address provided")
2685        }
2686    }
2687
2688    async fn connect_to_endpoint(endpoint: Endpoint) -> Result<Channel> {
2689        let channel = endpoint
2690            .http2_keep_alive_interval(Duration::from_secs(Self::ENDPOINT_KEEP_ALIVE_INTERVAL_SEC))
2691            .keep_alive_timeout(Duration::from_secs(Self::ENDPOINT_KEEP_ALIVE_TIMEOUT_SEC))
2692            .connect_timeout(Duration::from_secs(5))
2693            .monitored_connect("grpc-meta-client", Default::default())
2694            .await?
2695            .wrapped();
2696
2697        Ok(channel)
2698    }
2699
2700    pub(crate) fn retry_strategy_to_bound(
2701        high_bound: Duration,
2702        exceed: bool,
2703    ) -> impl Iterator<Item = Duration> {
2704        let iter = exponential_backoff(
2705            Duration::from_millis(Self::INIT_RETRY_BASE_INTERVAL_MS),
2706            Self::INIT_RETRY_BASE_INTERVAL_MS,
2707            Duration::from_millis(Self::INIT_RETRY_MAX_INTERVAL_MS),
2708        )
2709        .map(jitter);
2710
2711        let mut sum = Duration::default();
2712
2713        iter.take_while(move |duration| {
2714            sum += *duration;
2715
2716            if exceed {
2717                sum < high_bound + *duration
2718            } else {
2719                sum < high_bound
2720            }
2721        })
2722    }
2723}
2724
2725macro_rules! for_all_meta_rpc {
2726    ($macro:ident) => {
2727        $macro! {
2728             { cluster_client, add_worker_node, AddWorkerNodeRequest, AddWorkerNodeResponse }
2729            ,{ cluster_client, activate_worker_node, ActivateWorkerNodeRequest, ActivateWorkerNodeResponse }
2730            ,{ cluster_client, delete_worker_node, DeleteWorkerNodeRequest, DeleteWorkerNodeResponse }
2731            ,{ cluster_client, list_all_nodes, ListAllNodesRequest, ListAllNodesResponse }
2732            ,{ cluster_client, get_cluster_recovery_status, GetClusterRecoveryStatusRequest, GetClusterRecoveryStatusResponse }
2733            ,{ cluster_client, get_meta_store_info, GetMetaStoreInfoRequest, GetMetaStoreInfoResponse }
2734            ,{ heartbeat_client, heartbeat, HeartbeatRequest, HeartbeatResponse }
2735            ,{ stream_client, flush, FlushRequest, FlushResponse }
2736            ,{ stream_client, pause, PauseRequest, PauseResponse }
2737            ,{ stream_client, resume, ResumeRequest, ResumeResponse }
2738            ,{ stream_client, apply_throttle, ApplyThrottleRequest, ApplyThrottleResponse }
2739            ,{ stream_client, cancel_creating_jobs, CancelCreatingJobsRequest, CancelCreatingJobsResponse }
2740            ,{ stream_client, list_table_fragments, ListTableFragmentsRequest, ListTableFragmentsResponse }
2741            ,{ stream_client, list_streaming_job_states, ListStreamingJobStatesRequest, ListStreamingJobStatesResponse }
2742            ,{ stream_client, list_fragment_distribution, ListFragmentDistributionRequest, ListFragmentDistributionResponse }
2743            ,{ stream_client, list_creating_fragment_distribution, ListCreatingFragmentDistributionRequest, ListCreatingFragmentDistributionResponse }
2744            ,{ stream_client, list_sink_log_store_tables, ListSinkLogStoreTablesRequest, ListSinkLogStoreTablesResponse }
2745            ,{ stream_client, list_actor_states, ListActorStatesRequest, ListActorStatesResponse }
2746            ,{ stream_client, list_actor_splits, ListActorSplitsRequest, ListActorSplitsResponse }
2747            ,{ stream_client, recover, RecoverRequest, RecoverResponse }
2748            ,{ stream_client, list_rate_limits, ListRateLimitsRequest, ListRateLimitsResponse }
2749            ,{ stream_client, list_cdc_progress, ListCdcProgressRequest, ListCdcProgressResponse }
2750            ,{ stream_client, list_refresh_table_states, ListRefreshTableStatesRequest, ListRefreshTableStatesResponse }
2751            ,{ stream_client, list_iceberg_compaction_status, ListIcebergCompactionStatusRequest, ListIcebergCompactionStatusResponse }
2752            ,{ stream_client, alter_connector_props, AlterConnectorPropsRequest, AlterConnectorPropsResponse }
2753            ,{ stream_client, alter_source_properties_safe, AlterSourcePropertiesSafeRequest, AlterSourcePropertiesSafeResponse }
2754            ,{ stream_client, reset_source_splits, ResetSourceSplitsRequest, ResetSourceSplitsResponse }
2755            ,{ stream_client, inject_source_offsets, InjectSourceOffsetsRequest, InjectSourceOffsetsResponse }
2756            ,{ stream_client, get_fragment_by_id, GetFragmentByIdRequest, GetFragmentByIdResponse }
2757            ,{ stream_client, get_fragment_vnodes, GetFragmentVnodesRequest, GetFragmentVnodesResponse }
2758            ,{ stream_client, get_actor_vnodes, GetActorVnodesRequest, GetActorVnodesResponse }
2759            ,{ stream_client, set_sync_log_store_aligned, SetSyncLogStoreAlignedRequest, SetSyncLogStoreAlignedResponse }
2760            ,{ stream_client, refresh, RefreshRequest, RefreshResponse }
2761            ,{ stream_client, list_unmigrated_tables, ListUnmigratedTablesRequest, ListUnmigratedTablesResponse }
2762            ,{ ddl_client, create_table, CreateTableRequest, CreateTableResponse }
2763            ,{ ddl_client, alter_name, AlterNameRequest, AlterNameResponse }
2764            ,{ ddl_client, alter_owner, AlterOwnerRequest, AlterOwnerResponse }
2765            ,{ ddl_client, alter_subscription_retention, AlterSubscriptionRetentionRequest, AlterSubscriptionRetentionResponse }
2766            ,{ ddl_client, alter_set_schema, AlterSetSchemaRequest, AlterSetSchemaResponse }
2767            ,{ ddl_client, alter_parallelism, AlterParallelismRequest, AlterParallelismResponse }
2768            ,{ ddl_client, alter_backfill_parallelism, AlterBackfillParallelismRequest, AlterBackfillParallelismResponse }
2769            ,{ ddl_client, alter_streaming_job_config, AlterStreamingJobConfigRequest, AlterStreamingJobConfigResponse }
2770            ,{ ddl_client, alter_fragment_parallelism, AlterFragmentParallelismRequest, AlterFragmentParallelismResponse }
2771            ,{ ddl_client, alter_cdc_table_backfill_parallelism, AlterCdcTableBackfillParallelismRequest, AlterCdcTableBackfillParallelismResponse }
2772            ,{ ddl_client, alter_resource_group, AlterResourceGroupRequest, AlterResourceGroupResponse }
2773            ,{ ddl_client, alter_database_resource_group, AlterDatabaseResourceGroupRequest, AlterDatabaseResourceGroupResponse }
2774            ,{ ddl_client, alter_database_param, AlterDatabaseParamRequest, AlterDatabaseParamResponse }
2775            ,{ ddl_client, create_materialized_view, CreateMaterializedViewRequest, CreateMaterializedViewResponse }
2776            ,{ ddl_client, create_view, CreateViewRequest, CreateViewResponse }
2777            ,{ ddl_client, create_source, CreateSourceRequest, CreateSourceResponse }
2778            ,{ ddl_client, create_sink, CreateSinkRequest, CreateSinkResponse }
2779            ,{ ddl_client, create_subscription, CreateSubscriptionRequest, CreateSubscriptionResponse }
2780            ,{ ddl_client, create_schema, CreateSchemaRequest, CreateSchemaResponse }
2781            ,{ ddl_client, create_database, CreateDatabaseRequest, CreateDatabaseResponse }
2782            ,{ ddl_client, create_secret, CreateSecretRequest, CreateSecretResponse }
2783            ,{ ddl_client, create_index, CreateIndexRequest, CreateIndexResponse }
2784            ,{ ddl_client, create_function, CreateFunctionRequest, CreateFunctionResponse }
2785            ,{ ddl_client, drop_table, DropTableRequest, DropTableResponse }
2786            ,{ ddl_client, drop_materialized_view, DropMaterializedViewRequest, DropMaterializedViewResponse }
2787            ,{ ddl_client, drop_view, DropViewRequest, DropViewResponse }
2788            ,{ ddl_client, drop_source, DropSourceRequest, DropSourceResponse }
2789            ,{ ddl_client, reset_source, ResetSourceRequest, ResetSourceResponse }
2790            ,{ ddl_client, drop_secret, DropSecretRequest, DropSecretResponse}
2791            ,{ ddl_client, drop_sink, DropSinkRequest, DropSinkResponse }
2792            ,{ ddl_client, drop_subscription, DropSubscriptionRequest, DropSubscriptionResponse }
2793            ,{ ddl_client, drop_database, DropDatabaseRequest, DropDatabaseResponse }
2794            ,{ ddl_client, drop_schema, DropSchemaRequest, DropSchemaResponse }
2795            ,{ ddl_client, drop_index, DropIndexRequest, DropIndexResponse }
2796            ,{ ddl_client, drop_function, DropFunctionRequest, DropFunctionResponse }
2797            ,{ ddl_client, replace_job_plan, ReplaceJobPlanRequest, ReplaceJobPlanResponse }
2798            ,{ ddl_client, alter_source, AlterSourceRequest, AlterSourceResponse }
2799            ,{ ddl_client, risectl_list_state_tables, RisectlListStateTablesRequest, RisectlListStateTablesResponse }
2800            ,{ ddl_client, risectl_resume_backfill, RisectlResumeBackfillRequest, RisectlResumeBackfillResponse }
2801            ,{ ddl_client, get_ddl_progress, GetDdlProgressRequest, GetDdlProgressResponse }
2802            ,{ ddl_client, create_connection, CreateConnectionRequest, CreateConnectionResponse }
2803            ,{ ddl_client, list_connections, ListConnectionsRequest, ListConnectionsResponse }
2804            ,{ ddl_client, drop_connection, DropConnectionRequest, DropConnectionResponse }
2805            ,{ ddl_client, comment_on, CommentOnRequest, CommentOnResponse }
2806            ,{ ddl_client, get_tables, GetTablesRequest, GetTablesResponse }
2807            ,{ ddl_client, wait, WaitRequest, WaitResponse }
2808            ,{ ddl_client, auto_schema_change, AutoSchemaChangeRequest, AutoSchemaChangeResponse }
2809            ,{ ddl_client, alter_swap_rename, AlterSwapRenameRequest, AlterSwapRenameResponse }
2810            ,{ ddl_client, alter_secret, AlterSecretRequest, AlterSecretResponse }
2811            ,{ ddl_client, compact_iceberg_table, CompactIcebergTableRequest, CompactIcebergTableResponse }
2812            ,{ ddl_client, rewrite_iceberg_table_manifests, RewriteIcebergTableManifestsRequest, RewriteIcebergTableManifestsResponse }
2813            ,{ ddl_client, expire_iceberg_table_snapshots, ExpireIcebergTableSnapshotsRequest, ExpireIcebergTableSnapshotsResponse }
2814            ,{ ddl_client, remove_iceberg_table_orphan_files, RemoveIcebergTableOrphanFilesRequest, RemoveIcebergTableOrphanFilesResponse }
2815            ,{ ddl_client, create_iceberg_table, CreateIcebergTableRequest, CreateIcebergTableResponse }
2816            ,{ ddl_client, wait_iceberg_pk_index_sink_epoch, WaitIcebergPkIndexSinkEpochRequest, WaitIcebergPkIndexSinkEpochResponse }
2817            ,{ hummock_client, unpin_version_before, UnpinVersionBeforeRequest, UnpinVersionBeforeResponse }
2818            ,{ hummock_client, get_current_version, GetCurrentVersionRequest, GetCurrentVersionResponse }
2819            ,{ hummock_client, replay_version_delta, ReplayVersionDeltaRequest, ReplayVersionDeltaResponse }
2820            ,{ hummock_client, list_version_deltas, ListVersionDeltasRequest, ListVersionDeltasResponse }
2821            ,{ hummock_client, get_assigned_compact_task_num, GetAssignedCompactTaskNumRequest, GetAssignedCompactTaskNumResponse }
2822            ,{ hummock_client, trigger_compaction_deterministic, TriggerCompactionDeterministicRequest, TriggerCompactionDeterministicResponse }
2823            ,{ hummock_client, disable_commit_epoch, DisableCommitEpochRequest, DisableCommitEpochResponse }
2824            ,{ hummock_client, get_new_object_ids, GetNewObjectIdsRequest, GetNewObjectIdsResponse }
2825            ,{ hummock_client, trigger_manual_compaction, TriggerManualCompactionRequest, TriggerManualCompactionResponse }
2826            ,{ hummock_client, trigger_full_gc, TriggerFullGcRequest, TriggerFullGcResponse }
2827            ,{ hummock_client, rise_ctl_get_pinned_versions_summary, RiseCtlGetPinnedVersionsSummaryRequest, RiseCtlGetPinnedVersionsSummaryResponse }
2828            ,{ hummock_client, rise_ctl_list_compaction_group, RiseCtlListCompactionGroupRequest, RiseCtlListCompactionGroupResponse }
2829            ,{ hummock_client, rise_ctl_update_compaction_config, RiseCtlUpdateCompactionConfigRequest, RiseCtlUpdateCompactionConfigResponse }
2830            ,{ hummock_client, rise_ctl_get_checkpoint_version, RiseCtlGetCheckpointVersionRequest, RiseCtlGetCheckpointVersionResponse }
2831            ,{ hummock_client, rise_ctl_pause_version_checkpoint, RiseCtlPauseVersionCheckpointRequest, RiseCtlPauseVersionCheckpointResponse }
2832            ,{ hummock_client, rise_ctl_resume_version_checkpoint, RiseCtlResumeVersionCheckpointRequest, RiseCtlResumeVersionCheckpointResponse }
2833            ,{ hummock_client, init_metadata_for_replay, InitMetadataForReplayRequest, InitMetadataForReplayResponse }
2834            ,{ hummock_client, split_compaction_group, SplitCompactionGroupRequest, SplitCompactionGroupResponse }
2835            ,{ hummock_client, rise_ctl_list_compaction_status, RiseCtlListCompactionStatusRequest, RiseCtlListCompactionStatusResponse }
2836            ,{ hummock_client, get_compaction_score, GetCompactionScoreRequest, GetCompactionScoreResponse }
2837            ,{ hummock_client, rise_ctl_rebuild_table_stats, RiseCtlRebuildTableStatsRequest, RiseCtlRebuildTableStatsResponse }
2838            ,{ hummock_client, subscribe_compaction_event, impl tonic::IntoStreamingRequest<Message = SubscribeCompactionEventRequest>, Streaming<SubscribeCompactionEventResponse> }
2839            ,{ hummock_client, subscribe_iceberg_compaction_event, impl tonic::IntoStreamingRequest<Message = SubscribeIcebergCompactionEventRequest>, Streaming<SubscribeIcebergCompactionEventResponse> }
2840            ,{ hummock_client, list_branched_object, ListBranchedObjectRequest, ListBranchedObjectResponse }
2841            ,{ hummock_client, list_active_write_limit, ListActiveWriteLimitRequest, ListActiveWriteLimitResponse }
2842            ,{ hummock_client, list_hummock_meta_config, ListHummockMetaConfigRequest, ListHummockMetaConfigResponse }
2843            ,{ hummock_client, list_compact_task_assignment, ListCompactTaskAssignmentRequest, ListCompactTaskAssignmentResponse }
2844            ,{ hummock_client, list_compact_task_progress, ListCompactTaskProgressRequest, ListCompactTaskProgressResponse }
2845            ,{ hummock_client, cancel_compact_task, CancelCompactTaskRequest, CancelCompactTaskResponse}
2846            ,{ hummock_client, get_version_by_epoch, GetVersionByEpochRequest, GetVersionByEpochResponse }
2847            ,{ hummock_client, merge_compaction_group, MergeCompactionGroupRequest, MergeCompactionGroupResponse }
2848            ,{ hummock_client, get_table_change_logs, GetTableChangeLogsRequest, GetTableChangeLogsResponse }
2849            ,{ user_client, create_user, CreateUserRequest, CreateUserResponse }
2850            ,{ user_client, update_user, UpdateUserRequest, UpdateUserResponse }
2851            ,{ user_client, drop_user, DropUserRequest, DropUserResponse }
2852            ,{ user_client, grant_privilege, GrantPrivilegeRequest, GrantPrivilegeResponse }
2853            ,{ user_client, revoke_privilege, RevokePrivilegeRequest, RevokePrivilegeResponse }
2854            ,{ user_client, alter_default_privilege, AlterDefaultPrivilegeRequest, AlterDefaultPrivilegeResponse }
2855            ,{ scale_client, get_cluster_info, GetClusterInfoRequest, GetClusterInfoResponse }
2856            ,{ scale_client, reschedule, RescheduleRequest, RescheduleResponse }
2857            ,{ notification_client, subscribe, SubscribeRequest, Streaming<SubscribeResponse> }
2858            ,{ backup_client, backup_meta, BackupMetaRequest, BackupMetaResponse }
2859            ,{ backup_client, get_backup_job_status, GetBackupJobStatusRequest, GetBackupJobStatusResponse }
2860            ,{ backup_client, delete_meta_snapshot, DeleteMetaSnapshotRequest, DeleteMetaSnapshotResponse}
2861            ,{ backup_client, get_meta_snapshot_manifest, GetMetaSnapshotManifestRequest, GetMetaSnapshotManifestResponse}
2862            ,{ telemetry_client, get_telemetry_info, GetTelemetryInfoRequest, TelemetryInfoResponse}
2863            ,{ system_params_client, get_system_params, GetSystemParamsRequest, GetSystemParamsResponse }
2864            ,{ system_params_client, set_system_param, SetSystemParamRequest, SetSystemParamResponse }
2865            ,{ system_params_client, clear_file_cache, ClearFileCacheRequest, ClearFileCacheResponse }
2866            ,{ session_params_client, get_session_params, GetSessionParamsRequest, GetSessionParamsResponse }
2867            ,{ session_params_client, set_session_param, SetSessionParamRequest, SetSessionParamResponse }
2868            ,{ serving_client, get_serving_vnode_mappings, GetServingVnodeMappingsRequest, GetServingVnodeMappingsResponse }
2869            ,{ serving_client, warm_up_table_cache, WarmUpTableCacheRequest, WarmUpTableCacheResponse }
2870            ,{ cloud_client, rw_cloud_validate_source, RwCloudValidateSourceRequest, RwCloudValidateSourceResponse }
2871            ,{ event_log_client, list_event_log, ListEventLogRequest, ListEventLogResponse }
2872            ,{ event_log_client, add_event_log, AddEventLogRequest, AddEventLogResponse }
2873            ,{ cluster_limit_client, get_cluster_limits, GetClusterLimitsRequest, GetClusterLimitsResponse }
2874            ,{ hosted_iceberg_catalog_service_client, list_iceberg_tables, ListIcebergTablesRequest, ListIcebergTablesResponse }
2875            ,{ monitor_client, stack_trace, StackTraceRequest, StackTraceResponse }
2876        }
2877    };
2878}
2879
2880impl GrpcMetaClient {
2881    async fn refresh_client_if_needed(&self, code: Code) {
2882        if matches!(
2883            code,
2884            Code::Unknown | Code::Unimplemented | Code::Unavailable
2885        ) {
2886            tracing::debug!("matching tonic code {}", code);
2887            let (result_sender, result_receiver) = oneshot::channel();
2888            if self
2889                .member_monitor_event_sender
2890                .try_send(result_sender)
2891                .is_ok()
2892            {
2893                if let Ok(Err(e)) = result_receiver.await {
2894                    tracing::warn!(error = %e.as_report(), "force refresh meta client failed");
2895                }
2896            } else {
2897                tracing::debug!("skipping the current refresh, somewhere else is already doing it")
2898            }
2899        }
2900    }
2901}
2902
2903impl GrpcMetaClient {
2904    for_all_meta_rpc! { meta_rpc_client_method_impl }
2905}