1use std::net::SocketAddr;
33
34use memcache_proto::ResponseBytes as McResponseBytes;
35use ringline::ConnCtx;
36
37use crate::{
38 Client, Error, GetValue, Value, check_error_bytes, encode_add, encode_request, encode_set,
39};
40use memcache_proto::Request as McRequest;
41
42pub struct ShardedConfig {
44 pub servers: Vec<SocketAddr>,
46 pub pool_size: usize,
48 pub connect_timeout_ms: u64,
50 pub tls_server_name: Option<String>,
52}
53
54enum ShardConn {
55 Connected(ConnCtx),
56 Disconnected,
57}
58
59struct Shard {
60 addr: SocketAddr,
61 conns: Vec<ShardConn>,
62 next: usize,
63}
64
65pub struct ShardedClient {
71 shards: Vec<Shard>,
72 ring: ketama::Ring,
73 connect_timeout_ms: u64,
74 tls_server_name: Option<String>,
75}
76
77impl ShardedClient {
78 pub fn new(config: ShardedConfig) -> Self {
80 let pool_size = config.pool_size.max(1);
81
82 let server_ids: Vec<String> = config.servers.iter().map(|a| a.to_string()).collect();
83 let ring = ketama::Ring::build(&server_ids.iter().map(|s| s.as_str()).collect::<Vec<_>>());
84
85 let shards = config
86 .servers
87 .iter()
88 .map(|&addr| {
89 let mut conns = Vec::with_capacity(pool_size);
90 for _ in 0..pool_size {
91 conns.push(ShardConn::Disconnected);
92 }
93 Shard {
94 addr,
95 conns,
96 next: 0,
97 }
98 })
99 .collect();
100
101 Self {
102 shards,
103 ring,
104 connect_timeout_ms: config.connect_timeout_ms,
105 tls_server_name: config.tls_server_name,
106 }
107 }
108
109 pub async fn connect_all(&mut self) -> Result<(), Error> {
111 let opts = self.connect_opts();
112 for shard in &mut self.shards {
113 for slot in &mut shard.conns {
114 let conn = do_connect(shard.addr, &opts).await?;
115 *slot = ShardConn::Connected(conn);
116 }
117 }
118 Ok(())
119 }
120
121 pub fn close_all(&mut self) {
123 for shard in &mut self.shards {
124 for slot in &mut shard.conns {
125 if let ShardConn::Connected(c) = slot {
126 c.close();
127 }
128 *slot = ShardConn::Disconnected;
129 }
130 }
131 }
132
133 pub fn shard_count(&self) -> usize {
135 self.shards.len()
136 }
137
138 fn connect_opts(&self) -> ConnectOpts {
139 ConnectOpts {
140 connect_timeout_ms: self.connect_timeout_ms,
141 tls_server_name: self.tls_server_name.clone(),
142 }
143 }
144
145 pub async fn shard_client(&mut self, index: usize) -> Result<Client, Error> {
154 let opts = self.connect_opts();
155 let shard = &mut self.shards[index];
156 let conn = get_conn(shard, &opts).await?;
157 Ok(Client::new(conn))
158 }
159
160 async fn route_command(
173 &mut self,
174 key: &[u8],
175 encoded: &[u8],
176 ) -> Result<McResponseBytes, Error> {
177 let opts = self.connect_opts();
178 let shard_idx = self.ring.route(key);
179 let shard = &mut self.shards[shard_idx];
180 let size = shard.conns.len();
181
182 for attempt in 0..size {
183 let idx = (shard.next + attempt) % size;
184 let conn = match &shard.conns[idx] {
185 ShardConn::Connected(c) => *c,
186 ShardConn::Disconnected => match do_connect(shard.addr, &opts).await {
187 Ok(c) => {
188 shard.conns[idx] = ShardConn::Connected(c);
189 c
190 }
191 Err(_) => continue,
192 },
193 };
194
195 if conn.send(encoded).is_err() {
196 shard.conns[idx] = ShardConn::Disconnected;
202 conn.close();
203 continue;
204 }
205 match Client::new(conn).read_response().await {
206 Ok(response) => {
207 shard.next = (idx + 1) % size;
208 check_error_bytes(&response)?;
209 return Ok(response);
210 }
211 Err(Error::ConnectionClosed) => {
212 shard.conns[idx] = ShardConn::Disconnected;
213 continue;
214 }
215 Err(e) => return Err(e),
216 }
217 }
218
219 Err(Error::AllConnectionsFailed)
220 }
221
222 fn require_same_shard(&self, keys: &[&[u8]]) -> Result<usize, Error> {
224 let first = self.ring.route(keys[0]);
225 for key in &keys[1..] {
226 if self.ring.route(key) != first {
227 return Err(Error::Memcache(
228 "keys in request don't route to the same shard".into(),
229 ));
230 }
231 }
232 Ok(first)
233 }
234
235 pub async fn get(&mut self, key: impl AsRef<[u8]>) -> Result<Option<Value>, Error> {
239 let key = key.as_ref();
240 let encoded = encode_request(&memcache_proto::Request::get(key))?;
241 let response = self.route_command(key, &encoded).await?;
242 match response {
243 McResponseBytes::Values(mut values) => {
244 if values.is_empty() {
245 Ok(None)
246 } else {
247 let v = values.swap_remove(0);
248 Ok(Some(Value {
249 data: v.data,
250 flags: v.flags,
251 }))
252 }
253 }
254 _ => Err(Error::UnexpectedResponse),
255 }
256 }
257
258 pub async fn gets(&mut self, keys: &[&[u8]]) -> Result<Vec<GetValue>, Error> {
261 if keys.is_empty() {
262 return Ok(Vec::new());
263 }
264 self.require_same_shard(keys)?;
265 let encoded = encode_request(&McRequest::gets(keys))?;
266 let response = self.route_command(keys[0], &encoded).await?;
267 match response {
268 McResponseBytes::Values(values) => Ok(values
269 .into_iter()
270 .map(|v| GetValue {
271 key: v.key,
272 data: v.data,
273 flags: v.flags,
274 cas: v.cas,
275 })
276 .collect()),
277 _ => Err(Error::UnexpectedResponse),
278 }
279 }
280
281 pub async fn set(
283 &mut self,
284 key: impl AsRef<[u8]>,
285 value: impl AsRef<[u8]>,
286 ) -> Result<(), Error> {
287 self.set_with_options(key, value, 0, 0).await
288 }
289
290 pub async fn set_with_options(
292 &mut self,
293 key: impl AsRef<[u8]>,
294 value: impl AsRef<[u8]>,
295 flags: u32,
296 exptime: u32,
297 ) -> Result<(), Error> {
298 let key = key.as_ref();
299 let value = value.as_ref();
300 let encoded = encode_set(key, value, flags, exptime)?;
301 let response = self.route_command(key, &encoded).await?;
302 match response {
303 McResponseBytes::Stored => Ok(()),
304 _ => Err(Error::UnexpectedResponse),
305 }
306 }
307
308 pub async fn add(
311 &mut self,
312 key: impl AsRef<[u8]>,
313 value: impl AsRef<[u8]>,
314 ) -> Result<bool, Error> {
315 let key = key.as_ref();
316 let value = value.as_ref();
317 let encoded = encode_add(key, value)?;
318 let response = self.route_command(key, &encoded).await?;
319 match response {
320 McResponseBytes::Stored => Ok(true),
321 McResponseBytes::NotStored => Ok(false),
322 _ => Err(Error::UnexpectedResponse),
323 }
324 }
325
326 pub async fn replace(
329 &mut self,
330 key: impl AsRef<[u8]>,
331 value: impl AsRef<[u8]>,
332 ) -> Result<bool, Error> {
333 let key = key.as_ref();
334 let value = value.as_ref();
335 let encoded = encode_request(&McRequest::Replace {
336 key,
337 value,
338 flags: 0,
339 exptime: 0,
340 })?;
341 let response = self.route_command(key, &encoded).await?;
342 match response {
343 McResponseBytes::Stored => Ok(true),
344 McResponseBytes::NotStored => Ok(false),
345 _ => Err(Error::UnexpectedResponse),
346 }
347 }
348
349 pub async fn incr(&mut self, key: impl AsRef<[u8]>, delta: u64) -> Result<Option<u64>, Error> {
352 let key = key.as_ref();
353 let encoded = encode_request(&McRequest::incr(key, delta))?;
354 let response = self.route_command(key, &encoded).await?;
355 match response {
356 McResponseBytes::Numeric(val) => Ok(Some(val)),
357 McResponseBytes::NotFound => Ok(None),
358 _ => Err(Error::UnexpectedResponse),
359 }
360 }
361
362 pub async fn decr(&mut self, key: impl AsRef<[u8]>, delta: u64) -> Result<Option<u64>, Error> {
365 let key = key.as_ref();
366 let encoded = encode_request(&McRequest::decr(key, delta))?;
367 let response = self.route_command(key, &encoded).await?;
368 match response {
369 McResponseBytes::Numeric(val) => Ok(Some(val)),
370 McResponseBytes::NotFound => Ok(None),
371 _ => Err(Error::UnexpectedResponse),
372 }
373 }
374
375 pub async fn append(
378 &mut self,
379 key: impl AsRef<[u8]>,
380 value: impl AsRef<[u8]>,
381 ) -> Result<bool, Error> {
382 let key = key.as_ref();
383 let value = value.as_ref();
384 let encoded = encode_request(&McRequest::append(key, value))?;
385 let response = self.route_command(key, &encoded).await?;
386 match response {
387 McResponseBytes::Stored => Ok(true),
388 McResponseBytes::NotStored => Ok(false),
389 _ => Err(Error::UnexpectedResponse),
390 }
391 }
392
393 pub async fn prepend(
396 &mut self,
397 key: impl AsRef<[u8]>,
398 value: impl AsRef<[u8]>,
399 ) -> Result<bool, Error> {
400 let key = key.as_ref();
401 let value = value.as_ref();
402 let encoded = encode_request(&McRequest::prepend(key, value))?;
403 let response = self.route_command(key, &encoded).await?;
404 match response {
405 McResponseBytes::Stored => Ok(true),
406 McResponseBytes::NotStored => Ok(false),
407 _ => Err(Error::UnexpectedResponse),
408 }
409 }
410
411 pub async fn cas(
415 &mut self,
416 key: impl AsRef<[u8]>,
417 value: impl AsRef<[u8]>,
418 cas_unique: u64,
419 ) -> Result<bool, Error> {
420 let key = key.as_ref();
421 let value = value.as_ref();
422 let encoded = encode_request(&McRequest::cas(key, value, cas_unique))?;
423 let response = self.route_command(key, &encoded).await?;
424 match response {
425 McResponseBytes::Stored => Ok(true),
426 McResponseBytes::Exists => Ok(false),
427 McResponseBytes::NotFound => Err(Error::Memcache("NOT_FOUND".into())),
428 _ => Err(Error::UnexpectedResponse),
429 }
430 }
431
432 pub async fn delete(&mut self, key: impl AsRef<[u8]>) -> Result<bool, Error> {
434 let key = key.as_ref();
435 let encoded = encode_request(&McRequest::delete(key))?;
436 let response = self.route_command(key, &encoded).await?;
437 match response {
438 McResponseBytes::Deleted => Ok(true),
439 McResponseBytes::NotFound => Ok(false),
440 _ => Err(Error::UnexpectedResponse),
441 }
442 }
443
444 pub async fn flush_all(&mut self) -> Result<(), Error> {
454 let opts = self.connect_opts();
455 for shard in &mut self.shards {
456 flush_all_on_shard(shard, &opts).await?;
457 }
458 Ok(())
459 }
460
461 pub async fn version(&mut self) -> Result<Box<str>, Error> {
468 let opts = self.connect_opts();
469 for shard in &mut self.shards {
470 match version_on_shard(shard, &opts).await {
471 Ok(v) => return Ok(v),
472 Err(Error::AllConnectionsFailed) => continue,
473 Err(e) => return Err(e),
474 }
475 }
476 Err(Error::AllConnectionsFailed)
477 }
478}
479
480async fn flush_all_on_shard(shard: &mut Shard, opts: &ConnectOpts) -> Result<(), Error> {
484 let size = shard.conns.len();
485 for _ in 0..size {
486 let idx = shard.next;
487 shard.next = (shard.next + 1) % size;
488 let conn = match &shard.conns[idx] {
489 ShardConn::Connected(c) => *c,
490 ShardConn::Disconnected => match do_connect(shard.addr, opts).await {
491 Ok(c) => {
492 shard.conns[idx] = ShardConn::Connected(c);
493 c
494 }
495 Err(_) => continue,
496 },
497 };
498 match Client::new(conn).flush_all().await {
499 Ok(()) => return Ok(()),
500 Err(Error::ConnectionClosed) => {
501 shard.conns[idx] = ShardConn::Disconnected;
502 conn.close();
503 continue;
504 }
505 Err(e) => return Err(e),
506 }
507 }
508 Err(Error::AllConnectionsFailed)
509}
510
511async fn version_on_shard(shard: &mut Shard, opts: &ConnectOpts) -> Result<Box<str>, Error> {
513 let size = shard.conns.len();
514 for _ in 0..size {
515 let idx = shard.next;
516 shard.next = (shard.next + 1) % size;
517 let conn = match &shard.conns[idx] {
518 ShardConn::Connected(c) => *c,
519 ShardConn::Disconnected => match do_connect(shard.addr, opts).await {
520 Ok(c) => {
521 shard.conns[idx] = ShardConn::Connected(c);
522 c
523 }
524 Err(_) => continue,
525 },
526 };
527 match Client::new(conn).version().await {
528 Ok(v) => return Ok(v),
529 Err(Error::ConnectionClosed) => {
530 shard.conns[idx] = ShardConn::Disconnected;
531 conn.close();
532 continue;
533 }
534 Err(e) => return Err(e),
535 }
536 }
537 Err(Error::AllConnectionsFailed)
538}
539
540#[derive(Clone)]
542struct ConnectOpts {
543 connect_timeout_ms: u64,
544 tls_server_name: Option<String>,
545}
546
547async fn get_conn(shard: &mut Shard, opts: &ConnectOpts) -> Result<ConnCtx, Error> {
549 let size = shard.conns.len();
550 for _ in 0..size {
551 let idx = shard.next;
552 shard.next = (shard.next + 1) % size;
553
554 match &shard.conns[idx] {
555 ShardConn::Connected(c) => return Ok(*c),
556 ShardConn::Disconnected => {
557 if let Ok(conn) = do_connect(shard.addr, opts).await {
558 shard.conns[idx] = ShardConn::Connected(conn);
559 return Ok(conn);
560 }
561 }
562 }
563 }
564 Err(Error::AllConnectionsFailed)
565}
566
567async fn do_connect(addr: SocketAddr, opts: &ConnectOpts) -> Result<ConnCtx, Error> {
568 let conn = if let Some(ref sni) = opts.tls_server_name {
569 let fut = if opts.connect_timeout_ms > 0 {
570 ringline::connect_tls_with_timeout(addr, sni, opts.connect_timeout_ms)?
571 } else {
572 ringline::connect_tls(addr, sni)?
573 };
574 fut.await?
575 } else {
576 let fut = if opts.connect_timeout_ms > 0 {
577 ringline::connect_with_timeout(addr, opts.connect_timeout_ms)?
578 } else {
579 ringline::connect(addr)?
580 };
581 fut.await?
582 };
583
584 Ok(conn)
585}
586
587#[cfg(test)]
588mod tests {
589 use super::*;
590
591 #[test]
592 fn test_single_server_always_routes_to_zero() {
593 let config = ShardedConfig {
594 servers: vec!["127.0.0.1:11211".parse().unwrap()],
595 pool_size: 1,
596 connect_timeout_ms: 0,
597 tls_server_name: None,
598 };
599 let client = ShardedClient::new(config);
600 assert_eq!(client.ring.route(b"any-key"), 0);
601 assert_eq!(client.ring.route(b"another-key"), 0);
602 assert_eq!(client.ring.route(b""), 0);
603 }
604
605 #[test]
606 fn test_deterministic_routing() {
607 let config = ShardedConfig {
608 servers: vec![
609 "127.0.0.1:11211".parse().unwrap(),
610 "127.0.0.1:11212".parse().unwrap(),
611 "127.0.0.1:11213".parse().unwrap(),
612 ],
613 pool_size: 1,
614 connect_timeout_ms: 0,
615 tls_server_name: None,
616 };
617 let client = ShardedClient::new(config);
618
619 let a = client.ring.route(b"test-key");
620 let b = client.ring.route(b"test-key");
621 assert_eq!(a, b);
622
623 let c = client.ring.route(b"other-key");
624 let d = client.ring.route(b"other-key");
625 assert_eq!(c, d);
626 }
627
628 #[test]
629 fn test_config_defaults() {
630 let config = ShardedConfig {
631 servers: vec![
632 "127.0.0.1:11211".parse().unwrap(),
633 "127.0.0.1:11212".parse().unwrap(),
634 ],
635 pool_size: 4,
636 connect_timeout_ms: 500,
637 tls_server_name: None,
638 };
639 let client = ShardedClient::new(config);
640 assert_eq!(client.shard_count(), 2);
641 assert_eq!(client.ring.node_count(), 2);
642 assert_eq!(client.shards[0].conns.len(), 4);
643 assert_eq!(client.shards[1].conns.len(), 4);
644 }
645
646 #[test]
647 fn test_pool_size_minimum() {
648 let config = ShardedConfig {
649 servers: vec!["127.0.0.1:11211".parse().unwrap()],
650 pool_size: 0,
651 connect_timeout_ms: 0,
652 tls_server_name: None,
653 };
654 let client = ShardedClient::new(config);
655 assert_eq!(client.shards[0].conns.len(), 1);
656 }
657
658 #[test]
659 fn test_require_same_shard_matching() {
660 let config = ShardedConfig {
661 servers: vec![
662 "127.0.0.1:11211".parse().unwrap(),
663 "127.0.0.1:11212".parse().unwrap(),
664 ],
665 pool_size: 1,
666 connect_timeout_ms: 0,
667 tls_server_name: None,
668 };
669 let client = ShardedClient::new(config);
670 let keys: &[&[u8]] = &[b"same-key", b"same-key"];
671 assert!(client.require_same_shard(keys).is_ok());
672 }
673
674 #[test]
675 fn test_require_same_shard_single_key() {
676 let config = ShardedConfig {
677 servers: vec![
678 "127.0.0.1:11211".parse().unwrap(),
679 "127.0.0.1:11212".parse().unwrap(),
680 ],
681 pool_size: 1,
682 connect_timeout_ms: 0,
683 tls_server_name: None,
684 };
685 let client = ShardedClient::new(config);
686 let keys: &[&[u8]] = &[b"anykey"];
687 assert!(client.require_same_shard(keys).is_ok());
688 }
689}