log_store/kafka/
client_manager.rs1use std::collections::HashMap;
16use std::sync::Arc;
17
18use common_wal::config::kafka::DatanodeKafkaConfig;
19use common_wal::config::kafka::common::DEFAULT_BACKOFF_CONFIG;
20use dashmap::DashMap;
21use rskafka::client::ClientBuilder;
22use rskafka::client::partition::{Compression, PartitionClient, UnknownTopicHandling};
23use snafu::ResultExt;
24use store_api::logstore::provider::KafkaProvider;
25use tokio::sync::{Mutex, RwLock};
26
27use crate::error::{BuildClientSnafu, BuildPartitionClientSnafu, Result, TlsConfigSnafu};
28use crate::kafka::log_store::TopicStat;
29use crate::kafka::producer::{OrderedBatchProducer, OrderedBatchProducerRef};
30
31pub const DEFAULT_PARTITION: i32 = 0;
34
35pub(crate) type ClientManagerRef = Arc<ClientManager>;
37
38#[derive(Debug, Clone)]
40pub(crate) struct Client {
41 client: Arc<PartitionClient>,
42 producer: OrderedBatchProducerRef,
43}
44
45impl Client {
46 pub(crate) fn client(&self) -> &Arc<PartitionClient> {
47 &self.client
48 }
49
50 pub(crate) fn producer(&self) -> &OrderedBatchProducerRef {
51 &self.producer
52 }
53}
54
55#[derive(Debug)]
57pub(crate) struct ClientManager {
58 client: rskafka::client::Client,
59 mutex: Mutex<()>,
61 instances: RwLock<HashMap<Arc<KafkaProvider>, Client>>,
62
63 flush_batch_size: usize,
64 compression: Compression,
65
66 topic_stats: Arc<DashMap<Arc<KafkaProvider>, TopicStat>>,
68}
69
70impl ClientManager {
71 pub(crate) async fn try_new(
73 config: &DatanodeKafkaConfig,
74 topic_stats: Arc<DashMap<Arc<KafkaProvider>, TopicStat>>,
75 ) -> Result<Self> {
76 let mut builder = ClientBuilder::new(config.connection.broker_endpoints.clone())
78 .backoff_config(DEFAULT_BACKOFF_CONFIG)
79 .connect_timeout(Some(config.connection.connect_timeout))
80 .timeout(Some(config.connection.timeout));
81 if let Some(sasl) = &config.connection.sasl {
82 builder = builder.sasl_config(sasl.config.clone().into_sasl_config());
83 };
84 if let Some(tls) = &config.connection.tls {
85 builder = builder.tls_config(tls.to_tls_config().await.context(TlsConfigSnafu)?)
86 };
87
88 let client = builder.build().await.with_context(|_| BuildClientSnafu {
89 broker_endpoints: config.connection.broker_endpoints.clone(),
90 })?;
91
92 Ok(Self {
93 client,
94 mutex: Mutex::new(()),
95 instances: RwLock::new(HashMap::new()),
96 flush_batch_size: config.max_batch_bytes.as_bytes() as usize,
97 compression: Compression::Lz4,
98 topic_stats,
99 })
100 }
101
102 async fn try_insert(&self, provider: &Arc<KafkaProvider>) -> Result<Client> {
103 let _guard = self.mutex.lock().await;
104
105 let client = self.instances.read().await.get(provider).cloned();
106 match client {
107 Some(client) => Ok(client),
108 None => {
109 let client = self.try_create_client(provider).await?;
110 self.instances
111 .write()
112 .await
113 .insert(provider.clone(), client.clone());
114 self.topic_stats
115 .insert(provider.clone(), TopicStat::default());
116 Ok(client)
117 }
118 }
119 }
120
121 pub(crate) async fn get_or_insert(&self, provider: &Arc<KafkaProvider>) -> Result<Client> {
124 let client = self.instances.read().await.get(provider).cloned();
125 match client {
126 Some(client) => Ok(client),
127 None => self.try_insert(provider).await,
128 }
129 }
130
131 async fn try_create_client(&self, provider: &Arc<KafkaProvider>) -> Result<Client> {
132 let client = self
136 .client
137 .partition_client(
138 provider.topic.as_str(),
139 DEFAULT_PARTITION,
140 UnknownTopicHandling::Retry,
141 )
142 .await
143 .context(BuildPartitionClientSnafu {
144 topic: &provider.topic,
145 partition: DEFAULT_PARTITION,
146 })
147 .map(Arc::new)?;
148
149 let (tx, rx) = OrderedBatchProducer::channel();
150 let producer = Arc::new(OrderedBatchProducer::new(
151 (tx, rx),
152 provider.clone(),
153 client.clone(),
154 self.compression,
155 self.flush_batch_size,
156 self.topic_stats.clone(),
157 ));
158
159 Ok(Client { client, producer })
160 }
161
162 pub(crate) async fn list_topics(&self) -> Vec<Arc<KafkaProvider>> {
164 self.instances.read().await.keys().cloned().collect()
165 }
166
167 #[cfg(test)]
168 pub(crate) fn topic_stats(&self) -> &Arc<DashMap<Arc<KafkaProvider>, TopicStat>> {
169 &self.topic_stats
170 }
171}
172
173#[cfg(test)]
174impl ClientManager {
175 pub(crate) fn controller_client(&self) -> rskafka::client::controller::ControllerClient {
177 self.client.controller_client().unwrap()
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use common_wal::maybe_skip_kafka_integration_test;
184 use common_wal::test_util::get_kafka_endpoints;
185 use tokio::sync::Barrier;
186
187 use super::*;
188 use crate::kafka::test_util::prepare;
189
190 #[tokio::test]
192 async fn test_sequential() {
193 maybe_skip_kafka_integration_test!();
194 let broker_endpoints = get_kafka_endpoints();
195 let (manager, topics) = prepare("test_sequential", 128, broker_endpoints).await;
196 let region_topic = (0..512)
198 .map(|region_id| (region_id, &topics[region_id % topics.len()]))
199 .collect::<HashMap<_, _>>();
200
201 for (_, topic) in region_topic {
203 let provider = Arc::new(KafkaProvider::new(topic.clone()));
204 manager.get_or_insert(&provider).await.unwrap();
205 }
206
207 let client_pool = manager.instances.read().await;
209 let all_exist = topics.iter().all(|topic| {
210 let provider = Arc::new(KafkaProvider::new(topic.clone()));
211 client_pool.contains_key(&provider)
212 });
213 assert!(all_exist);
214 }
215
216 #[tokio::test(flavor = "multi_thread")]
218 async fn test_parallel() {
219 maybe_skip_kafka_integration_test!();
220 let broker_endpoints = get_kafka_endpoints();
221 let (manager, topics) = prepare("test_parallel", 128, broker_endpoints).await;
222 let region_topic = (0..512)
224 .map(|region_id| (region_id, topics[region_id % topics.len()].clone()))
225 .collect::<HashMap<_, _>>();
226
227 let manager = Arc::new(manager);
229 let barrier = Arc::new(Barrier::new(region_topic.len()));
230 let tasks = region_topic
231 .into_values()
232 .map(|topic| {
233 let manager = manager.clone();
234 let barrier = barrier.clone();
235
236 tokio::spawn(async move {
237 barrier.wait().await;
238 let provider = Arc::new(KafkaProvider::new(topic));
239 assert!(manager.get_or_insert(&provider).await.is_ok());
240 })
241 })
242 .collect::<Vec<_>>();
243 futures::future::try_join_all(tasks).await.unwrap();
244
245 let client_pool = manager.instances.read().await;
247 let all_exist = topics.iter().all(|topic| {
248 let provider = Arc::new(KafkaProvider::new(topic.clone()));
249 client_pool.contains_key(&provider)
250 });
251 assert!(all_exist);
252 }
253}