1use std::collections::HashSet;
16use std::sync::Arc;
17
18use api::v1::meta::store_client::StoreClient;
19use api::v1::meta::{
20 BatchDeleteRequest, BatchDeleteResponse, BatchGetRequest, BatchGetResponse, BatchPutRequest,
21 BatchPutResponse, CompareAndPutRequest, CompareAndPutResponse, DeleteRangeRequest,
22 DeleteRangeResponse, PutRequest, PutResponse, RangeRequest, RangeResponse, Role,
23};
24use common_grpc::channel_manager::ChannelManager;
25use common_telemetry::tracing_context::TracingContext;
26use snafu::{OptionExt, ResultExt, ensure};
27use tokio::sync::RwLock;
28use tonic::codec::CompressionEncoding;
29use tonic::transport::Channel;
30
31use crate::client::{Id, load_balance as lb};
32use crate::error;
33use crate::error::Result;
34
35#[derive(Clone, Debug)]
36pub struct Client {
37 inner: Arc<RwLock<Inner>>,
38}
39
40impl Client {
41 pub fn new(id: Id, role: Role, channel_manager: ChannelManager) -> Self {
42 Self::new_with_read_only(id, role, channel_manager, true)
43 }
44
45 #[cfg(test)]
47 pub(super) fn new_writable(id: Id, role: Role, channel_manager: ChannelManager) -> Self {
48 Self::new_with_read_only(id, role, channel_manager, false)
49 }
50
51 fn new_with_read_only(
52 id: Id,
53 role: Role,
54 channel_manager: ChannelManager,
55 read_only: bool,
56 ) -> Self {
57 let inner = Arc::new(RwLock::new(Inner {
58 id,
59 role,
60 channel_manager,
61 peers: vec![],
62 read_only,
63 }));
64
65 Self { inner }
66 }
67
68 pub async fn start<U, A>(&mut self, urls: A) -> Result<()>
69 where
70 U: AsRef<str>,
71 A: AsRef<[U]>,
72 {
73 let mut inner = self.inner.write().await;
74 inner.start(urls).await
75 }
76
77 pub async fn range(&self, req: RangeRequest) -> Result<RangeResponse> {
78 let inner = self.inner.read().await;
79 inner.range(req).await
80 }
81
82 pub async fn put(&self, req: PutRequest) -> Result<PutResponse> {
83 let inner = self.inner.read().await;
84 inner.put(req).await
85 }
86
87 pub async fn batch_get(&self, req: BatchGetRequest) -> Result<BatchGetResponse> {
88 let inner = self.inner.read().await;
89 inner.batch_get(req).await
90 }
91
92 pub async fn batch_put(&self, req: BatchPutRequest) -> Result<BatchPutResponse> {
93 let inner = self.inner.read().await;
94 inner.batch_put(req).await
95 }
96
97 pub async fn batch_delete(&self, req: BatchDeleteRequest) -> Result<BatchDeleteResponse> {
98 let inner = self.inner.read().await;
99 inner.batch_delete(req).await
100 }
101
102 pub async fn compare_and_put(
103 &self,
104 req: CompareAndPutRequest,
105 ) -> Result<CompareAndPutResponse> {
106 let inner = self.inner.read().await;
107 inner.compare_and_put(req).await
108 }
109
110 pub async fn delete_range(&self, req: DeleteRangeRequest) -> Result<DeleteRangeResponse> {
111 let inner = self.inner.read().await;
112 inner.delete_range(req).await
113 }
114}
115
116#[derive(Debug)]
117struct Inner {
118 id: Id,
119 role: Role,
120 channel_manager: ChannelManager,
121 peers: Vec<String>,
122 read_only: bool,
123}
124
125impl Inner {
126 async fn start<U, A>(&mut self, urls: A) -> Result<()>
127 where
128 U: AsRef<str>,
129 A: AsRef<[U]>,
130 {
131 ensure!(
132 !self.is_started(),
133 error::IllegalGrpcClientStateSnafu {
134 err_msg: "Store client already started",
135 }
136 );
137
138 self.peers = urls
139 .as_ref()
140 .iter()
141 .map(|url| url.as_ref().to_string())
142 .collect::<HashSet<_>>()
143 .drain()
144 .collect::<Vec<_>>();
145
146 Ok(())
147 }
148
149 async fn range(&self, mut req: RangeRequest) -> Result<RangeResponse> {
150 let mut client = self.random_client()?;
151 req.set_header(
152 self.id,
153 self.role,
154 TracingContext::from_current_span().to_w3c(),
155 );
156 let res = client.range(req).await.map_err(error::Error::from)?;
157
158 Ok(res.into_inner())
159 }
160
161 async fn put(&self, mut req: PutRequest) -> Result<PutResponse> {
162 self.ensure_writable()?;
163
164 let mut client = self.random_client()?;
165 req.set_header(
166 self.id,
167 self.role,
168 TracingContext::from_current_span().to_w3c(),
169 );
170 let res = client.put(req).await.map_err(error::Error::from)?;
171
172 Ok(res.into_inner())
173 }
174
175 async fn batch_get(&self, mut req: BatchGetRequest) -> Result<BatchGetResponse> {
176 let mut client = self.random_client()?;
177 req.set_header(
178 self.id,
179 self.role,
180 TracingContext::from_current_span().to_w3c(),
181 );
182
183 let res = client.batch_get(req).await.map_err(error::Error::from)?;
184
185 Ok(res.into_inner())
186 }
187
188 async fn batch_put(&self, mut req: BatchPutRequest) -> Result<BatchPutResponse> {
189 self.ensure_writable()?;
190
191 let mut client = self.random_client()?;
192 req.set_header(
193 self.id,
194 self.role,
195 TracingContext::from_current_span().to_w3c(),
196 );
197 let res = client.batch_put(req).await.map_err(error::Error::from)?;
198
199 Ok(res.into_inner())
200 }
201
202 async fn batch_delete(&self, mut req: BatchDeleteRequest) -> Result<BatchDeleteResponse> {
203 self.ensure_writable()?;
204
205 let mut client = self.random_client()?;
206 req.set_header(
207 self.id,
208 self.role,
209 TracingContext::from_current_span().to_w3c(),
210 );
211 let res = client.batch_delete(req).await.map_err(error::Error::from)?;
212
213 Ok(res.into_inner())
214 }
215
216 async fn compare_and_put(
217 &self,
218 mut req: CompareAndPutRequest,
219 ) -> Result<CompareAndPutResponse> {
220 self.ensure_writable()?;
221
222 let mut client = self.random_client()?;
223 req.set_header(
224 self.id,
225 self.role,
226 TracingContext::from_current_span().to_w3c(),
227 );
228 let res = client
229 .compare_and_put(req)
230 .await
231 .map_err(error::Error::from)?;
232
233 Ok(res.into_inner())
234 }
235
236 async fn delete_range(&self, mut req: DeleteRangeRequest) -> Result<DeleteRangeResponse> {
237 self.ensure_writable()?;
238
239 let mut client = self.random_client()?;
240 req.set_header(
241 self.id,
242 self.role,
243 TracingContext::from_current_span().to_w3c(),
244 );
245 let res = client.delete_range(req).await.map_err(error::Error::from)?;
246
247 Ok(res.into_inner())
248 }
249
250 fn random_client(&self) -> Result<StoreClient<Channel>> {
251 let len = self.peers.len();
252 let peer = lb::random_get(len, |i| Some(&self.peers[i])).context(
253 error::IllegalGrpcClientStateSnafu {
254 err_msg: "Empty peers, store client may not start yet",
255 },
256 )?;
257
258 self.make_client(peer)
259 }
260
261 fn ensure_writable(&self) -> Result<()> {
262 if self.read_only {
263 return error::ReadOnlyKvBackendSnafu {
264 name: "MetaClient Store".to_string(),
265 }
266 .fail();
267 }
268
269 Ok(())
270 }
271
272 fn make_client(&self, addr: impl AsRef<str>) -> Result<StoreClient<Channel>> {
273 let channel = self
274 .channel_manager
275 .get(addr)
276 .context(error::CreateChannelSnafu)?;
277
278 let max_decoding_message_size = self
279 .channel_manager
280 .config()
281 .max_recv_message_size
282 .as_bytes() as usize;
283
284 Ok(StoreClient::new(channel)
285 .accept_compressed(CompressionEncoding::Gzip)
286 .accept_compressed(CompressionEncoding::Zstd)
287 .send_compressed(CompressionEncoding::Zstd)
288 .max_decoding_message_size(max_decoding_message_size))
289 }
290
291 #[inline]
292 fn is_started(&self) -> bool {
293 !self.peers.is_empty()
294 }
295}
296
297#[cfg(test)]
298mod test {
299 use super::*;
300
301 #[tokio::test]
302 async fn test_already_start() {
303 let mut client = Client::new(0, Role::Frontend, ChannelManager::default());
304 client
305 .start(&["127.0.0.1:1000", "127.0.0.1:1001"])
306 .await
307 .unwrap();
308 let res = client.start(&["127.0.0.1:1002"]).await;
309 assert!(res.is_err());
310 assert!(matches!(
311 res.err(),
312 Some(error::Error::IllegalGrpcClientState { .. })
313 ));
314 }
315
316 #[tokio::test]
317 async fn test_start_with_duplicate_peers() {
318 let mut client = Client::new(0, Role::Frontend, ChannelManager::default());
319 client
320 .start(&["127.0.0.1:1000", "127.0.0.1:1000", "127.0.0.1:1000"])
321 .await
322 .unwrap();
323 assert_eq!(1, client.inner.write().await.peers.len());
324 }
325
326 #[tokio::test]
327 async fn test_read_only_store_rejects_writes_before_rpc() {
328 let client = Client::new(0, Role::Frontend, ChannelManager::default());
329
330 fn assert_read_only<T>(result: Result<T>) {
331 assert!(matches!(
332 result,
333 Err(error::Error::ReadOnlyKvBackend { .. })
334 ));
335 }
336
337 assert_read_only(client.put(PutRequest::default()).await);
338 assert_read_only(client.batch_put(BatchPutRequest::default()).await);
339 assert_read_only(client.batch_delete(BatchDeleteRequest::default()).await);
340 assert_read_only(
341 client
342 .compare_and_put(CompareAndPutRequest::default())
343 .await,
344 );
345 assert_read_only(client.delete_range(DeleteRangeRequest::default()).await);
346 }
347}