Skip to main content

rocketmq_client_rust/admin/default_mq_admin_ext_impl/
admin_api.rs

1// Copyright 2023 The RocketMQ Rust Authors
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 super::broker::*;
16use super::group::*;
17use super::security::*;
18use super::topic::*;
19use super::*;
20
21#[allow(unused_variables, reason = "trait contracts reserve parameters")]
22#[allow(unused_mut)]
23impl RouteAdmin for DefaultMQAdminExtImpl {
24    async fn examine_broker_cluster_info(&self) -> crate::ClientResult<ClusterInfo> {
25        self.mq_client_api()?
26            .get_broker_cluster_info(self.remoting_timeout_millis()?)
27            .await
28    }
29
30    async fn get_name_server_address_list(&self) -> Vec<CheetahString> {
31        self.client_instance
32            .as_ref()
33            .map(|client_instance| {
34                client_instance
35                    .get_mq_client_api_impl()
36                    .map(|mq_client_api| mq_client_api.get_name_server_address_list().to_vec())
37                    .unwrap_or_default()
38            })
39            .unwrap_or_default()
40    }
41
42    async fn put_kv_config(&self, namespace: CheetahString, key: CheetahString, value: CheetahString) {
43        if let Err(error) = self.create_and_update_kv_config(namespace, key, value).await {
44            warn!("put_kv_config failed: {}", error);
45        }
46    }
47
48    async fn get_kv_config(&self, namespace: CheetahString, key: CheetahString) -> crate::ClientResult<CheetahString> {
49        Ok(self
50            .client_instance
51            .as_ref()
52            .ok_or(crate::ClientError::not_started())?
53            .get_mq_client_api_impl()?
54            .get_kvconfig_value(namespace, key, self.remoting_timeout_millis()?)
55            .await?
56            .unwrap_or_default())
57    }
58
59    async fn get_kv_list_by_namespace(&self, namespace: CheetahString) -> crate::ClientResult<KVTable> {
60        self.mq_client_api()?
61            .get_kvlist_by_namespace(namespace, self.remoting_timeout_millis()?)
62            .await
63    }
64
65    async fn create_and_update_kv_config(
66        &self,
67        namespace: CheetahString,
68        key: CheetahString,
69        value: CheetahString,
70    ) -> crate::ClientResult<()> {
71        self.mq_client_api()?
72            .put_kvconfig_value(namespace, key, value, self.remoting_timeout_millis()?)
73            .await
74    }
75
76    async fn delete_kv_config(&self, namespace: CheetahString, key: CheetahString) -> crate::ClientResult<()> {
77        self.mq_client_api()?
78            .delete_kvconfig_value(namespace, key, self.remoting_timeout_millis()?)
79            .await
80    }
81
82    async fn get_cluster_list(&self, topic: String) -> crate::ClientResult<HashSet<CheetahString>> {
83        self.get_topic_cluster_list(topic).await
84    }
85
86    async fn update_name_server_config(
87        &self,
88        properties: HashMap<CheetahString, CheetahString>,
89        name_servers: Option<Vec<CheetahString>>,
90    ) -> crate::ClientResult<()> {
91        self.mq_client_api()?
92            .update_name_server_config(properties, name_servers, self.remoting_timeout_millis()?)
93            .await
94    }
95
96    async fn get_name_server_config(
97        &self,
98        name_servers: Vec<CheetahString>,
99    ) -> crate::ClientResult<HashMap<CheetahString, HashMap<CheetahString, CheetahString>>> {
100        Ok(self
101            .mq_client_api()?
102            .get_name_server_config(Some(name_servers), self.timeout_millis)
103            .await?
104            .unwrap_or_default())
105    }
106
107    async fn probe_name_server(&self, name_server: CheetahString) -> crate::ClientResult<()> {
108        self.client_instance
109            .as_ref()
110            .ok_or(crate::ClientError::not_started())?
111            .get_mq_client_api_impl()?
112            .probe_name_server(&name_server, self.timeout_millis)
113            .await
114    }
115}
116
117#[allow(unused_variables, reason = "trait contracts reserve parameters")]
118#[allow(unused_mut)]
119impl TopicAdmin for DefaultMQAdminExtImpl {
120    async fn create_and_update_topic_config(
121        &self,
122        addr: CheetahString,
123        config: TopicConfig,
124    ) -> crate::ClientResult<()> {
125        let topic = config
126            .topic_name
127            .clone()
128            .ok_or_else(|| crate::ClientError::illegal_argument("Topic name is required"))?;
129        let attributes = encode_topic_attributes(&config.attributes);
130        let request_header = CreateTopicRequestHeader {
131            topic,
132            default_topic: CheetahString::from_static_str(TopicValidator::AUTO_CREATE_TOPIC_KEY_TOPIC),
133            read_queue_nums: config.read_queue_nums as i32,
134            write_queue_nums: config.write_queue_nums as i32,
135            perm: config.perm as i32,
136            topic_filter_type: CheetahString::from_static_str(config.topic_filter_type.as_str()),
137            topic_sys_flag: Some(config.topic_sys_flag as i32),
138            order: config.order,
139            attributes,
140            force: Some(false),
141            topic_request_header: None,
142        };
143
144        self.mq_client_api()?
145            .update_or_create_topic(&addr, request_header, self.remoting_timeout_millis()?)
146            .await
147    }
148
149    async fn create_and_update_topic_config_list(
150        &self,
151        addr: CheetahString,
152        topic_config_list: Vec<TopicConfig>,
153    ) -> crate::ClientResult<()> {
154        for config in topic_config_list {
155            self.create_and_update_topic_config(addr.clone(), config).await?;
156        }
157        Ok(())
158    }
159
160    async fn examine_topic_stats(
161        &self,
162        topic: CheetahString,
163        broker_addr: Option<CheetahString>,
164    ) -> crate::ClientResult<TopicStatsTable> {
165        let timeout = self.remoting_timeout_millis()?;
166        let request_header = GetTopicStatsInfoRequestHeader {
167            topic: topic.clone(),
168            topic_request_header: None,
169        };
170        if let Some(addr) = broker_addr {
171            return self
172                .mq_client_api()?
173                .get_topic_stats_info(&addr, request_header, timeout)
174                .await;
175        }
176
177        let topic_route = self.examine_topic_route_info(topic).await?;
178        let mut result = TopicStatsTable::new();
179        if let Some(route_data) = topic_route {
180            for broker_data in &route_data.broker_datas {
181                if let Some(master_addr) = broker_data.broker_addrs().get(&mix_all::MASTER_ID) {
182                    let stats = self
183                        .mq_client_api()?
184                        .get_topic_stats_info(master_addr, request_header.clone(), timeout)
185                        .await?;
186                    result.get_offset_table_mut().extend(stats.into_offset_table());
187                }
188            }
189        }
190
191        Ok(result)
192    }
193
194    async fn examine_topic_stats_concurrent(&self, topic: CheetahString) -> AdminToolResult<TopicStatsTable> {
195        match self.examine_topic_stats(topic, None).await {
196            Ok(stats) => AdminToolResult::success(stats),
197            Err(error) => AdminToolResult::failure(
198                crate::common::admin_tools_result_code_enum::AdminToolsResultCodeEnum::RemotingError,
199                error.to_string(),
200            ),
201        }
202    }
203
204    async fn fetch_all_topic_list(&self) -> crate::ClientResult<TopicList> {
205        self.mq_client_api()?
206            .get_all_topic_list_from_name_server(self.remoting_timeout_millis()?)
207            .await
208    }
209
210    async fn fetch_topics_by_cluster(&self, cluster_name: CheetahString) -> crate::ClientResult<TopicList> {
211        self.mq_client_api()?
212            .get_topics_by_cluster(cluster_name, self.remoting_timeout_millis()?)
213            .await
214    }
215
216    async fn examine_topic_route_info(&self, topic: CheetahString) -> crate::ClientResult<Option<TopicRouteData>> {
217        self.mq_client_api()?
218            .get_topic_route_info_from_name_server(&topic, self.remoting_timeout_millis()?)
219            .await
220    }
221
222    async fn delete_topic(&self, topic_name: CheetahString, cluster_name: CheetahString) -> crate::ClientResult<()> {
223        let cluster_info = self.examine_broker_cluster_info().await?;
224        let mut broker_addrs = HashSet::new();
225        if let Some(cluster_addr_table) = cluster_info.cluster_addr_table.as_ref() {
226            if let Some(broker_names) = cluster_addr_table.get(&cluster_name) {
227                if let Some(broker_addr_table) = cluster_info.broker_addr_table.as_ref() {
228                    for broker_name in broker_names {
229                        if let Some(broker_data) = broker_addr_table.get(broker_name) {
230                            broker_addrs.extend(broker_data.broker_addrs().values().cloned());
231                        }
232                    }
233                }
234            }
235        }
236        self.delete_topic_in_broker(broker_addrs, topic_name.clone()).await?;
237
238        let namesrv_addrs: HashSet<CheetahString> = self.get_name_server_address_list().await.into_iter().collect();
239        self.delete_topic_in_name_server(namesrv_addrs, Some(cluster_name), topic_name)
240            .await
241    }
242
243    async fn delete_topic_in_broker(
244        &self,
245        addrs: HashSet<CheetahString>,
246        topic: CheetahString,
247    ) -> crate::ClientResult<()> {
248        let request_header = DeleteTopicRequestHeader {
249            topic: topic.clone(),
250            topic_request_header: None,
251        };
252        let api = self.mq_client_api()?;
253        let timeout = self.remoting_timeout_millis()?;
254        for addr in addrs {
255            api.delete_topic_in_broker(
256                &addr,
257                DeleteTopicRequestHeader {
258                    topic: request_header.topic.clone(),
259                    topic_request_header: None,
260                },
261                timeout,
262            )
263            .await?;
264        }
265        Ok(())
266    }
267
268    async fn delete_topic_in_broker_list(
269        &self,
270        addrs: HashSet<CheetahString>,
271        topics: Vec<CheetahString>,
272    ) -> crate::ClientResult<()> {
273        let api = self.mq_client_api()?;
274        let timeout = self.remoting_timeout_millis()?;
275        for addr in addrs {
276            api.delete_topic_in_broker_list(&addr, topics.clone(), timeout).await?;
277        }
278        Ok(())
279    }
280
281    async fn delete_topic_in_name_server(
282        &self,
283        addrs: HashSet<CheetahString>,
284        cluster_name: Option<CheetahString>,
285        topic: CheetahString,
286    ) -> crate::ClientResult<()> {
287        let request_header = DeleteTopicFromNamesrvRequestHeader::new(topic, cluster_name);
288        let api = self.mq_client_api()?;
289        let timeout = self.remoting_timeout_millis()?;
290        for addr in addrs {
291            api.delete_topic_in_nameserver(&addr, request_header.clone(), timeout)
292                .await?;
293        }
294        Ok(())
295    }
296
297    async fn create_or_update_order_conf(
298        &self,
299        key: CheetahString,
300        value: CheetahString,
301        is_cluster: bool,
302    ) -> crate::ClientResult<()> {
303        if is_cluster {
304            return self
305                .mq_client_api()?
306                .put_kvconfig_value(
307                    CheetahString::from_static_str(NAMESPACE_ORDER_TOPIC_CONFIG),
308                    key,
309                    value,
310                    self.remoting_timeout_millis()?,
311                )
312                .await;
313        }
314
315        let existing = self
316            .mq_client_api()?
317            .get_kvconfig_value(
318                CheetahString::from_static_str(NAMESPACE_ORDER_TOPIC_CONFIG),
319                key.clone(),
320                self.remoting_timeout_millis()?,
321            )
322            .await?
323            .unwrap_or_default();
324
325        let merged_order_conf = merge_order_conf_entries(existing.as_str(), value.as_str());
326
327        self.mq_client_api()?
328            .put_kvconfig_value(
329                CheetahString::from_static_str(NAMESPACE_ORDER_TOPIC_CONFIG),
330                key,
331                merged_order_conf.into(),
332                self.remoting_timeout_millis()?,
333            )
334            .await
335    }
336
337    async fn query_topic_consume_by_who(&self, topic: CheetahString) -> crate::ClientResult<GroupList> {
338        let topic_route = self
339            .mq_client_api()?
340            .get_topic_route_info_from_name_server(&topic, self.remoting_timeout_millis()?)
341            .await?;
342
343        if let Some(route_data) = topic_route {
344            for bd in &route_data.broker_datas {
345                if let Some(master_addr) = bd.broker_addrs().get(&rocketmq_model::common::mix_all::MASTER_ID) {
346                    let request_header = QueryTopicConsumeByWhoRequestHeader {
347                        topic: topic.clone(),
348                        topic_request_header: None,
349                    };
350                    return self
351                        .mq_client_api()?
352                        .query_topic_consume_by_who(master_addr, request_header, self.remoting_timeout_millis()?)
353                        .await;
354                }
355            }
356        }
357
358        Ok(GroupList::default())
359    }
360
361    async fn query_topics_by_consumer(&self, group: CheetahString) -> crate::ClientResult<TopicList> {
362        self.query_topics_by_consumer_from_route(group).await
363    }
364
365    async fn query_topics_by_consumer_concurrent(&self, group: CheetahString) -> AdminToolResult<TopicList> {
366        let timeout = match self.remoting_timeout_millis() {
367            Ok(timeout) => timeout,
368            Err(error) => return AdminToolResult::failure(admin_result_code_for_error(&error), error.to_string()),
369        };
370        let retry_topic: CheetahString = mix_all::get_retry_topic(&group).into();
371        let api = match self.mq_client_api() {
372            Ok(api) => api,
373            Err(error) => {
374                return AdminToolResult::failure(AdminToolsResultCodeEnum::MQClientError, error.to_string());
375            }
376        };
377        let topic_route = match api.get_topic_route_info_from_name_server(&retry_topic, timeout).await {
378            Ok(Some(route_data)) if !route_data.broker_datas.is_empty() => route_data,
379            Ok(_) => {
380                return AdminToolResult::failure(
381                    AdminToolsResultCodeEnum::TopicRouteInfoNotExist,
382                    "router info not found.".to_string(),
383                )
384            }
385            Err(error) => {
386                return AdminToolResult::failure(AdminToolsResultCodeEnum::MQClientError, error.to_string());
387            }
388        };
389
390        let mut result = TopicList::default();
391        for broker_data in &topic_route.broker_datas {
392            let Some(addr) = broker_data.select_broker_addr() else {
393                continue;
394            };
395            match api
396                .query_topics_by_consumer(&addr, QueryTopicsByConsumerRequestHeader::new(group.clone()), timeout)
397                .await
398            {
399                Ok(topic_list) => {
400                    for topic in topic_list.topic_list {
401                        if !result.topic_list.contains(&topic) {
402                            result.topic_list.push(topic);
403                        }
404                    }
405                }
406                Err(error) => {
407                    warn!("query_topics_by_consumer error. group={}, error={}", group, error);
408                }
409            }
410        }
411
412        AdminToolResult::success(result)
413    }
414
415    async fn clean_unused_topic(
416        &self,
417        cluster: Option<CheetahString>,
418        addr: Option<CheetahString>,
419    ) -> crate::ClientResult<bool> {
420        self.execute_broker_cleanup_operation(cluster, addr, BrokerCleanupOperation::CleanUnusedTopic)
421            .await
422    }
423
424    async fn clean_unused_topic_by_addr(&self, addr: CheetahString) -> crate::ClientResult<bool> {
425        self.clean_unused_topic(None, Some(addr)).await
426    }
427
428    async fn get_topic_cluster_list(&self, topic: String) -> crate::ClientResult<HashSet<CheetahString>> {
429        let cluster_info = self.examine_broker_cluster_info().await?;
430        let topic_route_data = self
431            .examine_topic_route_info(topic.clone().into())
432            .await?
433            .ok_or_else(|| mq_client_err!(format!("Topic route not found for: {topic}")))?;
434        let broker_data = topic_route_data
435            .broker_datas
436            .first()
437            .ok_or_else(|| mq_client_err!("Broker datas is empty"))?;
438        let mut cluster_set = HashSet::new();
439        let broker_name = broker_data.broker_name();
440        if let Some(cluster_addr_table) = cluster_info.cluster_addr_table.as_ref() {
441            cluster_set.extend(
442                cluster_addr_table
443                    .iter()
444                    .filter(|(cluster_name, broker_names)| broker_names.contains(broker_name))
445                    .map(|(cluster_name, broker_names)| cluster_name.clone()),
446            );
447        }
448        Ok(cluster_set)
449    }
450
451    async fn get_all_topic_config(
452        &self,
453        broker_addr: CheetahString,
454        timeout_millis: u64,
455    ) -> crate::ClientResult<TopicConfigSerializeWrapper> {
456        self.mq_client_api()?
457            .get_all_topic_config(&broker_addr, timeout_millis)
458            .await
459    }
460
461    async fn set_message_request_mode(
462        &self,
463        broker_addr: CheetahString,
464        topic: CheetahString,
465        consumer_group: CheetahString,
466        mode: MessageRequestMode,
467        pop_work_group_size: i32,
468        timeout_millis: u64,
469    ) -> crate::ClientResult<()> {
470        let mq_client_api = self.mq_client_api()?;
471        match mq_client_api
472            .set_message_request_mode(
473                &broker_addr,
474                &topic,
475                &consumer_group,
476                mode,
477                pop_work_group_size,
478                timeout_millis,
479            )
480            .await
481        {
482            Ok(_) => Ok(()),
483            Err(e) => Err(e),
484        }
485    }
486
487    async fn examine_topic_config(
488        &self,
489        addr: CheetahString,
490        topic: CheetahString,
491    ) -> crate::ClientResult<TopicConfig> {
492        self.client_instance
493            .as_ref()
494            .ok_or(crate::ClientError::not_started())?
495            .get_topic_config(&addr, topic, self.remoting_timeout_millis()?)
496            .await
497    }
498
499    async fn create_static_topic(
500        &self,
501        addr: CheetahString,
502        default_topic: CheetahString,
503        topic_config: TopicConfig,
504        mapping_detail: TopicQueueMappingDetail,
505        force: bool,
506    ) -> crate::ClientResult<()> {
507        self.mq_client_api()?
508            .create_static_topic(
509                &addr,
510                default_topic,
511                topic_config,
512                mapping_detail,
513                force,
514                self.remoting_timeout_millis()?,
515            )
516            .await
517    }
518
519    async fn create_lite_pull_topic(
520        &self,
521        addr: CheetahString,
522        topic: CheetahString,
523        queue_num: i32,
524        topic_sys_flag: i32,
525        read_queue_nums: i32,
526        write_queue_nums: i32,
527    ) -> crate::ClientResult<()> {
528        let config = lite_pull_topic_config(
529            topic,
530            queue_num,
531            topic_sys_flag,
532            read_queue_nums,
533            write_queue_nums,
534            false,
535        )?;
536        self.create_and_update_topic_config(addr, config).await
537    }
538
539    async fn update_lite_pull_topic(
540        &self,
541        addr: CheetahString,
542        topic: CheetahString,
543        read_queue_nums: i32,
544        write_queue_nums: i32,
545    ) -> crate::ClientResult<()> {
546        let config = lite_pull_topic_config(topic, 0, 0, read_queue_nums, write_queue_nums, true)?;
547        self.create_and_update_topic_config(addr, config).await
548    }
549
550    async fn get_lite_pull_topic(&self, addr: CheetahString, topic: CheetahString) -> crate::ClientResult<TopicConfig> {
551        let lite_info = self
552            .mq_client_api()?
553            .get_broker_lite_info(&addr, self.remoting_timeout_millis()?)
554            .await?;
555        if !lite_info.get_topic_meta().contains_key(&topic) {
556            return Err(mq_client_err!(format!("Lite pull topic not found: {topic}")));
557        }
558        self.examine_topic_config(addr, topic).await
559    }
560
561    async fn delete_lite_pull_topic(
562        &self,
563        addr: CheetahString,
564        cluster_name: CheetahString,
565        topic: CheetahString,
566    ) -> crate::ClientResult<()> {
567        self.delete_topic_in_broker(HashSet::from([addr]), topic.clone())
568            .await?;
569        if cluster_name.is_empty() {
570            return Ok(());
571        }
572        let namesrv_addrs = self.get_name_server_address_list().await.into_iter().collect();
573        self.delete_topic_in_name_server(namesrv_addrs, Some(cluster_name), topic)
574            .await
575    }
576
577    async fn query_lite_pull_topic_list(&self, addr: CheetahString) -> crate::ClientResult<TopicList> {
578        let lite_info = self
579            .mq_client_api()?
580            .get_broker_lite_info(&addr, self.remoting_timeout_millis()?)
581            .await?;
582        Ok(lite_topic_list_from_broker_lite_info(Some(addr), &lite_info))
583    }
584
585    async fn query_lite_pull_topic_by_cluster(&self, cluster_name: CheetahString) -> crate::ClientResult<TopicList> {
586        let timeout_millis = self.remoting_timeout_millis()?;
587        let api = self.mq_client_api()?;
588        let cluster_info = api.get_broker_cluster_info(timeout_millis).await?;
589        let mut topic_names = HashSet::new();
590
591        for broker_addr in broker_addrs_for_cluster(&cluster_info, &cluster_name) {
592            let lite_info = api.get_broker_lite_info(&broker_addr, timeout_millis).await?;
593            topic_names.extend(lite_info.get_topic_meta().keys().cloned());
594        }
595
596        Ok(topic_list_from_lite_topic_names(None, topic_names))
597    }
598
599    async fn get_topic_config_by_topic_name(
600        &self,
601        broker_addr: CheetahString,
602        topic_name: CheetahString,
603    ) -> crate::ClientResult<TopicConfig> {
604        self.client_instance
605            .as_ref()
606            .ok_or(crate::ClientError::not_started())?
607            .get_topic_config(&broker_addr, topic_name, self.remoting_timeout_millis()?)
608            .await
609    }
610
611    async fn get_topic_stats_info(
612        &self,
613        broker_addr: CheetahString,
614        topic: CheetahString,
615    ) -> crate::ClientResult<TopicStatsTable> {
616        self.mq_client_api()?
617            .get_topic_stats_info(
618                &broker_addr,
619                GetTopicStatsInfoRequestHeader {
620                    topic,
621                    topic_request_header: None,
622                },
623                self.remoting_timeout_millis()?,
624            )
625            .await
626    }
627
628    async fn query_broker_has_topic(
629        &self,
630        broker_addr: CheetahString,
631        topic: CheetahString,
632    ) -> crate::ClientResult<bool> {
633        map_topic_config_lookup_result(self.get_topic_config_by_topic_name(broker_addr, topic).await)
634    }
635
636    async fn get_system_topic_list_from_broker(&self, broker_addr: CheetahString) -> crate::ClientResult<TopicList> {
637        self.mq_client_api()?
638            .get_system_topic_list_from_broker(&broker_addr, self.remoting_timeout_millis()?)
639            .await
640    }
641
642    async fn examine_topic_route_info_with_timeout(
643        &self,
644        topic: CheetahString,
645        timeout_millis: u64,
646    ) -> crate::ClientResult<Option<TopicRouteData>> {
647        self.mq_client_api()?
648            .get_topic_route_info_from_name_server(&topic, timeout_millis)
649            .await
650    }
651    #[allow(deprecated)]
652    async fn delete_topic_in_broker_concurrent(
653        &self,
654        addrs: HashSet<CheetahString>,
655        topic: CheetahString,
656    ) -> AdminToolResult<BrokerOperatorResult> {
657        let api = match self.mq_client_api() {
658            Ok(api) => api,
659            Err(error) => {
660                return AdminToolResult::failure(admin_result_code_for_error(&error), error.to_string());
661            }
662        };
663        let timeout = match self.remoting_timeout_millis() {
664            Ok(timeout) => timeout,
665            Err(error) => return AdminToolResult::failure(admin_result_code_for_error(&error), error.to_string()),
666        };
667        let mut success_list = Vec::new();
668        let mut failure_list = Vec::new();
669
670        for addr in addrs {
671            let request_header = DeleteTopicRequestHeader {
672                topic: topic.clone(),
673                topic_request_header: None,
674            };
675            match api.delete_topic_in_broker(&addr, request_header, timeout).await {
676                Ok(()) => success_list.push(addr),
677                Err(error) => {
678                    warn!("deleteTopicInBroker error. topic={}, broker={}, {}", topic, addr, error);
679                    failure_list.push(addr);
680                }
681            }
682        }
683
684        AdminToolResult::success(broker_operator_result(success_list, failure_list))
685    }
686
687    async fn get_parent_topic_info(
688        &self,
689        broker_addr: CheetahString,
690        topic: CheetahString,
691    ) -> crate::ClientResult<GetParentTopicInfoResponseBody> {
692        self.mq_client_api()?
693            .get_parent_topic_info(&broker_addr, topic, self.remoting_timeout_millis()?)
694            .await
695    }
696
697    async fn get_lite_topic_info(
698        &self,
699        broker_addr: CheetahString,
700        parent_topic: CheetahString,
701        lite_topic: CheetahString,
702    ) -> crate::ClientResult<GetLiteTopicInfoResponseBody> {
703        if let Some(ref mq_client_instance) = self.client_instance {
704            mq_client_instance
705                .get_mq_client_api_impl()?
706                .get_lite_topic_info(
707                    &broker_addr,
708                    &parent_topic,
709                    &lite_topic,
710                    self.remoting_timeout_millis()?,
711                )
712                .await
713        } else {
714            Err(crate::ClientError::not_started())
715        }
716    }
717}
718
719#[allow(unused_variables, reason = "trait contracts reserve parameters")]
720#[allow(unused_mut)]
721impl ConsumerAdmin for DefaultMQAdminExtImpl {
722    async fn create_and_update_subscription_group_config(
723        &self,
724        addr: CheetahString,
725        config: SubscriptionGroupConfig,
726    ) -> crate::ClientResult<()> {
727        self.client_instance
728            .as_ref()
729            .ok_or(crate::ClientError::not_started())?
730            .get_mq_client_api_impl()?
731            .create_subscription_group(&addr, &config, self.remoting_timeout_millis()?)
732            .await
733    }
734
735    async fn create_and_update_subscription_group_config_list(
736        &self,
737        broker_addr: CheetahString,
738        configs: Vec<SubscriptionGroupConfig>,
739    ) -> crate::ClientResult<()> {
740        for config in configs {
741            self.create_and_update_subscription_group_config(broker_addr.clone(), config)
742                .await?;
743        }
744        Ok(())
745    }
746
747    async fn examine_subscription_group_config(
748        &self,
749        addr: CheetahString,
750        group: CheetahString,
751    ) -> crate::ClientResult<SubscriptionGroupConfig> {
752        self.mq_client_api()?
753            .get_subscription_group_config(&addr, group, self.remoting_timeout_millis()?)
754            .await
755    }
756
757    async fn examine_consume_stats(
758        &self,
759        consumer_group: CheetahString,
760        topic: Option<CheetahString>,
761        cluster_name: Option<CheetahString>,
762        broker_addr: Option<CheetahString>,
763        timeout_millis: Option<u64>,
764    ) -> crate::ClientResult<ConsumeStats> {
765        let timeout = timeout_millis.unwrap_or(self.remoting_timeout_millis()?);
766        let topic_str = topic.clone().unwrap_or_default();
767
768        if let Some(addr) = broker_addr {
769            let request_header = GetConsumeStatsRequestHeader {
770                consumer_group,
771                topic: topic_str,
772                topic_list: None,
773                topic_request_header: None,
774            };
775            return self
776                .mq_client_api()?
777                .get_consume_stats(&addr, request_header, timeout)
778                .await;
779        }
780
781        let retry_topic: CheetahString = rocketmq_model::common::mix_all::get_retry_topic(&consumer_group).into();
782        let topic_route = self
783            .mq_client_api()?
784            .get_topic_route_info_from_name_server(&retry_topic, timeout)
785            .await?;
786
787        let mut result = ConsumeStats::new();
788
789        if let Some(route_data) = topic_route {
790            for bd in &route_data.broker_datas {
791                if let Some(master_addr) = bd.broker_addrs().get(&rocketmq_model::common::mix_all::MASTER_ID) {
792                    let request_header = GetConsumeStatsRequestHeader {
793                        consumer_group: consumer_group.clone(),
794                        topic: topic_str.clone(),
795                        topic_list: None,
796                        topic_request_header: None,
797                    };
798                    let cs = self
799                        .mq_client_api()?
800                        .get_consume_stats(master_addr, request_header, timeout)
801                        .await?;
802
803                    result.get_offset_table_mut().extend(cs.offset_table);
804                    let new_tps = result.get_consume_tps() + cs.consume_tps;
805                    result.set_consume_tps(new_tps);
806                }
807            }
808        }
809
810        Ok(result)
811    }
812
813    async fn examine_consumer_connection_info(
814        &self,
815        consumer_group: CheetahString,
816        broker_addr: Option<CheetahString>,
817    ) -> crate::ClientResult<ConsumerConnection> {
818        let mut result = ConsumerConnection::new();
819        let timeout = self.remoting_timeout_millis()?;
820
821        let selected_addr = if let Some(broker_addr) = broker_addr {
822            Some(broker_addr)
823        } else {
824            let topic = CheetahString::from_string(mix_all::get_retry_topic(consumer_group.as_str()));
825            let topic_route_data = self
826                .mq_client_api()?
827                .get_topic_route_info_from_name_server(&topic, timeout)
828                .await?;
829
830            topic_route_data.and_then(|topic_route_data| {
831                topic_route_data
832                    .broker_datas
833                    .choose(&mut rand::rng())
834                    .and_then(|broker_data| broker_data.select_broker_addr())
835            })
836        };
837
838        if let Some(broker_addr) = selected_addr {
839            result = self
840                .mq_client_api()?
841                .get_consumer_connection_list(broker_addr.as_str(), consumer_group.clone(), timeout)
842                .await?;
843        }
844
845        if result.get_connection_set().is_empty() {
846            return Err(mq_client_err!(
847                rocketmq_protocol::code::response_code::ResponseCode::ConsumerNotOnline,
848                "Not found the consumer group connection"
849            ));
850        }
851
852        Ok(result)
853    }
854
855    async fn examine_producer_connection_info(
856        &self,
857        producer_group: CheetahString,
858        topic: CheetahString,
859    ) -> crate::ClientResult<ProducerConnection> {
860        let mut result = ProducerConnection::new();
861        let timeout = self.remoting_timeout_millis()?;
862
863        if let Some(topic_route_data) = self.examine_topic_route_info(topic).await? {
864            let brokers = &topic_route_data.broker_datas;
865            let selected_addr = brokers
866                .choose(&mut rand::rng())
867                .and_then(|broker_data| broker_data.select_broker_addr());
868            if let Some(addr) = selected_addr {
869                result = self
870                    .mq_client_api()?
871                    .get_producer_connection_list(addr.as_str(), producer_group.clone(), timeout)
872                    .await?;
873            }
874        }
875
876        if result.connection_set().is_empty() {
877            return Err(mq_client_err!("Not found the producer group connection"));
878        }
879
880        Ok(result)
881    }
882
883    async fn get_all_producer_info(&self, broker_addr: CheetahString) -> crate::ClientResult<ProducerTableInfo> {
884        self.mq_client_api()?
885            .get_all_producer_info(broker_addr.as_str(), self.remoting_timeout_millis()?)
886            .await
887    }
888
889    async fn delete_subscription_group(
890        &self,
891        addr: CheetahString,
892        group_name: CheetahString,
893        remove_offset: Option<bool>,
894    ) -> crate::ClientResult<()> {
895        self.mq_client_api()?
896            .delete_subscription_group(
897                &addr,
898                group_name,
899                remove_offset.unwrap_or(false),
900                self.remoting_timeout_millis()?,
901            )
902            .await
903    }
904
905    async fn delete_subscription_group_list(
906        &self,
907        addr: CheetahString,
908        group_names: Vec<CheetahString>,
909        clean_offset: bool,
910    ) -> crate::ClientResult<()> {
911        self.mq_client_api()?
912            .delete_subscription_group_list(&addr, group_names, clean_offset, self.remoting_timeout_millis()?)
913            .await
914    }
915
916    async fn get_consume_status(
917        &self,
918        topic: CheetahString,
919        group: CheetahString,
920        client_addr: CheetahString,
921    ) -> crate::ClientResult<HashMap<CheetahString, HashMap<MessageQueue, u64>>> {
922        let Some(route_data) = self.examine_topic_route_info(topic.clone()).await? else {
923            return Ok(HashMap::new());
924        };
925
926        let api = self.mq_client_api()?;
927        let timeout = self.remoting_timeout_millis()?;
928        let mut merged = HashMap::new();
929        let mut queried = false;
930        let mut last_error = None;
931
932        for broker_data in &route_data.broker_datas {
933            let Some(addr) = broker_data.select_broker_addr() else {
934                continue;
935            };
936            queried = true;
937            match api
938                .invoke_broker_to_get_consumer_status(
939                    addr.as_str(),
940                    topic.clone(),
941                    group.clone(),
942                    client_addr.clone(),
943                    timeout,
944                )
945                .await
946            {
947                Ok(result) => merge_consume_status_result(&mut merged, result)?,
948                Err(error) => {
949                    warn!("get_consume_status failed on broker {}: {}", addr, error);
950                    last_error = Some(error);
951                }
952            }
953        }
954
955        if !merged.is_empty() || last_error.is_none() || !queried {
956            return Ok(merged);
957        }
958
959        if let Some(error) = last_error {
960            Err(error)
961        } else {
962            Ok(merged)
963        }
964    }
965
966    async fn query_subscription(
967        &self,
968        group: CheetahString,
969        topic: CheetahString,
970    ) -> crate::ClientResult<SubscriptionData> {
971        let timeout = self.remoting_timeout_millis()?;
972        let api = self.mq_client_api()?;
973        let topic_route = api.get_topic_route_info_from_name_server(&topic, timeout).await?;
974        let Some(route_data) = topic_route else {
975            return Err(mq_client_err!(format!("Topic route not found for: {topic}")));
976        };
977
978        for broker_data in &route_data.broker_datas {
979            let Some(addr) = broker_data.select_broker_addr() else {
980                continue;
981            };
982            let request_header = QuerySubscriptionByConsumerRequestHeader {
983                group: group.clone(),
984                topic: topic.clone(),
985                topic_request_header: None,
986            };
987            return api.query_subscription_by_consumer(&addr, request_header, timeout).await;
988        }
989
990        Err(mq_client_err!(format!("Broker address not found for topic: {topic}")))
991    }
992
993    async fn clean_expired_consumer_queue(
994        &self,
995        cluster: Option<CheetahString>,
996        addr: Option<CheetahString>,
997    ) -> crate::ClientResult<bool> {
998        self.execute_broker_cleanup_operation(cluster, addr, BrokerCleanupOperation::CleanExpiredConsumerQueue)
999            .await
1000    }
1001
1002    async fn clean_expired_consumer_queue_by_addr(&self, addr: CheetahString) -> crate::ClientResult<bool> {
1003        self.clean_expired_consumer_queue(None, Some(addr)).await
1004    }
1005
1006    async fn get_consumer_running_info(
1007        &self,
1008        consumer_group: CheetahString,
1009        client_id: CheetahString,
1010        jstack: bool,
1011        _metrics: Option<bool>,
1012    ) -> crate::ClientResult<ConsumerRunningInfo> {
1013        let retry_topic = CheetahString::from_string(mix_all::get_retry_topic(&consumer_group));
1014        let route = self
1015            .examine_topic_route_info(retry_topic.clone())
1016            .await?
1017            .ok_or_else(|| admin_route_not_found(&retry_topic))?;
1018        let api = self.mq_client_api()?;
1019        let timeout = self.remoting_timeout_millis()?;
1020        let mut last_error = None;
1021        // A client's advertised connection is an outbound ephemeral socket, not
1022        // an RPC listener. The Broker forwards diagnostics over that connection.
1023        // Try each route because the client may only be connected to one Broker.
1024        for broker in &route.broker_datas {
1025            let Some(address) = broker.select_broker_addr() else {
1026                continue;
1027            };
1028            match api
1029                .get_consumer_running_info(&address, consumer_group.clone(), client_id.clone(), jstack, timeout)
1030                .await
1031            {
1032                Ok(info) => return Ok(info),
1033                Err(error) => last_error = Some(error),
1034            }
1035        }
1036        Err(last_error.unwrap_or_else(|| admin_route_not_found(&retry_topic)))
1037    }
1038
1039    async fn consume_message_directly(
1040        &self,
1041        consumer_group: CheetahString,
1042        client_id: CheetahString,
1043        topic: CheetahString,
1044        msg_id: CheetahString,
1045    ) -> crate::ClientResult<ConsumeMessageDirectlyResult> {
1046        let message =
1047            ConsumerAdmin::query_message(self, CheetahString::default(), topic.clone(), msg_id.clone()).await?;
1048        // The owning Broker resolves this physical offset and forwards the
1049        // request over its existing consumer connection. A client's outbound
1050        // socket is not an RPC listener; retrying another Broker could consume
1051        // a different message at the same offset.
1052        let broker_addr = CheetahString::from(message.store_host().to_string());
1053        let consumer_connection = self
1054            .examine_consumer_connection_info(consumer_group.clone(), Some(broker_addr.clone()))
1055            .await?;
1056        let (resolved_client_id, _) =
1057            select_consumer_direct_connection(&consumer_group, &consumer_connection, Some(&client_id))?;
1058        let request_header = ConsumeMessageDirectlyResultRequestHeader {
1059            consumer_group,
1060            client_id: Some(resolved_client_id),
1061            msg_id: Some(msg_id),
1062            broker_name: (!message.broker_name().is_empty()).then(|| message.broker_name.clone()),
1063            topic: Some(topic),
1064            topic_sys_flag: None,
1065            group_sys_flag: None,
1066            topic_request_header: None,
1067        };
1068
1069        self.client_instance
1070            .as_ref()
1071            .ok_or(crate::ClientError::not_started())?
1072            .get_mq_client_api_impl()?
1073            .consume_message_directly(&broker_addr, request_header, &message, self.remoting_timeout_millis()?)
1074            .await
1075    }
1076
1077    async fn consume_message_directly_ext(
1078        &self,
1079        _cluster_name: CheetahString,
1080        consumer_group: CheetahString,
1081        client_id: CheetahString,
1082        topic: CheetahString,
1083        msg_id: CheetahString,
1084    ) -> crate::ClientResult<ConsumeMessageDirectlyResult> {
1085        self.consume_message_directly(consumer_group, client_id, topic, msg_id)
1086            .await
1087    }
1088
1089    async fn update_cold_data_flow_ctr_group_config(
1090        &self,
1091        broker_addr: CheetahString,
1092        properties: HashMap<CheetahString, CheetahString>,
1093    ) -> crate::ClientResult<()> {
1094        self.mq_client_api()?
1095            .update_cold_data_flow_ctr_group_config(broker_addr, properties, self.remoting_timeout_millis()?)
1096            .await
1097    }
1098
1099    async fn remove_cold_data_flow_ctr_group_config(
1100        &self,
1101        broker_addr: CheetahString,
1102        consumer_group: CheetahString,
1103    ) -> crate::ClientResult<()> {
1104        self.mq_client_api()?
1105            .remove_cold_data_flow_ctr_group_config(broker_addr, consumer_group, self.remoting_timeout_millis()?)
1106            .await
1107    }
1108
1109    async fn query_lite_pull_subscription_list(
1110        &self,
1111        addr: CheetahString,
1112        topic: CheetahString,
1113    ) -> crate::ClientResult<GroupList> {
1114        let lite_info = self
1115            .mq_client_api()?
1116            .get_broker_lite_info(&addr, self.remoting_timeout_millis()?)
1117            .await?;
1118        Ok(lite_subscription_group_list_from_broker_lite_info(&topic, &lite_info))
1119    }
1120
1121    async fn examine_consume_stats_with_queue(
1122        &self,
1123        consumer_group: CheetahString,
1124        topic: Option<CheetahString>,
1125        queue_id: Option<i32>,
1126    ) -> crate::ClientResult<ConsumeStats> {
1127        let mut stats = self
1128            .examine_consume_stats(consumer_group, topic.clone(), None, None, None)
1129            .await?;
1130        filter_consume_stats(&mut stats, topic.as_ref(), queue_id);
1131        Ok(stats)
1132    }
1133
1134    async fn examine_consume_stats_concurrent(
1135        &self,
1136        consumer_group: CheetahString,
1137        topic: Option<CheetahString>,
1138    ) -> AdminToolResult<ConsumeStats> {
1139        match self
1140            .examine_consume_stats(consumer_group, topic, None, None, None)
1141            .await
1142        {
1143            Ok(stats) => AdminToolResult::success(stats),
1144            Err(error) => AdminToolResult::failure(admin_result_code_for_error(&error), error.to_string()),
1145        }
1146    }
1147
1148    async fn examine_consume_stats_concurrent_with_cluster(
1149        &self,
1150        consumer_group: CheetahString,
1151        topic: Option<CheetahString>,
1152        cluster_name: Option<CheetahString>,
1153    ) -> AdminToolResult<ConsumeStats> {
1154        match self
1155            .examine_consume_stats(consumer_group, topic, cluster_name, None, None)
1156            .await
1157        {
1158            Ok(stats) => AdminToolResult::success(stats),
1159            Err(error) => AdminToolResult::failure(admin_result_code_for_error(&error), error.to_string()),
1160        }
1161    }
1162
1163    async fn sync_broker_member_group(
1164        &self,
1165        controller_addr: CheetahString,
1166        cluster_name: CheetahString,
1167        broker_name: CheetahString,
1168    ) -> crate::ClientResult<()> {
1169        if !controller_addr.is_empty() {
1170            return Err(ClientError::illegal_argument(
1171                "syncBrokerMemberGroup uses NameServer; controllerAddr is not supported by this facade",
1172            ));
1173        }
1174
1175        self.mq_client_api()?
1176            .sync_broker_member_group(&cluster_name, &broker_name, false)
1177            .await?;
1178        Ok(())
1179    }
1180    #[allow(deprecated)]
1181    async fn message_track_detail(&self, msg: MessageExt) -> crate::ClientResult<Vec<MessageTrack>> {
1182        let group_list = self.query_topic_consume_by_who(msg.topic().clone()).await?;
1183        let mut result = Vec::with_capacity(group_list.get_group_list().len());
1184
1185        for group in group_list.get_group_list() {
1186            let mut track = build_message_track(group.as_str());
1187            let consumer_connection = match self.examine_consumer_connection_info(group.clone(), None).await {
1188                Ok(connection) => connection,
1189                Err(error) => {
1190                    apply_track_error(&mut track, &error);
1191                    result.push(track);
1192                    continue;
1193                }
1194            };
1195
1196            match consumer_connection.get_consume_type() {
1197                Some(ConsumeType::ConsumeActively) => {
1198                    track.set_track_type(TrackType::Pull);
1199                }
1200                Some(ConsumeType::ConsumePassively) => {
1201                    if consumer_connection.get_message_model() == Some(MessageModel::Broadcasting) {
1202                        track.set_track_type(TrackType::ConsumeBroadcasting);
1203                        result.push(track);
1204                        continue;
1205                    }
1206
1207                    let consumed = match self.message_consumed_by_group(&msg, group).await {
1208                        Ok(consumed) => consumed,
1209                        Err(error) => {
1210                            apply_track_error(&mut track, &error);
1211                            result.push(track);
1212                            continue;
1213                        }
1214                    };
1215
1216                    if consumed {
1217                        track.set_track_type(resolve_consumed_track_type(&msg, &consumer_connection));
1218                    } else {
1219                        track.set_track_type(TrackType::NotConsumedYet);
1220                    }
1221                }
1222                _ => {}
1223            }
1224
1225            result.push(track);
1226        }
1227
1228        result.sort_by(|left, right| left.consumer_group.cmp(&right.consumer_group));
1229        Ok(result)
1230    }
1231    #[allow(deprecated)]
1232    async fn message_track_detail_concurrent(&self, msg: MessageExt) -> AdminToolResult<Vec<MessageTrack>> {
1233        match self.message_track_detail(msg).await {
1234            Ok(data) => AdminToolResult::success(data),
1235            Err(error) => AdminToolResult::failure(admin_result_code_for_error(&error), error.to_string()),
1236        }
1237    }
1238
1239    async fn fetch_consume_stats_in_broker(
1240        &self,
1241        broker_addr: CheetahString,
1242        is_order: bool,
1243        timeout_millis: u64,
1244    ) -> crate::ClientResult<ConsumeStatsList> {
1245        self.mq_client_api()?
1246            .fetch_consume_stats_in_broker(&broker_addr, GetConsumeStatsInBrokerHeader { is_order }, timeout_millis)
1247            .await
1248    }
1249
1250    async fn get_all_subscription_group(
1251        &self,
1252        broker_addr: CheetahString,
1253        timeout_millis: u64,
1254    ) -> crate::ClientResult<SubscriptionGroupWrapper> {
1255        self.mq_client_api()?
1256            .get_all_subscription_group_config(&broker_addr, timeout_millis)
1257            .await
1258    }
1259
1260    async fn query_consume_queue(
1261        &self,
1262        broker_addr: CheetahString,
1263        topic: CheetahString,
1264        queue_id: i32,
1265        index: u64,
1266        count: i32,
1267        consumer_group: CheetahString,
1268    ) -> crate::ClientResult<QueryConsumeQueueResponseBody> {
1269        let index = query_consume_queue_index_to_java_long(index)?;
1270        self.mq_client_api()?
1271            .query_consume_queue(
1272                &broker_addr,
1273                topic,
1274                queue_id,
1275                index,
1276                count,
1277                consumer_group,
1278                self.remoting_timeout_millis()?,
1279            )
1280            .await
1281    }
1282
1283    async fn update_and_get_group_read_forbidden(
1284        &self,
1285        broker_addr: CheetahString,
1286        group_name: CheetahString,
1287        topic_name: CheetahString,
1288        readable: Option<bool>,
1289    ) -> crate::ClientResult<GroupForbidden> {
1290        let client_instance = self.client_instance.as_ref().ok_or(crate::ClientError::not_started())?;
1291        let request_header = update_group_forbidden_request_header(group_name, topic_name, readable);
1292
1293        client_instance
1294            .get_mq_client_api_impl()?
1295            .update_and_get_group_forbidden(&broker_addr, request_header, self.remoting_timeout_millis()?)
1296            .await
1297    }
1298
1299    async fn query_message(
1300        &self,
1301        _cluster_name: CheetahString,
1302        topic: CheetahString,
1303        msg_id: CheetahString,
1304    ) -> crate::ClientResult<MessageExt> {
1305        let client_instance = self.client_instance.as_ref().ok_or(crate::ClientError::not_started())?;
1306
1307        let msg_id_str = msg_id.as_str();
1308
1309        if let Err(e) = MessageDecoder::validate_message_id(msg_id_str) {
1310            return Err(crate::ClientError::illegal_argument(format!(
1311                "Invalid message ID: {}",
1312                e
1313            )));
1314        }
1315
1316        let message_id = MessageDecoder::decode_message_id(msg_id_str)
1317            .map_err(|e| crate::ClientError::illegal_argument(format!("Failed to decode message ID: {}", e)))?;
1318        let broker_addr =
1319            CheetahString::from_string(format!("{}:{}", message_id.address.ip(), message_id.address.port()));
1320
1321        let request_header = ViewMessageRequestHeader {
1322            topic: Some(topic),
1323            offset: message_id.offset,
1324        };
1325
1326        client_instance
1327            .get_mq_client_api_impl()?
1328            .view_message(&broker_addr, request_header, self.remoting_timeout_millis()?)
1329            .await
1330    }
1331
1332    async fn get_lite_group_info(
1333        &self,
1334        broker_addr: CheetahString,
1335        group: CheetahString,
1336        lite_topic: CheetahString,
1337        top_k: i32,
1338    ) -> crate::ClientResult<GetLiteGroupInfoResponseBody> {
1339        self.mq_client_api()?
1340            .get_lite_group_info(&broker_addr, group, lite_topic, top_k, self.remoting_timeout_millis()?)
1341            .await
1342    }
1343}
1344
1345#[allow(unused_variables, reason = "trait contracts reserve parameters")]
1346#[allow(unused_mut)]
1347impl BrokerAdmin for DefaultMQAdminExtImpl {
1348    async fn start(&mut self) -> crate::ClientResult<()> {
1349        self.start_admin().await
1350    }
1351
1352    async fn shutdown(&mut self) {
1353        self.shutdown_admin().await;
1354    }
1355
1356    async fn add_broker_to_container(
1357        &self,
1358        broker_container_addr: CheetahString,
1359        broker_config: CheetahString,
1360    ) -> crate::ClientResult<()> {
1361        if let Some(ref mq_client_instance) = self.client_instance {
1362            mq_client_instance
1363                .get_mq_client_api_impl()?
1364                .add_broker(&broker_container_addr, broker_config, self.remoting_timeout_millis()?)
1365                .await
1366        } else {
1367            Err(crate::ClientError::not_started())
1368        }
1369    }
1370
1371    async fn remove_broker_from_container(
1372        &self,
1373        broker_container_addr: CheetahString,
1374        cluster_name: CheetahString,
1375        broker_name: CheetahString,
1376        broker_id: u64,
1377    ) -> crate::ClientResult<()> {
1378        if let Some(ref mq_client_instance) = self.client_instance {
1379            mq_client_instance
1380                .get_mq_client_api_impl()?
1381                .remove_broker(
1382                    &broker_container_addr,
1383                    cluster_name,
1384                    broker_name,
1385                    broker_id,
1386                    self.remoting_timeout_millis()?,
1387                )
1388                .await
1389        } else {
1390            Err(crate::ClientError::not_started())
1391        }
1392    }
1393
1394    async fn update_broker_config(
1395        &self,
1396        broker_addr: CheetahString,
1397        properties: HashMap<CheetahString, CheetahString>,
1398    ) -> crate::ClientResult<()> {
1399        let validator_input = properties
1400            .iter()
1401            .map(|(key, value)| (key.to_string(), value.to_string()))
1402            .collect::<HashMap<String, String>>();
1403        Validators::check_broker_config(&validator_input)?;
1404
1405        if let Some(ref mq_client_instance) = self.client_instance {
1406            mq_client_instance
1407                .get_mq_client_api_impl()?
1408                .update_broker_config(&broker_addr, properties, self.remoting_timeout_millis()?)
1409                .await
1410        } else {
1411            Err(crate::ClientError::not_started())
1412        }
1413    }
1414
1415    async fn get_broker_config(
1416        &self,
1417        broker_addr: CheetahString,
1418    ) -> crate::ClientResult<HashMap<CheetahString, CheetahString>> {
1419        if let Some(ref mq_client_instance) = self.client_instance {
1420            mq_client_instance
1421                .get_mq_client_api_impl()?
1422                .get_broker_config(&broker_addr, self.remoting_timeout_millis()?)
1423                .await
1424        } else {
1425            Err(crate::ClientError::not_started())
1426        }
1427    }
1428
1429    async fn fetch_broker_runtime_stats(&self, broker_addr: CheetahString) -> crate::ClientResult<KVTable> {
1430        self.mq_client_api()?
1431            .get_broker_runtime_info(&broker_addr, self.remoting_timeout_millis()?)
1432            .await
1433    }
1434
1435    async fn check_rocksdb_cq_write_progress(
1436        &self,
1437        broker_addr: CheetahString,
1438        topic: CheetahString,
1439        check_store_time: i64,
1440    ) -> crate::ClientResult<CheckRocksdbCqWriteResult> {
1441        self.mq_client_api()?
1442            .check_rocksdb_cq_write_progress(&broker_addr, topic, check_store_time, self.remoting_timeout_millis()?)
1443            .await
1444    }
1445
1446    async fn wipe_write_perm_of_broker(
1447        &self,
1448        namesrv_addr: CheetahString,
1449        broker_name: CheetahString,
1450    ) -> crate::ClientResult<i32> {
1451        self.mq_client_api()?
1452            .wipe_write_perm_of_broker(namesrv_addr, broker_name, self.remoting_timeout_millis()?)
1453            .await
1454    }
1455
1456    async fn add_write_perm_of_broker(
1457        &self,
1458        namesrv_addr: CheetahString,
1459        broker_name: CheetahString,
1460    ) -> crate::ClientResult<i32> {
1461        self.mq_client_api()?
1462            .add_write_perm_of_broker(namesrv_addr, broker_name, self.remoting_timeout_millis()?)
1463            .await
1464    }
1465
1466    async fn resume_check_half_message(
1467        &self,
1468        topic: CheetahString,
1469        msg_id: CheetahString,
1470    ) -> crate::ClientResult<bool> {
1471        let message = self
1472            .query_message(CheetahString::default(), topic.clone(), msg_id.clone())
1473            .await?;
1474        let broker_addr = CheetahString::from_string(message.store_host().to_string());
1475        let broker_msg_id = if message
1476            .property(&CheetahString::from_static_str(
1477                MessageConst::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX,
1478            ))
1479            .is_some()
1480        {
1481            message.msg_id().clone()
1482        } else {
1483            msg_id
1484        };
1485
1486        self.mq_client_api()?
1487            .resume_check_half_message(&broker_addr, topic, broker_msg_id, self.remoting_timeout_millis()?)
1488            .await
1489    }
1490
1491    async fn get_controller_meta_data(
1492        &self,
1493        controller_addr: CheetahString,
1494    ) -> crate::ClientResult<GetMetaDataResponseHeader> {
1495        if let Some(ref mq_client_instance) = self.client_instance {
1496            Ok(mq_client_instance
1497                .get_mq_client_api_impl()?
1498                .get_controller_metadata(controller_addr, self.remoting_timeout_millis()?)
1499                .await?)
1500        } else {
1501            Err(crate::ClientError::not_started())
1502        }
1503    }
1504
1505    async fn get_controller_config(
1506        &self,
1507        controller_servers: Vec<CheetahString>,
1508    ) -> crate::ClientResult<HashMap<CheetahString, HashMap<CheetahString, CheetahString>>> {
1509        if let Some(ref mq_client_instance) = self.client_instance {
1510            let mut result: HashMap<CheetahString, HashMap<CheetahString, CheetahString>> = HashMap::new();
1511            let mq_client_api = mq_client_instance.get_mq_client_api_impl()?;
1512            let timeout_millis = self.remoting_timeout_millis()?;
1513            let controller_servers =
1514                controller_servers_or_namesrv(controller_servers, &mq_client_api.get_name_server_address_list());
1515
1516            for controller_addr in controller_servers {
1517                let config = mq_client_api
1518                    .get_controller_config(controller_addr.clone(), timeout_millis)
1519                    .await?;
1520                result.insert(controller_addr, config);
1521            }
1522
1523            Ok(result)
1524        } else {
1525            Err(crate::ClientError::not_started())
1526        }
1527    }
1528
1529    async fn update_controller_config(
1530        &self,
1531        properties: HashMap<CheetahString, CheetahString>,
1532        controllers: Vec<CheetahString>,
1533    ) -> crate::ClientResult<()> {
1534        if let Some(ref mq_client_instance) = self.client_instance {
1535            mq_client_instance
1536                .get_mq_client_api_impl()?
1537                .update_controller_config(properties, controllers, self.remoting_timeout_millis()?)
1538                .await
1539        } else {
1540            Err(crate::ClientError::not_started())
1541        }
1542    }
1543
1544    async fn clean_controller_broker_data(
1545        &self,
1546        controller_addr: CheetahString,
1547        cluster_name: CheetahString,
1548        broker_name: CheetahString,
1549        broker_controller_ids_to_clean: Option<CheetahString>,
1550        is_clean_living_broker: bool,
1551    ) -> crate::ClientResult<()> {
1552        if let Some(ref mq_client_instance) = self.client_instance {
1553            mq_client_instance
1554                .get_mq_client_api_impl()?
1555                .clean_controller_broker_data(
1556                    controller_addr,
1557                    cluster_name,
1558                    broker_name,
1559                    broker_controller_ids_to_clean,
1560                    is_clean_living_broker,
1561                    self.remoting_timeout_millis()?,
1562                )
1563                .await
1564        } else {
1565            Err(crate::ClientError::not_started())
1566        }
1567    }
1568
1569    async fn get_cold_data_flow_ctr_info(&self, broker_addr: CheetahString) -> crate::ClientResult<CheetahString> {
1570        self.mq_client_api()?
1571            .get_cold_data_flow_ctr_info(broker_addr, self.remoting_timeout_millis()?)
1572            .await
1573    }
1574
1575    async fn notify_min_broker_id_changed(
1576        &self,
1577        cluster_name: CheetahString,
1578        broker_name: CheetahString,
1579        min_broker_id: u64,
1580        min_broker_addr: CheetahString,
1581        offline_broker_addr: Option<CheetahString>,
1582        ha_broker_addr: Option<CheetahString>,
1583    ) -> crate::ClientResult<()> {
1584        let request_header = notify_min_broker_id_change_request_header(
1585            min_broker_id,
1586            min_broker_addr,
1587            offline_broker_addr,
1588            ha_broker_addr,
1589        )?;
1590        let mq_client_api = self.mq_client_api()?;
1591        let broker_member_group = mq_client_api
1592            .sync_broker_member_group(&cluster_name, &broker_name, false)
1593            .await?
1594            .unwrap_or_else(|| BrokerMemberGroup::new(cluster_name.clone(), broker_name.clone()));
1595        let broker_addrs = choose_min_broker_notify_addrs(
1596            &broker_member_group.broker_addrs,
1597            min_broker_id,
1598            request_header.offline_broker_addr.as_ref(),
1599        );
1600
1601        if broker_addrs.is_empty() {
1602            return Err(ClientError::illegal_argument(format!(
1603                "notifyMinBrokerIdChanged cannot resolve broker addresses for cluster `{}` broker `{}`",
1604                cluster_name, broker_name
1605            )));
1606        }
1607
1608        for broker_addr in broker_addrs {
1609            mq_client_api
1610                .notify_min_broker_id_changed(&broker_addr, request_header.clone(), 300)
1611                .await?;
1612        }
1613        Ok(())
1614    }
1615
1616    async fn export_pop_records(&self, broker_addr: CheetahString, timeout: u64) -> crate::ClientResult<()> {
1617        self.mq_client_api()?.export_pop_record(broker_addr, timeout).await
1618    }
1619
1620    async fn switch_timer_engine(
1621        &self,
1622        broker_addr: CheetahString,
1623        des_timer_engine: CheetahString,
1624    ) -> crate::ClientResult<()> {
1625        self.mq_client_api()?
1626            .switch_timer_engine(&broker_addr, des_timer_engine, self.remoting_timeout_millis()?)
1627            .await
1628    }
1629
1630    async fn trigger_lite_dispatch(
1631        &self,
1632        broker_addr: CheetahString,
1633        group: CheetahString,
1634        client_id: CheetahString,
1635    ) -> crate::ClientResult<()> {
1636        self.mq_client_api()?
1637            .trigger_lite_dispatch(&broker_addr, group, client_id, self.remoting_timeout_millis()?)
1638            .await
1639    }
1640
1641    async fn view_broker_stats_data(
1642        &self,
1643        broker_addr: CheetahString,
1644        stats_name: CheetahString,
1645        stats_key: CheetahString,
1646    ) -> crate::ClientResult<BrokerStatsData> {
1647        let request_header = ViewBrokerStatsDataRequestHeader { stats_name, stats_key };
1648        self.mq_client_api()?
1649            .view_broker_stats_data(&broker_addr, request_header, self.remoting_timeout_millis()?)
1650            .await
1651    }
1652
1653    async fn get_broker_ha_status(&self, broker_addr: CheetahString) -> crate::ClientResult<HARuntimeInfo> {
1654        if let Some(ref mq_client_instance) = self.client_instance {
1655            Ok(mq_client_instance
1656                .get_mq_client_api_impl()?
1657                .get_broker_ha_status(broker_addr, self.remoting_timeout_millis()?)
1658                .await?)
1659        } else {
1660            Err(crate::ClientError::not_started())
1661        }
1662    }
1663
1664    async fn get_in_sync_state_data(
1665        &self,
1666        controller_address: CheetahString,
1667        brokers: Vec<CheetahString>,
1668    ) -> crate::ClientResult<BrokerReplicasInfo> {
1669        if let Some(ref mq_client_instance) = self.client_instance {
1670            Ok(mq_client_instance
1671                .get_mq_client_api_impl()?
1672                .get_in_sync_state_data(controller_address, brokers, self.remoting_timeout_millis()?)
1673                .await?)
1674        } else {
1675            Err(crate::ClientError::not_started())
1676        }
1677    }
1678
1679    async fn get_broker_epoch_cache(&self, broker_addr: CheetahString) -> crate::ClientResult<EpochEntryCache> {
1680        if let Some(ref mq_client_instance) = self.client_instance {
1681            Ok(mq_client_instance
1682                .get_mq_client_api_impl()?
1683                .get_broker_epoch_cache(broker_addr, self.remoting_timeout_millis()?)
1684                .await?)
1685        } else {
1686            Err(crate::ClientError::not_started())
1687        }
1688    }
1689
1690    async fn elect_master(
1691        &self,
1692        controller_addr: CheetahString,
1693        cluster_name: CheetahString,
1694        broker_name: CheetahString,
1695        broker_id: Option<u64>,
1696    ) -> crate::ClientResult<(ElectMasterResponseHeader, BrokerMemberGroup)> {
1697        if let Some(ref mq_client_instance) = self.client_instance {
1698            mq_client_instance
1699                .get_mq_client_api_impl()?
1700                .elect_master(
1701                    controller_addr,
1702                    cluster_name,
1703                    broker_name,
1704                    broker_id,
1705                    self.remoting_timeout_millis()?,
1706                )
1707                .await
1708        } else {
1709            Err(crate::ClientError::not_started())
1710        }
1711    }
1712
1713    async fn get_broker_lite_info(
1714        &self,
1715        broker_addr: CheetahString,
1716    ) -> crate::ClientResult<GetBrokerLiteInfoResponseBody> {
1717        if let Some(ref mq_client_instance) = self.client_instance {
1718            mq_client_instance
1719                .get_mq_client_api_impl()?
1720                .get_broker_lite_info(&broker_addr, self.remoting_timeout_millis()?)
1721                .await
1722        } else {
1723            Err(crate::ClientError::not_started())
1724        }
1725    }
1726
1727    async fn get_lite_client_info(
1728        &self,
1729        broker_addr: CheetahString,
1730        parent_topic: CheetahString,
1731        group: CheetahString,
1732        client_id: CheetahString,
1733    ) -> crate::ClientResult<GetLiteClientInfoResponseBody> {
1734        self.mq_client_api()?
1735            .get_lite_client_info(
1736                &broker_addr,
1737                parent_topic,
1738                group,
1739                client_id,
1740                self.remoting_timeout_millis()?,
1741            )
1742            .await
1743    }
1744
1745    async fn export_rocksdb_config_to_json(
1746        &self,
1747        broker_addr: CheetahString,
1748        config_types: Vec<CheetahString>,
1749    ) -> crate::ClientResult<()> {
1750        if let Some(ref mq_client_instance) = self.client_instance {
1751            mq_client_instance
1752                .get_mq_client_api_impl()?
1753                .export_rocksdb_config_to_json(broker_addr, config_types, self.remoting_timeout_millis()?)
1754                .await
1755        } else {
1756            Err(crate::ClientError::not_started())
1757        }
1758    }
1759}
1760
1761#[allow(unused_variables, reason = "trait contracts reserve parameters")]
1762#[allow(unused_mut)]
1763impl AuthAdmin for DefaultMQAdminExtImpl {
1764    async fn create_and_update_plain_access_config(
1765        &self,
1766        addr: CheetahString,
1767        config: PlainAccessConfig,
1768    ) -> crate::ClientResult<()> {
1769        self.mq_client_api()?
1770            .create_and_update_plain_access_config(addr, &config, self.remoting_timeout_millis()?)
1771            .await
1772    }
1773
1774    async fn delete_plain_access_config(
1775        &self,
1776        addr: CheetahString,
1777        access_key: CheetahString,
1778    ) -> crate::ClientResult<()> {
1779        self.mq_client_api()?
1780            .delete_plain_access_config(addr, access_key, self.remoting_timeout_millis()?)
1781            .await
1782    }
1783
1784    async fn update_global_white_addr_config(
1785        &self,
1786        addr: CheetahString,
1787        global_white_addrs: CheetahString,
1788        acl_file_full_path: Option<CheetahString>,
1789    ) -> crate::ClientResult<()> {
1790        let mq_client_api = self.mq_client_api()?;
1791
1792        validate_acl_file_path_for_global_white_addr_config(acl_file_full_path.as_ref())?;
1793
1794        mq_client_api
1795            .update_global_white_addrs_config(addr, global_white_addrs, self.remoting_timeout_millis()?)
1796            .await
1797    }
1798
1799    async fn examine_broker_cluster_acl_version_info(&self, addr: CheetahString) -> crate::ClientResult<CheetahString> {
1800        let version_info = self
1801            .mq_client_api()?
1802            .get_broker_cluster_acl_version_info(addr, self.remoting_timeout_millis()?)
1803            .await?;
1804        serde_json::to_string(&version_info)
1805            .map(CheetahString::from_string)
1806            .map_err(|error| ClientError::response_process_source("encode ClusterAclVersionInfo", error))
1807    }
1808
1809    async fn get_user_topic_config(
1810        &self,
1811        broker_addr: CheetahString,
1812        special_topic: bool,
1813        timeout_millis: u64,
1814    ) -> crate::ClientResult<TopicConfigSerializeWrapper> {
1815        let mut topic_config_wrapper = self.get_all_topic_config(broker_addr.clone(), timeout_millis).await?;
1816        let system_topic_list = self.get_system_topic_list_from_broker(broker_addr).await?;
1817
1818        if let Some(ref mut topic_table) = topic_config_wrapper.topic_config_table_mut() {
1819            retain_java_user_topic_config(topic_table, &system_topic_list.topic_list, special_topic);
1820        }
1821
1822        Ok(topic_config_wrapper)
1823    }
1824
1825    async fn create_user(
1826        &self,
1827        broker_addr: CheetahString,
1828        username: CheetahString,
1829        password: CheetahString,
1830        user_type: CheetahString,
1831    ) -> crate::ClientResult<()> {
1832        let user_info = UserInfo {
1833            username: Some(username),
1834            user_type: Some(user_type),
1835            password: Some(password),
1836            user_status: None,
1837        };
1838
1839        if let Some(ref mq_client_instance) = self.client_instance {
1840            let mq_client_api = mq_client_instance.get_mq_client_api_impl()?;
1841            let timeout_millis = self.remoting_timeout_millis()?;
1842            mq_client_api
1843                .create_user(broker_addr, &user_info, timeout_millis)
1844                .await?;
1845            Ok(())
1846        } else {
1847            Err(crate::ClientError::not_started())
1848        }
1849    }
1850
1851    async fn update_user(
1852        &self,
1853        broker_addr: CheetahString,
1854        username: CheetahString,
1855        password: CheetahString,
1856        user_type: CheetahString,
1857        user_status: CheetahString,
1858    ) -> crate::ClientResult<()> {
1859        let mut user_info = UserInfo {
1860            username: Some(username),
1861            user_type: Some(user_type),
1862            password: Some(password),
1863            user_status: Some(user_status),
1864        };
1865
1866        if let Some(ref mq_client_instance) = self.client_instance {
1867            let mq_client_api = mq_client_instance.get_mq_client_api_impl()?;
1868            let timeout_millis = self.remoting_timeout_millis()?;
1869            mq_client_api
1870                .update_user(broker_addr, &user_info, timeout_millis)
1871                .await?;
1872            Ok(())
1873        } else {
1874            Err(crate::ClientError::not_started())
1875        }
1876    }
1877
1878    async fn delete_user(&self, broker_addr: CheetahString, username: CheetahString) -> crate::ClientResult<()> {
1879        if let Some(ref mq_client_instance) = self.client_instance {
1880            let mq_client_api = mq_client_instance.get_mq_client_api_impl()?;
1881            let timeout_millis = self.remoting_timeout_millis()?;
1882            mq_client_api.delete_user(broker_addr, username, timeout_millis).await?;
1883            Ok(())
1884        } else {
1885            Err(crate::ClientError::not_started())
1886        }
1887    }
1888
1889    async fn create_acl(
1890        &self,
1891        broker_addr: CheetahString,
1892        subject: CheetahString,
1893        resources: Vec<CheetahString>,
1894        actions: Vec<CheetahString>,
1895        source_ips: Vec<CheetahString>,
1896        decision: CheetahString,
1897    ) -> crate::ClientResult<()> {
1898        let acl_info = build_acl_info(subject, resources, actions, source_ips, decision);
1899        if let Some(ref mq_client_instance) = self.client_instance {
1900            mq_client_instance
1901                .get_mq_client_api_impl()?
1902                .create_acl(broker_addr, &acl_info, self.remoting_timeout_millis()?)
1903                .await
1904        } else {
1905            Err(crate::ClientError::not_started())
1906        }
1907    }
1908
1909    async fn update_acl(
1910        &self,
1911        broker_addr: CheetahString,
1912        subject: CheetahString,
1913        resources: Vec<CheetahString>,
1914        actions: Vec<CheetahString>,
1915        source_ips: Vec<CheetahString>,
1916        decision: CheetahString,
1917    ) -> crate::ClientResult<()> {
1918        let acl_info = build_acl_info(subject, resources, actions, source_ips, decision);
1919        if let Some(ref mq_client_instance) = self.client_instance {
1920            mq_client_instance
1921                .get_mq_client_api_impl()?
1922                .update_acl(broker_addr, &acl_info, self.remoting_timeout_millis()?)
1923                .await
1924        } else {
1925            Err(crate::ClientError::not_started())
1926        }
1927    }
1928
1929    async fn delete_acl_entry(
1930        &self,
1931        broker_addr: CheetahString,
1932        subject: CheetahString,
1933        policy_type: CheetahString,
1934        resource: CheetahString,
1935    ) -> crate::ClientResult<()> {
1936        if let Some(ref client_instance) = self.client_instance {
1937            client_instance
1938                .get_mq_client_api_impl()?
1939                .delete_acl_entry(
1940                    broker_addr,
1941                    subject,
1942                    policy_type,
1943                    resource,
1944                    self.remoting_timeout_millis()?,
1945                )
1946                .await
1947        } else {
1948            Err(crate::ClientError::not_started())
1949        }
1950    }
1951
1952    async fn delete_acl(
1953        &self,
1954        broker_addr: CheetahString,
1955        subject: CheetahString,
1956        resource: CheetahString,
1957    ) -> crate::ClientResult<()> {
1958        if let Some(ref client_instance) = self.client_instance {
1959            let mq_client_api = client_instance.get_mq_client_api_impl()?;
1960            mq_client_api
1961                .delete_acl(broker_addr, subject, resource, self.remoting_timeout_millis()?)
1962                .await
1963        } else {
1964            Err(crate::ClientError::not_started())
1965        }
1966    }
1967
1968    async fn get_user_subscription_group(
1969        &self,
1970        broker_addr: CheetahString,
1971        timeout_millis: u64,
1972    ) -> crate::ClientResult<SubscriptionGroupWrapper> {
1973        let mut subscription_group_wrapper = self.get_all_subscription_group(broker_addr, timeout_millis).await?;
1974
1975        let system_group_set = get_system_group_set();
1976        let table = subscription_group_wrapper.get_subscription_group_table_mut();
1977        // Remove system consumer groups
1978        table.retain(|key, _| !mix_all::is_sys_consumer_group(key.as_str()) && !system_group_set.contains(key));
1979
1980        Ok(subscription_group_wrapper)
1981    }
1982
1983    async fn create_user_with_info(
1984        &self,
1985        broker_addr: CheetahString,
1986        username: CheetahString,
1987        password: CheetahString,
1988    ) -> crate::ClientResult<()> {
1989        let user_info = UserInfo {
1990            username: Some(username),
1991            password: Some(password),
1992            user_type: None,
1993            user_status: None,
1994        };
1995
1996        self.mq_client_api()?
1997            .create_user(broker_addr, &user_info, self.remoting_timeout_millis()?)
1998            .await
1999    }
2000
2001    async fn update_user_with_info(
2002        &self,
2003        broker_addr: CheetahString,
2004        username: CheetahString,
2005        password: CheetahString,
2006    ) -> crate::ClientResult<()> {
2007        let user_info = UserInfo {
2008            username: Some(username),
2009            password: Some(password),
2010            user_type: None,
2011            user_status: None,
2012        };
2013
2014        self.mq_client_api()?
2015            .update_user(broker_addr, &user_info, self.remoting_timeout_millis()?)
2016            .await
2017    }
2018
2019    async fn get_user(
2020        &self,
2021        broker_addr: CheetahString,
2022        username: CheetahString,
2023    ) -> crate::ClientResult<Option<UserInfo>> {
2024        if let Some(ref mq_client_instance) = self.client_instance {
2025            let mq_client_api = mq_client_instance.get_mq_client_api_impl()?;
2026            let timeout_millis = self.remoting_timeout_millis()?;
2027            let result = mq_client_api.get_user(broker_addr, username, timeout_millis).await?;
2028            Ok(result)
2029        } else {
2030            Err(crate::ClientError::not_started())
2031        }
2032    }
2033
2034    async fn list_users(
2035        &self,
2036        broker_addr: CheetahString,
2037        filter: CheetahString,
2038    ) -> crate::ClientResult<Vec<UserInfo>> {
2039        if let Some(ref mq_client_instance) = self.client_instance {
2040            let mq_client_api = mq_client_instance.get_mq_client_api_impl()?;
2041            let timeout_millis = self.remoting_timeout_millis()?;
2042            let result = mq_client_api.list_users(broker_addr, filter, timeout_millis).await?;
2043            Ok(result)
2044        } else {
2045            Err(crate::ClientError::not_started())
2046        }
2047    }
2048
2049    async fn create_acl_with_info(
2050        &self,
2051        broker_addr: CheetahString,
2052        subject: CheetahString,
2053    ) -> crate::ClientResult<()> {
2054        self.create_acl_with_acl_info(
2055            broker_addr,
2056            AclInfo {
2057                subject: Some(subject),
2058                policies: None,
2059            },
2060        )
2061        .await
2062    }
2063
2064    async fn update_acl_with_info(
2065        &self,
2066        broker_addr: CheetahString,
2067        subject: CheetahString,
2068    ) -> crate::ClientResult<()> {
2069        self.update_acl_with_acl_info(
2070            broker_addr,
2071            AclInfo {
2072                subject: Some(subject),
2073                policies: None,
2074            },
2075        )
2076        .await
2077    }
2078
2079    async fn get_acl(&self, broker_addr: CheetahString, subject: CheetahString) -> crate::ClientResult<AclInfo> {
2080        let acl_infos = self
2081            .list_acl(broker_addr.clone(), subject.clone(), CheetahString::default())
2082            .await?;
2083        acl_infos
2084            .into_iter()
2085            .find(|acl_info| acl_info.subject.as_ref() == Some(&subject))
2086            .ok_or_else(|| {
2087                ClientError::illegal_argument(format!(
2088                    "ACL with subject {} was not found on broker {}",
2089                    subject, broker_addr
2090                ))
2091            })
2092    }
2093
2094    async fn list_acl(
2095        &self,
2096        broker_addr: CheetahString,
2097        subject_filter: CheetahString,
2098        resource_filter: CheetahString,
2099    ) -> crate::ClientResult<Vec<AclInfo>> {
2100        if let Some(ref mq_client_instance) = self.client_instance {
2101            let mq_client_api = mq_client_instance.get_mq_client_api_impl()?;
2102            let timeout_millis = self.remoting_timeout_millis()?;
2103            let result = mq_client_api
2104                .list_acl(broker_addr, subject_filter, resource_filter, timeout_millis)
2105                .await?;
2106            Ok(result)
2107        } else {
2108            Err(crate::ClientError::not_started())
2109        }
2110    }
2111}
2112
2113#[allow(unused_variables, reason = "trait contracts reserve parameters")]
2114#[allow(unused_mut)]
2115impl OffsetAdmin for DefaultMQAdminExtImpl {
2116    async fn reset_offset_by_timestamp(
2117        &self,
2118        cluster_name: Option<CheetahString>,
2119        topic: CheetahString,
2120        group: CheetahString,
2121        timestamp: u64,
2122        is_force: bool,
2123    ) -> crate::ClientResult<HashMap<MessageQueue, u64>> {
2124        let timestamp = timestamp_to_java_long("resetOffsetByTimestamp", timestamp)?;
2125        let topic_route = self.examine_topic_route_info(topic.clone()).await?;
2126        let mut offset_table = HashMap::new();
2127        let timeout = self.remoting_timeout_millis()?;
2128
2129        if let Some(route_data) = topic_route {
2130            for broker_data in &route_data.broker_datas {
2131                if let Some(expected_cluster) = cluster_name.as_ref() {
2132                    if broker_data.cluster() != expected_cluster {
2133                        continue;
2134                    }
2135                }
2136                if let Some(master_addr) = broker_data.broker_addrs().get(&mix_all::MASTER_ID) {
2137                    let request_header = ResetOffsetRequestHeader {
2138                        topic: topic.clone(),
2139                        group: group.clone(),
2140                        queue_id: -1,
2141                        offset: Some(-1),
2142                        timestamp,
2143                        is_force,
2144                        topic_request_header: None,
2145                    };
2146                    let offsets = self
2147                        .mq_client_api()?
2148                        .invoke_broker_to_reset_offset(master_addr, request_header, timeout)
2149                        .await?;
2150                    for (mq, offset) in offsets {
2151                        offset_table.insert(mq, java_long_to_u64("resetOffsetByTimestamp", "offset", offset)?);
2152                    }
2153                }
2154            }
2155        }
2156
2157        Ok(offset_table)
2158    }
2159
2160    async fn reset_offset_new(
2161        &self,
2162        consumer_group: CheetahString,
2163        topic: CheetahString,
2164        timestamp: u64,
2165    ) -> crate::ClientResult<()> {
2166        match self
2167            .reset_offset_by_timestamp(None, topic.clone(), consumer_group.clone(), timestamp, true)
2168            .await
2169        {
2170            Ok(_) => Ok(()),
2171            Err(error) if is_consumer_not_online_error(&error) => {
2172                self.reset_offset_by_timestamp_old(None, consumer_group, topic, timestamp, true)
2173                    .await?;
2174                Ok(())
2175            }
2176            Err(error) => Err(error),
2177        }
2178    }
2179
2180    async fn delete_expired_commit_log(
2181        &self,
2182        cluster: Option<CheetahString>,
2183        addr: Option<CheetahString>,
2184    ) -> crate::ClientResult<bool> {
2185        self.execute_broker_cleanup_operation(cluster, addr, BrokerCleanupOperation::DeleteExpiredCommitLog)
2186            .await
2187    }
2188
2189    async fn delete_expired_commit_log_by_addr(&self, addr: CheetahString) -> crate::ClientResult<bool> {
2190        self.delete_expired_commit_log(None, Some(addr)).await
2191    }
2192
2193    async fn clone_group_offset(
2194        &self,
2195        src_group: CheetahString,
2196        dest_group: CheetahString,
2197        topic: CheetahString,
2198        is_offline: bool,
2199    ) -> crate::ClientResult<()> {
2200        let retry_topic: CheetahString = mix_all::get_retry_topic(src_group.as_str()).into();
2201        let topic_route_data = self
2202            .examine_topic_route_info(retry_topic.clone())
2203            .await?
2204            .ok_or_else(|| mq_client_err!(format!("Topic route not found for retry topic: {retry_topic}")))?;
2205        let timeout = self.remoting_timeout_millis()?;
2206        let api = self.mq_client_api()?;
2207
2208        for broker_data in &topic_route_data.broker_datas {
2209            if let Some(addr) = broker_data.select_broker_addr() {
2210                api.clone_group_offset(
2211                    &addr,
2212                    src_group.clone(),
2213                    dest_group.clone(),
2214                    topic.clone(),
2215                    is_offline,
2216                    timeout,
2217                )
2218                .await?;
2219            }
2220        }
2221
2222        Ok(())
2223    }
2224
2225    async fn update_consume_offset(
2226        &self,
2227        broker_addr: CheetahString,
2228        consume_group: CheetahString,
2229        mq: MessageQueue,
2230        offset: u64,
2231    ) -> crate::ClientResult<()> {
2232        let request_header = update_consume_offset_request_header(consume_group, &mq, offset)?;
2233        let client_instance = self.client_instance.as_ref().ok_or(crate::ClientError::not_started())?;
2234
2235        client_instance
2236            .get_mq_client_api_impl()?
2237            .update_consumer_offset(&broker_addr, request_header, self.remoting_timeout_millis()?)
2238            .await
2239    }
2240
2241    async fn reset_offset_by_queue_id(
2242        &self,
2243        broker_addr: CheetahString,
2244        consumer_group: CheetahString,
2245        topic_name: CheetahString,
2246        queue_id: i32,
2247        reset_offset: u64,
2248    ) -> crate::ClientResult<()> {
2249        let (update_header, reset_header) =
2250            reset_offset_by_queue_id_request_headers(consumer_group, topic_name, queue_id, reset_offset)?;
2251        let client_instance = self.client_instance.as_ref().ok_or(crate::ClientError::not_started())?;
2252        let timeout_millis = self.remoting_timeout_millis()?;
2253
2254        client_instance
2255            .get_mq_client_api_impl()?
2256            .update_consumer_offset(&broker_addr, update_header, timeout_millis)
2257            .await?;
2258
2259        let offsets = client_instance
2260            .get_mq_client_api_impl()?
2261            .invoke_broker_to_reset_offset(&broker_addr, reset_header, timeout_millis)
2262            .await?;
2263
2264        for (mq, old_offset) in offsets {
2265            info!(
2266                "Reset single message queue {} offset from {} to {}",
2267                mq, old_offset, reset_offset
2268            );
2269        }
2270
2271        Ok(())
2272    }
2273
2274    async fn reset_master_flush_offset(
2275        &self,
2276        broker_addr: CheetahString,
2277        master_flush_offset: u64,
2278    ) -> crate::ClientResult<()> {
2279        if let Some(ref mq_client_instance) = self.client_instance {
2280            let master_flush_offset = master_flush_offset_to_java_long(master_flush_offset)?;
2281            mq_client_instance
2282                .get_mq_client_api_impl()?
2283                .reset_master_flush_offset(&broker_addr, master_flush_offset)
2284                .await
2285        } else {
2286            Err(crate::ClientError::not_started())
2287        }
2288    }
2289
2290    async fn set_commit_log_read_ahead_mode(
2291        &self,
2292        broker_addr: CheetahString,
2293        mode: CheetahString,
2294    ) -> crate::ClientResult<CheetahString> {
2295        self.mq_client_api()?
2296            .set_commit_log_read_ahead_mode(broker_addr, mode, self.remoting_timeout_millis()?)
2297            .await
2298    }
2299
2300    async fn update_lite_pull_consumer_offset(
2301        &self,
2302        addr: CheetahString,
2303        topic: CheetahString,
2304        group: CheetahString,
2305        queue_id: i32,
2306        offset: u64,
2307    ) -> crate::ClientResult<()> {
2308        let request_header = lite_pull_update_consumer_offset_request_header(topic, group, queue_id, offset)?;
2309        self.mq_client_api()?
2310            .update_consumer_offset(&addr, request_header, self.remoting_timeout_millis()?)
2311            .await
2312    }
2313
2314    async fn export_rocksdb_consumer_offset_to_json(
2315        &self,
2316        broker_addr: CheetahString,
2317        file_path: CheetahString,
2318    ) -> crate::ClientResult<()> {
2319        if !file_path.is_empty() {
2320            return Err(ClientError::illegal_argument(
2321                "exportRocksDB consumerOffsets filePath is local-mode only and cannot be sent over RPC",
2322            ));
2323        }
2324
2325        self.mq_client_api()?
2326            .export_rocksdb_config_to_json(
2327                broker_addr,
2328                vec![CheetahString::from_static_str(ROCKSDB_CONFIG_TYPE_CONSUMER_OFFSETS)],
2329                self.remoting_timeout_millis()?,
2330            )
2331            .await
2332    }
2333
2334    async fn export_rocksdb_consumer_offset_from_memory(
2335        &self,
2336        broker_addr: CheetahString,
2337    ) -> crate::ClientResult<CheetahString> {
2338        self.mq_client_api()?
2339            .get_all_consumer_offset_json(broker_addr, self.remoting_timeout_millis()?)
2340            .await
2341    }
2342
2343    async fn reset_offset_by_timestamp_old(
2344        &self,
2345        cluster_name: Option<CheetahString>,
2346        consumer_group: CheetahString,
2347        topic: CheetahString,
2348        timestamp: u64,
2349        force: bool,
2350    ) -> crate::ClientResult<Vec<RollbackStats>> {
2351        let timestamp = timestamp_to_java_long("resetOffsetByTimestampOld", timestamp)?;
2352        let mut route_topic = topic.clone();
2353        if !topic.is_empty()
2354            && (mix_all::is_lmq(Some(topic.as_str()))
2355                || topic.as_str() == format!("{}wheel_timer", TopicValidator::SYSTEM_TOPIC_PREFIX))
2356            && cluster_name.as_ref().is_some_and(|name| !name.is_empty())
2357        {
2358            if let Some(cluster_name) = cluster_name {
2359                route_topic = cluster_name;
2360            }
2361        }
2362        let topic_route_data = self.examine_topic_route_info(route_topic).await?;
2363        let mut rollback_stats_list = Vec::new();
2364
2365        if let Some(route_data) = topic_route_data {
2366            let mut topic_route_map = HashMap::new();
2367            for queue_data in &route_data.queue_datas {
2368                topic_route_map.insert(queue_data.broker_name().to_string(), queue_data.clone());
2369            }
2370
2371            for broker_data in &route_data.broker_datas {
2372                if let Some(addr) = broker_data.select_broker_addr() {
2373                    if let Some(queue_data) = topic_route_map.get(broker_data.broker_name().as_str()) {
2374                        let mut rollback_stats = self
2375                            .reset_offset_by_timestamp_old_on_broker(
2376                                addr,
2377                                queue_data,
2378                                consumer_group.clone(),
2379                                topic.clone(),
2380                                timestamp,
2381                                force,
2382                            )
2383                            .await?;
2384                        rollback_stats_list.append(&mut rollback_stats);
2385                    }
2386                }
2387            }
2388        }
2389
2390        Ok(rollback_stats_list)
2391    }
2392    #[allow(deprecated)]
2393    async fn reset_offset_new_concurrent(
2394        &self,
2395        group: CheetahString,
2396        topic: CheetahString,
2397        timestamp: u64,
2398    ) -> AdminToolResult<BrokerOperatorResult> {
2399        let timestamp = match timestamp_to_java_long("resetOffsetNewConcurrent", timestamp) {
2400            Ok(timestamp) => timestamp,
2401            Err(error) => return AdminToolResult::failure(admin_result_code_for_error(&error), error.to_string()),
2402        };
2403        let api = match self.mq_client_api() {
2404            Ok(api) => api,
2405            Err(error) => return AdminToolResult::failure(admin_result_code_for_error(&error), error.to_string()),
2406        };
2407        let timeout = match self.remoting_timeout_millis() {
2408            Ok(timeout) => timeout,
2409            Err(error) => return AdminToolResult::failure(admin_result_code_for_error(&error), error.to_string()),
2410        };
2411        let route_data = match api.get_topic_route_info_from_name_server(&topic, timeout).await {
2412            Ok(Some(route_data)) if !route_data.broker_datas.is_empty() => route_data,
2413            Ok(_) => {
2414                return AdminToolResult::failure(
2415                    AdminToolsResultCodeEnum::TopicRouteInfoNotExist,
2416                    "topic router info not found".to_string(),
2417                );
2418            }
2419            Err(error) => return AdminToolResult::failure(admin_result_code_for_error(&error), error.to_string()),
2420        };
2421
2422        let mut topic_route_map = HashMap::new();
2423        for queue_data in &route_data.queue_datas {
2424            topic_route_map.insert(queue_data.broker_name().clone(), queue_data.clone());
2425        }
2426
2427        let mut success_list = Vec::new();
2428        let mut failure_list = Vec::new();
2429        for broker_data in &route_data.broker_datas {
2430            let Some(addr) = broker_data.select_broker_addr() else {
2431                continue;
2432            };
2433
2434            let reset_header = ResetOffsetRequestHeader {
2435                topic: topic.clone(),
2436                group: group.clone(),
2437                queue_id: -1,
2438                offset: Some(-1),
2439                timestamp,
2440                is_force: true,
2441                topic_request_header: None,
2442            };
2443
2444            match api.invoke_broker_to_reset_offset(&addr, reset_header, timeout).await {
2445                Ok(_) => success_list.push(addr),
2446                Err(error) if is_consumer_not_online_error(&error) => {
2447                    match topic_route_map.get(broker_data.broker_name()) {
2448                        Some(queue_data) => {
2449                            match self
2450                                .reset_offset_by_timestamp_old_on_broker(
2451                                    addr.clone(),
2452                                    queue_data,
2453                                    group.clone(),
2454                                    topic.clone(),
2455                                    timestamp,
2456                                    true,
2457                                )
2458                                .await
2459                            {
2460                                Ok(_) => success_list.push(addr),
2461                                Err(error) => {
2462                                    warn!(
2463                                        "resetOffsetByTimestampOld error. addr={}, topic={}, group={}, timestamp={}, \
2464                                         {}",
2465                                        addr, topic, group, timestamp, error
2466                                    );
2467                                    failure_list.push(addr);
2468                                }
2469                            }
2470                        }
2471                        None => {
2472                            warn!(
2473                                "resetOffsetByTimestampOld error. addr={}, topic={}, group={}, timestamp={}, missing \
2474                                 queue data for broker {}",
2475                                addr,
2476                                topic,
2477                                group,
2478                                timestamp,
2479                                broker_data.broker_name()
2480                            );
2481                            failure_list.push(addr);
2482                        }
2483                    }
2484                }
2485                Err(error) if response_code_from_error(&error) == Some(ResponseCode::SystemError) => {
2486                    success_list.push(addr);
2487                }
2488                Err(error) => {
2489                    warn!(
2490                        "resetOffsetNewConcurrent error. addr={}, topic={}, group={}, timestamp={}, {}",
2491                        addr, topic, group, timestamp, error
2492                    );
2493                    failure_list.push(addr);
2494                }
2495            }
2496        }
2497
2498        let success = success_list.len() == route_data.broker_datas.len();
2499        let result = broker_operator_result(success_list, failure_list);
2500        if success {
2501            AdminToolResult::success(result)
2502        } else {
2503            AdminToolResult::failure_with_data(
2504                AdminToolsResultCodeEnum::MQBrokerError,
2505                "operator failure".into(),
2506                result,
2507            )
2508        }
2509    }
2510
2511    async fn query_consume_time_span(
2512        &self,
2513        topic: CheetahString,
2514        group: CheetahString,
2515    ) -> crate::ClientResult<Vec<QueueTimeSpan>> {
2516        let timeout = self.remoting_timeout_millis()?;
2517        let mut result = Vec::new();
2518        if let Some(route_data) = self.examine_topic_route_info(topic.clone()).await? {
2519            for broker_data in &route_data.broker_datas {
2520                if let Some(master_addr) = broker_data.broker_addrs().get(&mix_all::MASTER_ID) {
2521                    let spans = self
2522                        .mq_client_api()?
2523                        .query_consume_time_span(
2524                            master_addr,
2525                            QueryConsumeTimeSpanRequestHeader {
2526                                topic: topic.clone(),
2527                                group: group.clone(),
2528                                topic_request_header: None,
2529                            },
2530                            timeout,
2531                        )
2532                        .await?;
2533                    result.extend(spans);
2534                }
2535            }
2536        }
2537        Ok(result)
2538    }
2539
2540    async fn query_consume_time_span_concurrent(
2541        &self,
2542        topic: CheetahString,
2543        group: CheetahString,
2544    ) -> AdminToolResult<Vec<QueueTimeSpan>> {
2545        match self.query_consume_time_span(topic, group).await {
2546            Ok(spans) => AdminToolResult::success(spans),
2547            Err(error) => AdminToolResult::failure(admin_result_code_for_error(&error), error.to_string()),
2548        }
2549    }
2550
2551    async fn search_offset(
2552        &self,
2553        broker_addr: CheetahString,
2554        topic_name: CheetahString,
2555        queue_id: i32,
2556        timestamp: u64,
2557        timeout_millis: u64,
2558    ) -> crate::ClientResult<u64> {
2559        let timestamp = search_offset_timestamp_to_java_long(timestamp)?;
2560        let mq = MessageQueue::from_parts(&topic_name, "", queue_id);
2561        let offset = self
2562            .mq_client_api()?
2563            .search_offset_by_timestamp(
2564                broker_addr.as_str(),
2565                &mq,
2566                timestamp,
2567                rocketmq_model::common::boundary_type::BoundaryType::Lower,
2568                timeout_millis,
2569            )
2570            .await?;
2571        java_long_to_u64("searchOffset", "offset", offset)
2572    }
2573
2574    async fn min_offset(
2575        &self,
2576        broker_addr: CheetahString,
2577        message_queue: MessageQueue,
2578        timeout_millis: u64,
2579    ) -> crate::ClientResult<i64> {
2580        self.mq_client_api()?
2581            .get_min_offset(broker_addr.as_str(), &message_queue, timeout_millis)
2582            .await
2583    }
2584
2585    async fn max_offset(
2586        &self,
2587        broker_addr: CheetahString,
2588        message_queue: MessageQueue,
2589        timeout_millis: u64,
2590    ) -> crate::ClientResult<i64> {
2591        self.mq_client_api()?
2592            .get_max_offset(broker_addr.as_str(), &message_queue, timeout_millis)
2593            .await
2594    }
2595}