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,
39 encode_request_into,
40};
41use memcache_proto::Request as McRequest;
42
43pub struct ShardedConfig {
45 pub servers: Vec<SocketAddr>,
47 pub pool_size: usize,
49 pub connect_timeout_ms: u64,
51 pub tls_server_name: Option<String>,
53}
54
55enum ShardConn {
56 Connected(ConnCtx),
57 Disconnected,
58}
59
60struct Shard {
61 addr: SocketAddr,
62 conns: Vec<ShardConn>,
63 next: usize,
64}
65
66pub struct ShardedClient {
82 shards: Vec<Shard>,
83 ring: ketama::Ring,
84 connect_timeout_ms: u64,
85 tls_server_name: Option<String>,
86 encode_buf: Vec<u8>,
87}
88
89impl ShardedClient {
90 pub fn new(config: ShardedConfig) -> Self {
92 let pool_size = config.pool_size.max(1);
93
94 let server_ids: Vec<String> = config.servers.iter().map(|a| a.to_string()).collect();
95 let ring = ketama::Ring::build(&server_ids.iter().map(|s| s.as_str()).collect::<Vec<_>>());
96
97 let shards = config
98 .servers
99 .iter()
100 .map(|&addr| {
101 let mut conns = Vec::with_capacity(pool_size);
102 for _ in 0..pool_size {
103 conns.push(ShardConn::Disconnected);
104 }
105 Shard {
106 addr,
107 conns,
108 next: 0,
109 }
110 })
111 .collect();
112
113 Self {
114 shards,
115 ring,
116 connect_timeout_ms: config.connect_timeout_ms,
117 tls_server_name: config.tls_server_name,
118 encode_buf: Vec::new(),
119 }
120 }
121
122 pub async fn connect_all(&mut self) -> Result<(), Error> {
124 let opts = self.connect_opts();
125 for shard in &mut self.shards {
126 for slot in &mut shard.conns {
127 let conn = do_connect(shard.addr, &opts).await?;
128 *slot = ShardConn::Connected(conn);
129 }
130 }
131 Ok(())
132 }
133
134 pub fn close_all(&mut self) {
136 for shard in &mut self.shards {
137 for slot in &mut shard.conns {
138 if let ShardConn::Connected(c) = slot {
139 c.close();
140 }
141 *slot = ShardConn::Disconnected;
142 }
143 }
144 }
145
146 pub fn shard_count(&self) -> usize {
148 self.shards.len()
149 }
150
151 fn connect_opts(&self) -> ConnectOpts {
152 ConnectOpts {
153 connect_timeout_ms: self.connect_timeout_ms,
154 tls_server_name: self.tls_server_name.clone(),
155 }
156 }
157
158 pub async fn shard_client(&mut self, index: usize) -> Result<Client, Error> {
167 let opts = self.connect_opts();
168 let shard = &mut self.shards[index];
169 let conn = get_conn(shard, &opts).await?;
170 Ok(Client::new(conn))
171 }
172
173 async fn route_command(
186 &mut self,
187 key: &[u8],
188 encoded: &[u8],
189 ) -> Result<McResponseBytes, Error> {
190 let opts = self.connect_opts();
191 let shard_idx = self.ring.route(key);
192 let shard = &mut self.shards[shard_idx];
193 let size = shard.conns.len();
194
195 for attempt in 0..size {
196 let idx = (shard.next + attempt) % size;
197 let conn = match &shard.conns[idx] {
198 ShardConn::Connected(c) => *c,
199 ShardConn::Disconnected => match do_connect(shard.addr, &opts).await {
200 Ok(c) => {
201 shard.conns[idx] = ShardConn::Connected(c);
202 c
203 }
204 Err(_) => continue,
205 },
206 };
207
208 if conn.send(encoded).is_err() {
209 shard.conns[idx] = ShardConn::Disconnected;
215 conn.close();
216 continue;
217 }
218 match Client::new(conn).read_response().await {
219 Ok(response) => {
220 shard.next = (idx + 1) % size;
221 check_error_bytes(&response)?;
222 return Ok(response);
223 }
224 Err(Error::ConnectionClosed) => {
225 shard.conns[idx] = ShardConn::Disconnected;
226 continue;
227 }
228 Err(e) => return Err(e),
229 }
230 }
231
232 Err(Error::AllConnectionsFailed)
233 }
234
235 fn require_same_shard(&self, keys: &[&[u8]]) -> Result<usize, Error> {
237 let first = self.ring.route(keys[0]);
238 for key in &keys[1..] {
239 if self.ring.route(key) != first {
240 return Err(Error::Memcache(
241 "keys in request don't route to the same shard".into(),
242 ));
243 }
244 }
245 Ok(first)
246 }
247
248 pub async fn get(&mut self, key: impl AsRef<[u8]>) -> Result<Option<Value>, Error> {
252 let key = key.as_ref();
253 let mut buf = std::mem::take(&mut self.encode_buf);
254 buf.clear();
255 let response = match encode_request_into(&memcache_proto::Request::get(key), &mut buf) {
256 Ok(()) => self.route_command(key, &buf).await,
257 Err(e) => Err(e),
258 };
259 self.encode_buf = buf;
260 let response = response?;
261 match response {
262 McResponseBytes::Values(mut values) => {
263 if values.is_empty() {
264 Ok(None)
265 } else {
266 let v = values.swap_remove(0);
267 Ok(Some(Value {
268 data: v.data,
269 flags: v.flags,
270 }))
271 }
272 }
273 _ => Err(Error::UnexpectedResponse),
274 }
275 }
276
277 pub async fn gets(&mut self, keys: &[&[u8]]) -> Result<Vec<GetValue>, Error> {
280 if keys.is_empty() {
281 return Ok(Vec::new());
282 }
283 self.require_same_shard(keys)?;
284 let encoded = encode_request(&McRequest::gets(keys))?;
285 let response = self.route_command(keys[0], &encoded).await?;
286 match response {
287 McResponseBytes::Values(values) => Ok(values
288 .into_iter()
289 .map(|v| GetValue {
290 key: v.key,
291 data: v.data,
292 flags: v.flags,
293 cas: v.cas,
294 })
295 .collect()),
296 _ => Err(Error::UnexpectedResponse),
297 }
298 }
299
300 pub async fn set(
302 &mut self,
303 key: impl AsRef<[u8]>,
304 value: impl AsRef<[u8]>,
305 ) -> Result<(), Error> {
306 self.set_with_options(key, value, 0, 0).await
307 }
308
309 pub async fn set_with_options(
311 &mut self,
312 key: impl AsRef<[u8]>,
313 value: impl AsRef<[u8]>,
314 flags: u32,
315 exptime: u32,
316 ) -> Result<(), Error> {
317 let key = key.as_ref();
318 let value = value.as_ref();
319 let mut buf = std::mem::take(&mut self.encode_buf);
320 buf.clear();
321 let req = McRequest::Set {
322 key,
323 value,
324 flags,
325 exptime,
326 };
327 let response = match encode_request_into(&req, &mut buf) {
328 Ok(()) => self.route_command(key, &buf).await,
329 Err(e) => Err(e),
330 };
331 self.encode_buf = buf;
332 let response = response?;
333 match response {
334 McResponseBytes::Stored => Ok(()),
335 _ => Err(Error::UnexpectedResponse),
336 }
337 }
338
339 pub async fn add(
342 &mut self,
343 key: impl AsRef<[u8]>,
344 value: impl AsRef<[u8]>,
345 ) -> Result<bool, Error> {
346 let key = key.as_ref();
347 let value = value.as_ref();
348 let encoded = encode_add(key, value)?;
349 let response = self.route_command(key, &encoded).await?;
350 match response {
351 McResponseBytes::Stored => Ok(true),
352 McResponseBytes::NotStored => Ok(false),
353 _ => Err(Error::UnexpectedResponse),
354 }
355 }
356
357 pub async fn replace(
360 &mut self,
361 key: impl AsRef<[u8]>,
362 value: impl AsRef<[u8]>,
363 ) -> Result<bool, Error> {
364 let key = key.as_ref();
365 let value = value.as_ref();
366 let encoded = encode_request(&McRequest::Replace {
367 key,
368 value,
369 flags: 0,
370 exptime: 0,
371 })?;
372 let response = self.route_command(key, &encoded).await?;
373 match response {
374 McResponseBytes::Stored => Ok(true),
375 McResponseBytes::NotStored => Ok(false),
376 _ => Err(Error::UnexpectedResponse),
377 }
378 }
379
380 pub async fn incr(&mut self, key: impl AsRef<[u8]>, delta: u64) -> Result<Option<u64>, Error> {
383 let key = key.as_ref();
384 let encoded = encode_request(&McRequest::incr(key, delta))?;
385 let response = self.route_command(key, &encoded).await?;
386 match response {
387 McResponseBytes::Numeric(val) => Ok(Some(val)),
388 McResponseBytes::NotFound => Ok(None),
389 _ => Err(Error::UnexpectedResponse),
390 }
391 }
392
393 pub async fn decr(&mut self, key: impl AsRef<[u8]>, delta: u64) -> Result<Option<u64>, Error> {
396 let key = key.as_ref();
397 let encoded = encode_request(&McRequest::decr(key, delta))?;
398 let response = self.route_command(key, &encoded).await?;
399 match response {
400 McResponseBytes::Numeric(val) => Ok(Some(val)),
401 McResponseBytes::NotFound => Ok(None),
402 _ => Err(Error::UnexpectedResponse),
403 }
404 }
405
406 pub async fn append(
409 &mut self,
410 key: impl AsRef<[u8]>,
411 value: impl AsRef<[u8]>,
412 ) -> Result<bool, Error> {
413 let key = key.as_ref();
414 let value = value.as_ref();
415 let encoded = encode_request(&McRequest::append(key, value))?;
416 let response = self.route_command(key, &encoded).await?;
417 match response {
418 McResponseBytes::Stored => Ok(true),
419 McResponseBytes::NotStored => Ok(false),
420 _ => Err(Error::UnexpectedResponse),
421 }
422 }
423
424 pub async fn prepend(
427 &mut self,
428 key: impl AsRef<[u8]>,
429 value: impl AsRef<[u8]>,
430 ) -> Result<bool, Error> {
431 let key = key.as_ref();
432 let value = value.as_ref();
433 let encoded = encode_request(&McRequest::prepend(key, value))?;
434 let response = self.route_command(key, &encoded).await?;
435 match response {
436 McResponseBytes::Stored => Ok(true),
437 McResponseBytes::NotStored => Ok(false),
438 _ => Err(Error::UnexpectedResponse),
439 }
440 }
441
442 pub async fn cas(
446 &mut self,
447 key: impl AsRef<[u8]>,
448 value: impl AsRef<[u8]>,
449 cas_unique: u64,
450 ) -> Result<bool, Error> {
451 let key = key.as_ref();
452 let value = value.as_ref();
453 let encoded = encode_request(&McRequest::cas(key, value, cas_unique))?;
454 let response = self.route_command(key, &encoded).await?;
455 match response {
456 McResponseBytes::Stored => Ok(true),
457 McResponseBytes::Exists => Ok(false),
458 McResponseBytes::NotFound => Err(Error::Memcache("NOT_FOUND".into())),
459 _ => Err(Error::UnexpectedResponse),
460 }
461 }
462
463 pub async fn delete(&mut self, key: impl AsRef<[u8]>) -> Result<bool, Error> {
465 let key = key.as_ref();
466 let mut buf = std::mem::take(&mut self.encode_buf);
467 buf.clear();
468 let response = match encode_request_into(&McRequest::delete(key), &mut buf) {
469 Ok(()) => self.route_command(key, &buf).await,
470 Err(e) => Err(e),
471 };
472 self.encode_buf = buf;
473 let response = response?;
474 match response {
475 McResponseBytes::Deleted => Ok(true),
476 McResponseBytes::NotFound => Ok(false),
477 _ => Err(Error::UnexpectedResponse),
478 }
479 }
480
481 pub async fn flush_all(&mut self) -> Result<(), Error> {
491 let opts = self.connect_opts();
492 for shard in &mut self.shards {
493 flush_all_on_shard(shard, &opts).await?;
494 }
495 Ok(())
496 }
497
498 pub async fn version(&mut self) -> Result<Box<str>, Error> {
505 let opts = self.connect_opts();
506 for shard in &mut self.shards {
507 match version_on_shard(shard, &opts).await {
508 Ok(v) => return Ok(v),
509 Err(Error::AllConnectionsFailed) => continue,
510 Err(e) => return Err(e),
511 }
512 }
513 Err(Error::AllConnectionsFailed)
514 }
515}
516
517async fn flush_all_on_shard(shard: &mut Shard, opts: &ConnectOpts) -> Result<(), Error> {
521 let size = shard.conns.len();
522 for _ in 0..size {
523 let idx = shard.next;
524 shard.next = (shard.next + 1) % size;
525 let conn = match &shard.conns[idx] {
526 ShardConn::Connected(c) => *c,
527 ShardConn::Disconnected => match do_connect(shard.addr, opts).await {
528 Ok(c) => {
529 shard.conns[idx] = ShardConn::Connected(c);
530 c
531 }
532 Err(_) => continue,
533 },
534 };
535 match Client::new(conn).flush_all().await {
536 Ok(()) => return Ok(()),
537 Err(Error::ConnectionClosed) => {
538 shard.conns[idx] = ShardConn::Disconnected;
539 conn.close();
540 continue;
541 }
542 Err(e) => return Err(e),
543 }
544 }
545 Err(Error::AllConnectionsFailed)
546}
547
548async fn version_on_shard(shard: &mut Shard, opts: &ConnectOpts) -> Result<Box<str>, Error> {
550 let size = shard.conns.len();
551 for _ in 0..size {
552 let idx = shard.next;
553 shard.next = (shard.next + 1) % size;
554 let conn = match &shard.conns[idx] {
555 ShardConn::Connected(c) => *c,
556 ShardConn::Disconnected => match do_connect(shard.addr, opts).await {
557 Ok(c) => {
558 shard.conns[idx] = ShardConn::Connected(c);
559 c
560 }
561 Err(_) => continue,
562 },
563 };
564 match Client::new(conn).version().await {
565 Ok(v) => return Ok(v),
566 Err(Error::ConnectionClosed) => {
567 shard.conns[idx] = ShardConn::Disconnected;
568 conn.close();
569 continue;
570 }
571 Err(e) => return Err(e),
572 }
573 }
574 Err(Error::AllConnectionsFailed)
575}
576
577#[derive(Clone)]
579struct ConnectOpts {
580 connect_timeout_ms: u64,
581 tls_server_name: Option<String>,
582}
583
584async fn get_conn(shard: &mut Shard, opts: &ConnectOpts) -> Result<ConnCtx, Error> {
586 let size = shard.conns.len();
587 for _ in 0..size {
588 let idx = shard.next;
589 shard.next = (shard.next + 1) % size;
590
591 match &shard.conns[idx] {
592 ShardConn::Connected(c) => return Ok(*c),
593 ShardConn::Disconnected => {
594 if let Ok(conn) = do_connect(shard.addr, opts).await {
595 shard.conns[idx] = ShardConn::Connected(conn);
596 return Ok(conn);
597 }
598 }
599 }
600 }
601 Err(Error::AllConnectionsFailed)
602}
603
604async fn do_connect(addr: SocketAddr, opts: &ConnectOpts) -> Result<ConnCtx, Error> {
605 let conn = if let Some(ref sni) = opts.tls_server_name {
606 let fut = if opts.connect_timeout_ms > 0 {
607 ringline::connect_tls_with_timeout(addr, sni, opts.connect_timeout_ms)?
608 } else {
609 ringline::connect_tls(addr, sni)?
610 };
611 fut.await?
612 } else {
613 let fut = if opts.connect_timeout_ms > 0 {
614 ringline::connect_with_timeout(addr, opts.connect_timeout_ms)?
615 } else {
616 ringline::connect(addr)?
617 };
618 fut.await?
619 };
620
621 Ok(conn)
622}
623
624#[cfg(test)]
625mod tests {
626 use super::*;
627
628 #[test]
629 fn test_single_server_always_routes_to_zero() {
630 let config = ShardedConfig {
631 servers: vec!["127.0.0.1:11211".parse().unwrap()],
632 pool_size: 1,
633 connect_timeout_ms: 0,
634 tls_server_name: None,
635 };
636 let client = ShardedClient::new(config);
637 assert_eq!(client.ring.route(b"any-key"), 0);
638 assert_eq!(client.ring.route(b"another-key"), 0);
639 assert_eq!(client.ring.route(b""), 0);
640 }
641
642 #[test]
643 fn test_deterministic_routing() {
644 let config = ShardedConfig {
645 servers: vec![
646 "127.0.0.1:11211".parse().unwrap(),
647 "127.0.0.1:11212".parse().unwrap(),
648 "127.0.0.1:11213".parse().unwrap(),
649 ],
650 pool_size: 1,
651 connect_timeout_ms: 0,
652 tls_server_name: None,
653 };
654 let client = ShardedClient::new(config);
655
656 let a = client.ring.route(b"test-key");
657 let b = client.ring.route(b"test-key");
658 assert_eq!(a, b);
659
660 let c = client.ring.route(b"other-key");
661 let d = client.ring.route(b"other-key");
662 assert_eq!(c, d);
663 }
664
665 #[test]
666 fn test_config_defaults() {
667 let config = ShardedConfig {
668 servers: vec![
669 "127.0.0.1:11211".parse().unwrap(),
670 "127.0.0.1:11212".parse().unwrap(),
671 ],
672 pool_size: 4,
673 connect_timeout_ms: 500,
674 tls_server_name: None,
675 };
676 let client = ShardedClient::new(config);
677 assert_eq!(client.shard_count(), 2);
678 assert_eq!(client.ring.node_count(), 2);
679 assert_eq!(client.shards[0].conns.len(), 4);
680 assert_eq!(client.shards[1].conns.len(), 4);
681 }
682
683 #[test]
684 fn test_pool_size_minimum() {
685 let config = ShardedConfig {
686 servers: vec!["127.0.0.1:11211".parse().unwrap()],
687 pool_size: 0,
688 connect_timeout_ms: 0,
689 tls_server_name: None,
690 };
691 let client = ShardedClient::new(config);
692 assert_eq!(client.shards[0].conns.len(), 1);
693 }
694
695 #[test]
696 fn test_require_same_shard_matching() {
697 let config = ShardedConfig {
698 servers: vec![
699 "127.0.0.1:11211".parse().unwrap(),
700 "127.0.0.1:11212".parse().unwrap(),
701 ],
702 pool_size: 1,
703 connect_timeout_ms: 0,
704 tls_server_name: None,
705 };
706 let client = ShardedClient::new(config);
707 let keys: &[&[u8]] = &[b"same-key", b"same-key"];
708 assert!(client.require_same_shard(keys).is_ok());
709 }
710
711 #[test]
712 fn test_require_same_shard_single_key() {
713 let config = ShardedConfig {
714 servers: vec![
715 "127.0.0.1:11211".parse().unwrap(),
716 "127.0.0.1:11212".parse().unwrap(),
717 ],
718 pool_size: 1,
719 connect_timeout_ms: 0,
720 tls_server_name: None,
721 };
722 let client = ShardedClient::new(config);
723 let keys: &[&[u8]] = &[b"anykey"];
724 assert!(client.require_same_shard(keys).is_ok());
725 }
726}