1use core::fmt::Write as _;
49use std::sync::atomic::Ordering::Relaxed;
50use std::sync::atomic::{AtomicBool, AtomicU64};
51
52use yo_common::lock::Lock;
53use yo_common::{Code, Error, Result};
54
55use crate::reply::Out;
56
57use super::args::{self, Args};
58use super::{Server, Session};
59
60mod asm;
61mod bus;
62use super::keyspec;
63use super::table::Spec;
64pub(super) use asm::Migration;
65
66pub const SLOTS: usize = 16384;
72
73const WRITABLE_DELAY_MS: u64 = 2000;
80
81const REJOIN_DELAY_MS: u64 = 5000;
91
92const BUS_OFFSET: u16 = 10000;
94
95const ID_LEN: usize = 40;
97
98#[rustfmt::skip]
108const CRC16: [u16; 256] = [
109 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
110 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
111 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
112 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
113 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
114 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
115 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
116 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
117 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
118 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
119 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
120 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
121 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
122 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
123 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
124 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
125 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
126 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
127 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
128 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
129 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
130 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
131 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
132 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
133 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
134 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
135 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
136 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
137 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
138 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
139 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
140 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,
141];
142
143#[must_use]
145fn crc16(data: &[u8]) -> u16 {
146 let mut crc: u16 = 0;
147 for &byte in data {
148 let at = ((crc >> 8) ^ u16::from(byte)) & 0xff;
149 crc = (crc << 8) ^ CRC16[at as usize];
150 }
151 crc
152}
153
154#[must_use]
164pub fn key_slot(key: &[u8]) -> u16 {
165 let tagged = match key.iter().position(|&b| b == b'{') {
166 Some(open) => match key[open + 1..].iter().position(|&b| b == b'}') {
167 Some(0) | None => key,
169 Some(len) => &key[open + 1..open + 1 + len],
170 },
171 None => key,
172 };
173 crc16(tagged) % SLOTS as u16
174}
175
176pub(crate) const FLAG_MASTER: u16 = 1;
184pub(crate) const FLAG_SLAVE: u16 = 2;
185pub(crate) const FLAG_PFAIL: u16 = 4;
186pub(crate) const FLAG_FAIL: u16 = 8;
187pub(crate) const FLAG_MYSELF: u16 = 16;
188pub(crate) const FLAG_HANDSHAKE: u16 = 32;
189pub(crate) const FLAG_NOADDR: u16 = 64;
190pub(crate) const FLAG_MEET: u16 = 128;
191pub(crate) const FLAG_MIGRATE_TO: u16 = 256;
192pub(crate) const FLAG_NOFAILOVER: u16 = 512;
193pub(crate) const FLAG_EXTENSIONS: u16 = 1024;
194
195#[derive(Clone)]
197struct Node {
198 id: String,
200 host: String,
203 port: u16,
205 bus: u16,
209 shard: String,
211 epoch: u64,
213 flags: u16,
215 master: Option<u16>,
217 ping_sent: u64,
220 pong_recv: u64,
222 data_recv: u64,
226 fail_time: u64,
228 offset: u64,
230 linked: bool,
232 reports: Vec<(String, u64)>,
235}
236
237impl Node {
238 fn new(id: String, host: String, port: u16, bus: u16, flags: u16, now: u64) -> Node {
240 Node {
241 id,
242 host,
243 port,
244 bus,
245 shard: String::from_utf8_lossy(&new_id()).into_owned(),
246 epoch: 0,
247 flags,
248 master: None,
249 ping_sent: 0,
250 pong_recv: 0,
255 data_recv: now,
258 fail_time: 0,
259 offset: 0,
260 linked: false,
261 reports: Vec::new(),
262 }
263 }
264
265 fn is_master(&self) -> bool {
268 self.flags & FLAG_SLAVE == 0
269 }
270
271 fn down(&self) -> bool {
273 self.flags & (FLAG_PFAIL | FLAG_FAIL) != 0
274 }
275
276 fn flag_names(&self, into: &mut String) {
279 const NAMES: [(u16, &str); 8] = [
280 (FLAG_MYSELF, "myself"),
281 (FLAG_MASTER, "master"),
282 (FLAG_SLAVE, "slave"),
283 (FLAG_PFAIL, "fail?"),
284 (FLAG_FAIL, "fail"),
285 (FLAG_HANDSHAKE, "handshake"),
286 (FLAG_NOADDR, "noaddr"),
287 (FLAG_NOFAILOVER, "nofailover"),
288 ];
289 let mut first = true;
290 for (bit, name) in NAMES {
291 if self.flags & bit == 0 {
292 continue;
293 }
294 if !first {
295 into.push(',');
296 }
297 into.push_str(name);
298 first = false;
299 }
300 if first {
301 into.push_str("noflags");
302 }
303 }
304
305 fn address(&self, into: &mut String) {
307 let _ = write!(into, "{}:{}@{}", self.host, self.port, self.bus);
308 }
309
310 fn address_on_disk(&self, into: &mut String) {
315 self.address(into);
316 let _ = write!(into, ",,tls-port=0,shard-id={}", self.shard);
317 }
318}
319
320struct Map {
327 nodes: Vec<Node>,
329 owner: Vec<Option<u16>>,
331 migrating: Vec<Option<u16>>,
333 importing: Vec<Option<u16>>,
335}
336
337impl Map {
338 fn new(me: Node) -> Map {
340 Map {
341 nodes: vec![me],
342 owner: vec![None; SLOTS],
343 migrating: vec![None; SLOTS],
344 importing: vec![None; SLOTS],
345 }
346 }
347
348 fn find(&self, id: &[u8]) -> Option<u16> {
350 self.nodes
351 .iter()
352 .position(|n| n.id.as_bytes() == id)
353 .map(|at| at as u16)
354 }
355
356 fn forget(&mut self, at: u16) {
365 self.nodes.remove(usize::from(at));
366 let shift = |slot: &mut Option<u16>| match *slot {
367 Some(node) if node == at => *slot = None,
368 Some(node) if node > at => *slot = Some(node - 1),
369 _ => {}
370 };
371 for slot in 0..SLOTS {
372 shift(&mut self.owner[slot]);
373 shift(&mut self.migrating[slot]);
374 shift(&mut self.importing[slot]);
375 }
376 for node in &mut self.nodes {
377 shift(&mut node.master);
378 }
379 }
380
381 fn voters(&self) -> usize {
384 let mut seen = vec![false; self.nodes.len()];
385 for owner in self.owner.iter().flatten() {
386 seen[*owner as usize] = true;
387 }
388 seen.iter().filter(|s| **s).count()
389 }
390
391 fn mine(&self, slot: u16) -> bool {
393 self.owner[slot as usize] == Some(0)
394 }
395
396 fn assigned(&self) -> usize {
398 self.owner.iter().filter(|o| o.is_some()).count()
399 }
400
401 fn size(&self) -> usize {
404 let mut seen = vec![false; self.nodes.len()];
405 for owner in self.owner.iter().flatten() {
406 seen[*owner as usize] = true;
407 }
408 seen.iter().filter(|s| **s).count()
409 }
410
411 fn runs(&self, node: u16) -> Vec<(u16, u16)> {
413 let mut runs: Vec<(u16, u16)> = Vec::new();
414 for slot in 0..SLOTS as u16 {
415 if self.owner[slot as usize] != Some(node) {
416 continue;
417 }
418 match runs.last_mut() {
419 Some(last) if last.1 + 1 == slot => last.1 = slot,
420 _ => runs.push((slot, slot)),
421 }
422 }
423 runs
424 }
425}
426
427pub(crate) struct Cluster {
432 on: bool,
437 map: Lock<Map>,
439 epoch: AtomicU64,
441 covered_at: AtomicU64,
444 booted_at: AtomicU64,
447 was_down: AtomicBool,
450 full_coverage: AtomicBool,
453 reads_when_down: AtomicBool,
462 file: Lock<String>,
464 bus: bus::Bus,
467 asm: asm::Asm,
469}
470
471impl Default for Cluster {
472 fn default() -> Cluster {
473 Cluster {
474 on: false,
475 map: Lock::new(Map {
476 nodes: Vec::new(),
477 owner: Vec::new(),
478 migrating: Vec::new(),
479 importing: Vec::new(),
480 }),
481 epoch: AtomicU64::new(0),
482 covered_at: AtomicU64::new(0),
483 booted_at: AtomicU64::new(0),
484 was_down: AtomicBool::new(false),
485 full_coverage: AtomicBool::new(true),
486 reads_when_down: AtomicBool::new(false),
487 file: Lock::new(String::new()),
488 bus: bus::Bus::default(),
489 asm: asm::Asm::default(),
490 }
491 }
492}
493
494impl Server {
495 #[must_use]
498 pub fn cluster_enabled(&self) -> bool {
499 self.cluster.on
500 }
501
502 pub fn enable_cluster(&mut self, file: &str, port: u16) {
508 self.cluster.on = true;
509 self.cluster.booted_at.store(self.now_ms(), Relaxed);
510 let now = self.now_ms();
511 let me = yo_alloc::allow(|| {
512 Node::new(
513 String::from_utf8_lossy(&new_id()).into_owned(),
514 String::new(),
515 port,
516 port + BUS_OFFSET,
517 FLAG_MYSELF | FLAG_MASTER,
518 now,
519 )
520 });
521 let path = yo_alloc::allow(|| {
527 if file.is_empty() {
528 String::new()
529 } else {
530 self.dir().join(file).to_string_lossy().into_owned()
531 }
532 });
533 yo_alloc::allow(|| {
534 *self.cluster.map.lock() = Map::new(me);
535 *self.cluster.file.lock() = path;
536 *self.cluster.bus.secret.lock() = String::from_utf8_lossy(&new_id()).into_owned();
542 });
543 if let Err(e) = self.reload_cluster() {
548 eprintln!("cluster config file could not be read: {e}");
549 }
550 self.recount_coverage();
551 }
552
553 pub(crate) fn cluster_secret(&self) -> String {
563 let held = self.cluster.bus.secret.lock();
564 yo_alloc::allow(|| held.clone())
565 }
566
567 pub(crate) fn cluster_full_coverage(&self) -> bool {
569 self.cluster.full_coverage.load(Relaxed)
570 }
571
572 pub(crate) fn cluster_reads_when_down(&self) -> bool {
574 self.cluster.reads_when_down.load(Relaxed)
575 }
576
577 pub(crate) fn set_cluster_coverage(&self, full: bool, reads_when_down: bool) {
580 self.cluster.full_coverage.store(full, Relaxed);
581 self.cluster.reads_when_down.store(reads_when_down, Relaxed);
582 self.recount_coverage();
583 }
584
585 pub(crate) fn cluster_file(&self) -> String {
587 let file = self.cluster.file.lock();
588 yo_alloc::allow(|| file.clone())
589 }
590
591 pub(crate) fn cluster_id(&self) -> String {
593 let map = self.cluster.map.lock();
594 yo_alloc::allow(|| map.nodes.first().map_or_else(String::new, |n| n.id.clone()))
595 }
596
597 pub(crate) fn cluster_up(&self) -> bool {
604 let at = self.cluster.covered_at.load(Relaxed);
605 if at == 0 {
606 return false;
607 }
608 let (since, wait) = if self.cluster.was_down.load(Relaxed) {
609 (at, REJOIN_DELAY_MS)
610 } else {
611 (self.cluster.booted_at.load(Relaxed), WRITABLE_DELAY_MS)
612 };
613 self.now_ms().saturating_sub(since) >= wait
614 }
615
616 fn recount_coverage(&self) {
622 let covered = {
623 let map = self.cluster.map.lock();
624 let assigned = map.assigned();
625 if self.cluster_full_coverage() {
626 assigned == SLOTS
627 } else {
628 assigned > 0
629 }
630 };
631 if covered {
632 let _ =
633 self.cluster
634 .covered_at
635 .compare_exchange(0, self.now_ms().max(1), Relaxed, Relaxed);
636 } else {
637 self.cluster.covered_at.store(0, Relaxed);
638 self.cluster.was_down.store(true, Relaxed);
639 }
640 }
641}
642
643fn new_id() -> [u8; ID_LEN] {
645 const HEX: &[u8; 16] = b"0123456789abcdef";
646 let mut raw = [0u8; ID_LEN / 2];
647 yo_common::entropy::fill(&mut raw);
648 let mut id = [0u8; ID_LEN];
649 for (i, byte) in raw.iter().enumerate() {
650 id[i * 2] = HEX[usize::from(byte >> 4)];
651 id[i * 2 + 1] = HEX[usize::from(byte & 15)];
652 }
653 id
654}
655
656pub(super) fn asks(spec: &Spec) -> bool {
666 spec.flags.contains(&"asking")
667}
668
669pub(super) fn gate(
688 server: &Server,
689 db: usize,
690 asking: bool,
691 spec: &Spec,
692 args: Args<'_>,
693) -> Option<Error> {
694 if !keyspec::takes_keys(spec, args, 0) {
697 return None;
698 }
699 let mut slot: Option<u16> = None;
700 let mut crossed = false;
701 let mut keys = 0usize;
702 let mut present = 0usize;
703 let mut missing = 0usize;
704 let (owner, migrating, importing, here) = {
705 let map = server.cluster.map.lock();
706 keyspec::find(spec, args, 0, &mut |run| {
708 for i in 0..run.count {
709 let at = run.first + i * run.step;
710 if at >= args.len() {
711 continue;
712 }
713 let this = key_slot(args.get(at));
714 keys += 1;
715 match slot {
716 None => slot = Some(this),
717 Some(first) if first != this => crossed = true,
718 Some(_) => {}
719 }
720 }
721 });
722 let at = usize::from(slot?);
723 (
724 map.owner[at],
725 map.migrating[at].map(|to| node_at(&map, to)),
726 map.importing[at].is_some(),
727 map.owner[at] == Some(0),
728 )
729 };
730 let slot = slot?;
731 let Some(owner) = owner else {
734 return Some(Error::new(
735 Code::Invalid,
736 "CLUSTERDOWN Hash slot not served",
737 ));
738 };
739 if crossed {
740 return Some(Error::new(
741 Code::Invalid,
742 "CROSSSLOT Keys in request don't hash to the same slot",
743 ));
744 }
745 if !server.cluster_up() {
750 if !server.cluster.reads_when_down.load(Relaxed) {
751 return Some(Error::new(Code::Invalid, "CLUSTERDOWN The cluster is down"));
752 }
753 if spec.flags.contains(&"write") {
754 return Some(Error::new(
755 Code::Invalid,
756 "CLUSTERDOWN The cluster is down and only accepts read commands",
757 ));
758 }
759 }
760 if migrating.is_some() || importing {
764 let held = &server.dbs[db];
765 keyspec::find(spec, args, 0, &mut |run| {
766 for i in 0..run.count {
767 let at = run.first + i * run.step;
768 if at >= args.len() {
769 continue;
770 }
771 let key = args.get(at);
772 let mut stripe = held.hold(key);
773 if stripe.exists(key) {
774 present += 1;
775 } else {
776 missing += 1;
777 }
778 }
779 });
780 }
781 if let Some(to) = migrating
782 && missing > 0
783 {
784 if present > 0 {
787 return Some(Error::new(
788 Code::Invalid,
789 "TRYAGAIN Multiple keys request during rehashing of slot",
790 ));
791 }
792 return Some(redirect("ASK", slot, &to));
793 }
794 if importing && asking {
795 if keys > 1 && missing > 0 {
796 return Some(Error::new(
797 Code::Invalid,
798 "TRYAGAIN Multiple keys request during rehashing of slot",
799 ));
800 }
801 return None;
802 }
803 if here {
804 return None;
805 }
806 let (host, port) = {
807 let map = server.cluster.map.lock();
808 node_at(&map, owner)
809 };
810 Some(redirect("MOVED", slot, &(host, port)))
811}
812
813fn node_at(map: &Map, at: u16) -> (String, u16) {
815 let node = &map.nodes[at as usize];
816 (yo_alloc::allow(|| node.host.clone()), node.port)
817}
818
819fn redirect(word: &str, slot: u16, node: &(String, u16)) -> Error {
825 let host = if node.0.is_empty() {
826 "127.0.0.1"
827 } else {
828 node.0.as_str()
829 };
830 Error::fmt(
831 Code::Invalid,
832 format_args!("{word} {slot} {host}:{}", node.1),
833 )
834}
835
836pub(super) fn execute(
840 server: &Server,
841 session: &mut Session,
842 args: Args<'_>,
843 out: &mut Out,
844) -> Result<()> {
845 let session_db = session.db;
846 let sub = args.get(1);
847 if !server.cluster_enabled() {
851 return match arity_of(sub) {
852 Some(n) if !arity_ok(n, args.len()) => Err(wrong_sub_arity(sub)),
853 Some(_) => Err(disabled()),
854 None => Err(args::unknown_subcommand(sub, "CLUSTER")),
855 };
856 }
857 let Some(n) = arity_of(sub) else {
858 return Err(args::unknown_subcommand(sub, "CLUSTER"));
859 };
860 if !arity_ok(n, args.len()) {
861 return Err(wrong_sub_arity(sub));
862 }
863 match () {
864 () if args::is(sub, b"myid") => out.bulk(server.cluster_id().as_bytes()),
865 () if args::is(sub, b"myshardid") => {
866 let map = server.cluster.map.lock();
867 out.bulk(map.nodes[0].shard.as_bytes());
868 }
869 () if args::is(sub, b"keyslot") => out.int(i64::from(key_slot(args.get(2)))),
870 () if args::is(sub, b"info") => info(server, out),
871 () if args::is(sub, b"nodes") => nodes(server, out),
872 () if args::is(sub, b"slots") => reply_slots(server, out),
873 () if args::is(sub, b"shards") => shards(server, out),
874 () if args::is(sub, b"links") => server.cluster_links(out),
875 () if args::is(sub, b"slaves") || args::is(sub, b"replicas") => {
876 replicas(server, args.get(2), out)?;
877 }
878 () if args::is(sub, b"count-failure-reports") => {
879 let map = server.cluster.map.lock();
880 let Some(at) = map.find(args.get(2)) else {
881 return Err(unknown_node(args.get(2)));
882 };
883 out.int(map.nodes[usize::from(at)].reports.len() as i64);
884 }
885 () if args::is(sub, b"countkeysinslot") => count_keys(server, session_db, args, out)?,
886 () if args::is(sub, b"getkeysinslot") => get_keys(server, session_db, args, out)?,
887 () if args::is(sub, b"addslots") => {
888 add_or_del(server, args, true, false)?;
889 out.ok();
890 }
891 () if args::is(sub, b"delslots") => {
892 add_or_del(server, args, false, false)?;
893 out.ok();
894 }
895 () if args::is(sub, b"addslotsrange") => {
896 add_or_del(server, args, true, true)?;
897 out.ok();
898 }
899 () if args::is(sub, b"delslotsrange") => {
900 add_or_del(server, args, false, true)?;
901 out.ok();
902 }
903 () if args::is(sub, b"setslot") => setslot(server, session_db, args, out)?,
904 () if args::is(sub, b"flushslots") => flushslots(server, out)?,
905 () if args::is(sub, b"bumpepoch") => bumpepoch(server, out)?,
906 () if args::is(sub, b"set-config-epoch") => set_config_epoch(server, args, out)?,
907 () if args::is(sub, b"reset") => {
908 if args.len() > 3 {
909 return Err(sub_syntax(sub));
910 }
911 reset(server, args, out)?;
912 }
913 () if args::is(sub, b"slot-stats") => slot_stats(server, session_db, args, out)?,
914 () if args::is(sub, b"migration") => migration(server, args, out)?,
915 () if args::is(sub, b"syncslots") => syncslots(server, session, args, out)?,
916 () if args::is(sub, b"saveconfig") => {
917 save(server)?;
918 out.ok();
919 }
920 () if args::is(sub, b"forget") => {
921 forget(server, args.get(2))?;
922 out.ok();
923 }
924 () if args::is(sub, b"replicate") => {
925 replicate(server, session_db, args.get(2))?;
926 out.ok();
927 }
928 () if args::is(sub, b"failover") => {
929 if args.len() > 3 {
930 return Err(sub_syntax(sub));
931 }
932 if args.len() == 3
933 && !args::is(args.get(2), b"force")
934 && !args::is(args.get(2), b"takeover")
935 {
936 return Err(args::syntax());
937 }
938 return Err(Error::new(
939 Code::Invalid,
940 "You should send CLUSTER FAILOVER to a replica",
941 ));
942 }
943 () if args::is(sub, b"meet") => {
944 if args.len() > 5 {
945 return Err(sub_syntax(sub));
946 }
947 let (host, port, bus) = meet(&args)?;
948 server.cluster_meet(&host, port, bus);
949 out.ok();
950 }
951 () if args::is(sub, b"help") => help(out),
952 _ => return Err(args::unknown_subcommand(sub, "CLUSTER")),
953 }
954 Ok(())
955}
956
957pub(super) fn disabled() -> Error {
959 Error::new(Code::Invalid, "This instance has cluster support disabled")
960}
961
962fn not_yet(what: &str) -> Error {
968 Error::fmt(
969 Code::Invalid,
970 format_args!("{what} is not implemented yet, move the slot with SETSLOT and MIGRATE"),
971 )
972}
973
974fn unknown_node(id: &[u8]) -> Error {
976 Error::fmt(
977 Code::Invalid,
978 format_args!("Unknown node {}", String::from_utf8_lossy(id)),
979 )
980}
981
982fn dont_know(id: &[u8]) -> Error {
985 Error::fmt(
986 Code::Invalid,
987 format_args!("I don't know about node {}", String::from_utf8_lossy(id)),
988 )
989}
990
991fn arity_of(sub: &[u8]) -> Option<i32> {
998 const TABLE: &[(&str, i32)] = &[
999 ("addslots", -3),
1000 ("addslotsrange", -4),
1001 ("bumpepoch", 2),
1002 ("count-failure-reports", 3),
1003 ("countkeysinslot", 3),
1004 ("delslots", -3),
1005 ("delslotsrange", -4),
1006 ("failover", -2),
1007 ("flushslots", 2),
1008 ("forget", 3),
1009 ("getkeysinslot", 4),
1010 ("help", 2),
1011 ("info", 2),
1012 ("keyslot", 3),
1013 ("links", 2),
1014 ("meet", -4),
1015 ("migration", -4),
1016 ("myid", 2),
1017 ("myshardid", 2),
1018 ("nodes", 2),
1019 ("replicas", 3),
1020 ("replicate", 3),
1021 ("reset", -2),
1022 ("saveconfig", 2),
1023 ("set-config-epoch", 3),
1024 ("setslot", -4),
1025 ("shards", 2),
1026 ("slaves", 3),
1027 ("slot-stats", -4),
1028 ("slots", 2),
1029 ("syncslots", -3),
1030 ];
1031 TABLE
1032 .iter()
1033 .find(|(name, _)| args::is(sub, name.as_bytes()))
1034 .map(|(_, arity)| *arity)
1035}
1036
1037fn arity_ok(arity: i32, len: usize) -> bool {
1039 let len = len as i32;
1040 if arity >= 0 {
1041 len == arity
1042 } else {
1043 len >= -arity
1044 }
1045}
1046
1047fn sub_syntax(sub: &[u8]) -> Error {
1055 Error::fmt(
1056 Code::Unsupported,
1057 format_args!(
1058 "unknown subcommand or wrong number of arguments for '{}'. Try CLUSTER HELP.",
1059 String::from_utf8_lossy(sub)
1060 ),
1061 )
1062}
1063
1064fn wrong_sub_arity(sub: &[u8]) -> Error {
1066 Error::fmt(
1067 Code::Invalid,
1068 format_args!(
1069 "wrong number of arguments for 'cluster|{}' command",
1070 String::from_utf8_lossy(sub).to_lowercase()
1071 ),
1072 )
1073}
1074
1075fn info(server: &Server, out: &mut Out) {
1080 let (assigned, size, known, my_epoch) = {
1081 let map = server.cluster.map.lock();
1082 (
1083 map.assigned(),
1084 map.size(),
1085 map.nodes.len(),
1086 map.nodes[0].epoch,
1087 )
1088 };
1089 let state = if server.cluster_up() { "ok" } else { "fail" };
1090 let text = yo_alloc::allow(|| {
1091 let mut s = String::with_capacity(512);
1092 let _ = write!(
1093 s,
1094 "cluster_state:{state}\r\ncluster_slots_assigned:{assigned}\r\n\
1095 cluster_slots_ok:{assigned}\r\ncluster_slots_pfail:0\r\ncluster_slots_fail:0\r\n\
1096 cluster_known_nodes:{known}\r\ncluster_size:{size}\r\n\
1097 cluster_current_epoch:{}\r\ncluster_my_epoch:{my_epoch}\r\n\
1098 cluster_stats_messages_sent:0\r\ncluster_stats_messages_received:0\r\n\
1099 total_cluster_links_buffer_limit_exceeded:0\r\n\
1100 cluster_slot_migration_active_tasks:0\r\n\
1101 cluster_slot_migration_active_trim_running:0\r\n\
1102 cluster_slot_migration_active_trim_current_job_keys:0\r\n\
1103 cluster_slot_migration_active_trim_current_job_trimmed:0\r\n\
1104 cluster_slot_migration_stats_active_trim_started:0\r\n\
1105 cluster_slot_migration_stats_active_trim_completed:0\r\n\
1106 cluster_slot_migration_stats_active_trim_cancelled:0\r\n",
1107 server.cluster.epoch.load(Relaxed),
1108 );
1109 s
1110 });
1111 out.verbatim(b"txt", text.as_bytes());
1112}
1113
1114fn nodes(server: &Server, out: &mut Out) {
1116 let text = yo_alloc::allow(|| lines(server, false));
1117 out.verbatim(b"txt", text.as_bytes());
1118}
1119
1120fn lines(server: &Server, on_disk: bool) -> String {
1126 let map = server.cluster.map.lock();
1127 let mut s = String::with_capacity(256);
1128 for at in 0..map.nodes.len() as u16 {
1129 describe(&map, at, on_disk, &mut s);
1130 s.push('\n');
1131 }
1132 s
1133}
1134
1135fn describe(map: &Map, at: u16, on_disk: bool, s: &mut String) {
1138 let node = &map.nodes[usize::from(at)];
1139 s.push_str(&node.id);
1140 s.push(' ');
1141 if on_disk {
1142 node.address_on_disk(s);
1143 } else {
1144 node.address(s);
1145 }
1146 s.push(' ');
1147 node.flag_names(s);
1148 s.push(' ');
1149 match node.master.and_then(|m| map.nodes.get(usize::from(m))) {
1150 Some(master) => s.push_str(&master.id),
1151 None => s.push('-'),
1152 }
1153 let epoch = match node.master.and_then(|m| map.nodes.get(usize::from(m))) {
1156 Some(master) => master.epoch,
1157 None => node.epoch,
1158 };
1159 let link = if node.linked || at == 0 {
1160 "connected"
1161 } else {
1162 "disconnected"
1163 };
1164 let _ = write!(s, " {} {} {epoch} {link}", node.ping_sent, node.pong_recv);
1165 for (from, to) in map.runs(at) {
1166 if from == to {
1167 let _ = write!(s, " {from}");
1168 } else {
1169 let _ = write!(s, " {from}-{to}");
1170 }
1171 }
1172 if at == 0 {
1173 for slot in 0..SLOTS {
1174 if let Some(to) = map.migrating[slot] {
1175 let _ = write!(s, " [{slot}->-{}]", map.nodes[to as usize].id);
1176 }
1177 if let Some(from) = map.importing[slot] {
1178 let _ = write!(s, " [{slot}-<-{}]", map.nodes[from as usize].id);
1179 }
1180 }
1181 }
1182}
1183
1184fn reply_slots(server: &Server, out: &mut Out) {
1190 let mine = server.repl_offset();
1191 let map = server.cluster.map.lock();
1192 let at = out.len();
1193 let mut n = 0;
1194 let mut run: Option<(u16, u16)> = None;
1195 for slot in 0..=SLOTS as u16 {
1196 let owner = if slot as usize == SLOTS {
1197 None
1198 } else {
1199 map.owner[slot as usize]
1200 };
1201 match run {
1202 Some((node, _)) if owner == Some(node) => {}
1203 Some((node, from)) => {
1204 slot_run(&map, node, from, slot - 1, mine, out);
1205 n += 1;
1206 run = owner.map(|node| (node, slot));
1207 }
1208 None => run = owner.map(|node| (node, slot)),
1209 }
1210 }
1211 out.close_array(at, n);
1212}
1213
1214fn slot_run(map: &Map, node: u16, from: u16, to: u16, mine: u64, out: &mut Out) {
1222 let replicas: Vec<&Node> = map
1223 .nodes
1224 .iter()
1225 .enumerate()
1226 .filter(|(at, n)| {
1227 let offset = if *at == 0 { mine } else { n.offset };
1228 n.master == Some(node) && n.flags & FLAG_FAIL == 0 && offset != 0
1229 })
1230 .map(|(_, n)| n)
1231 .collect();
1232 out.array(3 + replicas.len());
1233 out.int(i64::from(from));
1234 out.int(i64::from(to));
1235 let held = &map.nodes[node as usize];
1236 for held in std::iter::once(held).chain(replicas.iter().copied()) {
1237 out.array(4);
1238 out.bulk(held.host.as_bytes());
1239 out.int(i64::from(held.port));
1240 out.bulk(held.id.as_bytes());
1241 out.array(0);
1242 }
1243}
1244
1245fn shards(server: &Server, out: &mut Out) {
1248 let map = server.cluster.map.lock();
1249 let at = out.len();
1250 let mut n = 0;
1251 let mut done: Vec<&str> = Vec::new();
1252 for node in 0..map.nodes.len() {
1253 let shard = map.nodes[node].shard.as_str();
1254 if done.contains(&shard) {
1255 continue;
1256 }
1257 done.push(shard);
1258 let members: Vec<usize> = (0..map.nodes.len())
1259 .filter(|other| map.nodes[*other].shard == shard)
1260 .collect();
1261 let runs: Vec<(u16, u16)> = members
1265 .iter()
1266 .flat_map(|member| map.runs(*member as u16))
1267 .collect();
1268 out.map(2);
1269 out.bulk(b"slots");
1270 out.array(runs.len() * 2);
1271 for (from, to) in runs {
1272 out.int(i64::from(from));
1273 out.int(i64::from(to));
1274 }
1275 out.bulk(b"nodes");
1276 out.array(members.len());
1277 for member in members {
1278 let held = &map.nodes[member];
1279 out.map(7);
1280 out.bulk(b"id");
1281 out.bulk(held.id.as_bytes());
1282 out.bulk(b"port");
1283 out.int(i64::from(held.port));
1284 out.bulk(b"ip");
1285 out.bulk(held.host.as_bytes());
1286 out.bulk(b"endpoint");
1287 out.bulk(held.host.as_bytes());
1288 out.bulk(b"role");
1289 out.bulk(if held.is_master() {
1290 b"master".as_slice()
1291 } else {
1292 b"replica".as_slice()
1293 });
1294 out.bulk(b"replication-offset");
1295 out.int(if member == 0 {
1296 server.repl_offset() as i64
1297 } else {
1298 held.offset as i64
1299 });
1300 out.bulk(b"health");
1301 out.bulk(match held.flags {
1302 f if f & FLAG_FAIL != 0 => b"fail".as_slice(),
1303 f if f & FLAG_PFAIL != 0 => b"loading".as_slice(),
1304 _ => b"online".as_slice(),
1305 });
1306 }
1307 n += 1;
1308 }
1309 out.close_array(at, n);
1310}
1311
1312fn help(out: &mut Out) {
1314 const LINES: &[&str] = &[
1315 "CLUSTER <subcommand> [<arg> [value] [opt] ...]. Subcommands are:",
1316 "COUNTKEYSINSLOT <slot>",
1317 " Return the number of keys in <slot>.",
1318 "GETKEYSINSLOT <slot> <count>",
1319 " Return key names stored by current node in a slot.",
1320 "INFO",
1321 " Return information about the cluster.",
1322 "KEYSLOT <key>",
1323 " Return the hash slot for <key>.",
1324 "MYID",
1325 " Return the node id.",
1326 "MYSHARDID",
1327 " Return the node's shard id.",
1328 "NODES",
1329 " Return cluster configuration seen by node. Output format:",
1330 " <id> <ip:port@bus-port[,hostname]> <flags> <master> <pings> <pongs> <epoch> <link> <slot> ...",
1331 "REPLICAS <node-id>",
1332 " Return <node-id> replicas.",
1333 "SLOTS",
1334 " Return information about slots range mappings. Each range is made of:",
1335 " start, end, master and replicas IP addresses, ports and ids",
1336 "SLOT-STATS",
1337 " Return an array of slot usage statistics for slots assigned to the current node.",
1338 "SHARDS",
1339 " Return information about slot range mappings and the nodes associated with them.",
1340 "ADDSLOTS <slot> [<slot> ...]",
1341 " Assign slots to current node.",
1342 "ADDSLOTSRANGE <start slot> <end slot> [<start slot> <end slot> ...]",
1343 " Assign slots which are between <start-slot> and <end-slot> to current node.",
1344 "BUMPEPOCH",
1345 " Advance the cluster config epoch.",
1346 "COUNT-FAILURE-REPORTS <node-id>",
1347 " Return number of failure reports for <node-id>.",
1348 "DELSLOTS <slot> [<slot> ...]",
1349 " Delete slots information from current node.",
1350 "DELSLOTSRANGE <start slot> <end slot> [<start slot> <end slot> ...]",
1351 " Delete slots information which are between <start-slot> and <end-slot> from current node.",
1352 "FAILOVER [FORCE|TAKEOVER]",
1353 " Promote current replica node to being a master.",
1354 "FORGET <node-id>",
1355 " Remove a node from the cluster.",
1356 "FLUSHSLOTS",
1357 " Delete current node own slots information.",
1358 "MEET <ip> <port> [<bus-port>]",
1359 " Connect nodes into a working cluster.",
1360 "REPLICATE <node-id>",
1361 " Configure current node as replica to <node-id>.",
1362 "RESET [HARD|SOFT]",
1363 " Reset current node (default: soft).",
1364 "SET-CONFIG-EPOCH <epoch>",
1365 " Set config epoch of current node.",
1366 "SETSLOT <slot> (IMPORTING <node-id>|MIGRATING <node-id>|STABLE|NODE <node-id>)",
1367 " Set slot state.",
1368 "SAVECONFIG",
1369 " Force saving cluster configuration on disk.",
1370 "LINKS",
1371 " Return information about all network links between this node and its peers.",
1372 " Output format is an array where each array element is a map containing attributes of a link",
1373 "MIGRATION IMPORT <start-slot end-slot [start-slot end-slot ...]> |",
1374 " STATUS [ID <task-id> | ALL] | CANCEL [ID <task-id> | ALL]",
1375 " Start, monitor and cancel slot migration.",
1376 "HELP",
1377 " Print this help.",
1378 ];
1379 out.array(LINES.len());
1380 for line in LINES {
1381 out.simple(line.as_bytes());
1382 }
1383}
1384
1385fn slot_arg(args: &Args<'_>, at: usize) -> Result<u16> {
1394 args.int(at)
1395 .ok()
1396 .and_then(|n| u16::try_from(n).ok())
1397 .filter(|s| usize::from(*s) < SLOTS)
1398 .ok_or_else(|| Error::new(Code::Invalid, "Invalid or out of range slot"))
1399}
1400
1401fn add_or_del(server: &Server, args: Args<'_>, add: bool, ranged: bool) -> Result<()> {
1408 let stride = if ranged { 2 } else { 1 };
1409 if ranged && !(args.len() - 2).is_multiple_of(2) {
1410 return Err(wrong_sub_arity(args.get(1)));
1411 }
1412 let mut wanted = Vec::new();
1413 let mut at = 2;
1414 while at < args.len() {
1415 let from = slot_arg(&args, at)?;
1416 let to = if ranged {
1417 slot_arg(&args, at + 1)?
1418 } else {
1419 from
1420 };
1421 if from > to {
1422 return Err(Error::fmt(
1423 Code::Invalid,
1424 format_args!("start slot number {from} is greater than end slot number {to}"),
1425 ));
1426 }
1427 for slot in from..=to {
1428 wanted.push(slot);
1429 }
1430 at += stride;
1431 }
1432 {
1433 let mut map = server.cluster.map.lock();
1434 let mut seen = vec![false; SLOTS];
1435 for slot in &wanted {
1436 let slot = usize::from(*slot);
1437 if seen[slot] {
1438 return Err(Error::fmt(
1439 Code::Invalid,
1440 format_args!("Slot {slot} specified multiple times"),
1441 ));
1442 }
1443 seen[slot] = true;
1444 let busy = map.owner[slot].is_some();
1445 if add && busy {
1446 return Err(Error::fmt(
1447 Code::Invalid,
1448 format_args!("Slot {slot} is already busy"),
1449 ));
1450 }
1451 if !add && !busy {
1452 return Err(Error::fmt(
1453 Code::Invalid,
1454 format_args!("Slot {slot} is already unassigned"),
1455 ));
1456 }
1457 }
1458 for slot in &wanted {
1459 let slot = usize::from(*slot);
1460 map.owner[slot] = if add { Some(0) } else { None };
1461 map.migrating[slot] = None;
1462 map.importing[slot] = None;
1463 }
1464 }
1465 server.recount_coverage();
1466 save(server)
1467}
1468
1469fn meet(args: &Args<'_>) -> Result<(String, u16, u16)> {
1475 let host = String::from_utf8_lossy(args.get(2));
1476 let typed = String::from_utf8_lossy(args.get(3));
1477 let port = args.int(3).map_err(|_| {
1478 Error::fmt(
1479 Code::Invalid,
1480 format_args!("Invalid base port specified: {typed}"),
1481 )
1482 })?;
1483 let bus = match args.opt(4) {
1484 None => port + i64::from(BUS_OFFSET),
1485 Some(word) => args.int(4).map_err(|_| {
1486 Error::fmt(
1487 Code::Invalid,
1488 format_args!(
1489 "Invalid bus port specified: {}",
1490 String::from_utf8_lossy(word)
1491 ),
1492 )
1493 })?,
1494 };
1495 if !(1..=65535).contains(&port) || !(0..=65535).contains(&bus) {
1496 return Err(Error::fmt(
1497 Code::Invalid,
1498 format_args!("Invalid node address specified: {host}:{typed}"),
1499 ));
1500 }
1501 let bus = if bus == 0 {
1502 port + i64::from(BUS_OFFSET)
1503 } else {
1504 bus
1505 };
1506 let host = yo_alloc::allow(|| host.into_owned());
1507 Ok((host, port as u16, bus as u16))
1508}
1509
1510fn replicas(server: &Server, id: &[u8], out: &mut Out) -> Result<()> {
1512 let map = server.cluster.map.lock();
1513 let Some(at) = map.find(id) else {
1514 return Err(unknown_node(id));
1515 };
1516 if !map.nodes[usize::from(at)].is_master() {
1517 return Err(Error::new(
1518 Code::Invalid,
1519 "The specified node is not a master",
1520 ));
1521 }
1522 let start = out.len();
1523 let mut n = 0;
1524 for other in 0..map.nodes.len() as u16 {
1525 if map.nodes[usize::from(other)].master != Some(at) {
1526 continue;
1527 }
1528 let line = yo_alloc::allow(|| {
1529 let mut s = String::with_capacity(256);
1530 describe(&map, other, false, &mut s);
1531 s
1532 });
1533 out.bulk(line.as_bytes());
1534 n += 1;
1535 }
1536 out.close_array(start, n);
1537 Ok(())
1538}
1539
1540fn forget(server: &Server, id: &[u8]) -> Result<()> {
1547 let at = {
1548 let map = server.cluster.map.lock();
1549 match map.find(id) {
1550 Some(0) => {
1551 return Err(Error::new(
1552 Code::Invalid,
1553 "I tried hard but I can't forget myself...",
1554 ));
1555 }
1556 Some(at) if map.nodes[0].master == Some(at) => {
1557 return Err(Error::new(Code::Invalid, "Can't forget my master!"));
1558 }
1559 Some(at) => at,
1560 None => {
1561 let name = String::from_utf8_lossy(id);
1562 if server.cluster_blacklisted(&name) {
1563 return Ok(());
1564 }
1565 return Err(unknown_node(id));
1566 }
1567 }
1568 };
1569 server.cluster_forget(at);
1570 Ok(())
1571}
1572
1573fn replicate(server: &Server, db: usize, id: &[u8]) -> Result<()> {
1579 let at = {
1580 let map = server.cluster.map.lock();
1581 match map.find(id) {
1582 None => return Err(unknown_node(id)),
1583 Some(0) => return Err(Error::new(Code::Invalid, "Can't replicate myself")),
1584 Some(at) if !map.nodes[usize::from(at)].is_master() => {
1585 return Err(Error::new(
1586 Code::Invalid,
1587 "I can only replicate a master, not a replica.",
1588 ));
1589 }
1590 Some(at) => {
1591 if map.nodes[0].is_master()
1592 && (!map.runs(0).is_empty() || !server.dbs[db].is_empty())
1593 {
1594 return Err(Error::new(
1595 Code::Invalid,
1596 "To set a master the node must be empty and without assigned slots.",
1597 ));
1598 }
1599 at
1600 }
1601 }
1602 };
1603 let Some(shared) = server.myself() else {
1604 return Err(Error::new(
1605 Code::Invalid,
1606 "CLUSTER REPLICATE is not available on an embedded server",
1607 ));
1608 };
1609 shared.cluster_replicate(at);
1610 Ok(())
1611}
1612
1613fn slot_stats(server: &Server, db: usize, args: Args<'_>, out: &mut Out) -> Result<()> {
1621 let sub = args.get(1);
1622 let mut wanted: Vec<u16> = Vec::new();
1623 let mut limit = SLOTS;
1624 let mut ascending = false;
1625 let ordered = args::is(args.get(2), b"orderby");
1626 if args::is(args.get(2), b"slotsrange") {
1627 if args.len() != 5 {
1628 return Err(sub_syntax(sub));
1629 }
1630 let from = slot_arg(&args, 3)?;
1631 let to = slot_arg(&args, 4)?;
1632 if from > to {
1633 return Err(Error::fmt(
1634 Code::Invalid,
1635 format_args!("Start slot number {from} is greater than end slot number {to}"),
1636 ));
1637 }
1638 wanted.extend(from..=to);
1639 } else if ordered {
1640 if !args::is(args.get(3), b"key-count") {
1641 return Err(Error::new(
1642 Code::Invalid,
1643 "Unrecognized sort metric for ORDERBY.",
1644 ));
1645 }
1646 let bad_limit = || {
1647 Error::new(
1648 Code::Invalid,
1649 "Limit has to lie in between 1 and 16384 (maximum number of slots).",
1650 )
1651 };
1652 let mut at = 4;
1653 while at < args.len() {
1654 let word = args.get(at);
1655 if args::is(word, b"limit") && at + 1 < args.len() {
1656 let n = args.int(at + 1).map_err(|_| bad_limit())?;
1657 if !(1..=SLOTS as i64).contains(&n) {
1658 return Err(bad_limit());
1659 }
1660 limit = n as usize;
1661 at += 2;
1662 } else if args::is(word, b"asc") {
1663 ascending = true;
1664 at += 1;
1665 } else if args::is(word, b"desc") {
1666 at += 1;
1667 } else {
1668 return Err(args::syntax());
1669 }
1670 }
1671 wanted.extend(0..SLOTS as u16);
1672 } else {
1673 return Err(sub_syntax(sub));
1674 }
1675 let mut counts = vec![0i64; SLOTS];
1676 server.dbs[db].keys(|key| counts[key_slot(key) as usize] += 1);
1677 let map = server.cluster.map.lock();
1678 wanted.retain(|slot| map.mine(*slot));
1679 drop(map);
1680 if ordered {
1681 if ascending {
1684 wanted.sort_by_key(|slot| (counts[*slot as usize], *slot));
1685 } else {
1686 wanted.sort_by_key(|slot| (-counts[*slot as usize], *slot));
1687 }
1688 wanted.truncate(limit);
1689 }
1690 out.array(wanted.len());
1691 for slot in wanted {
1692 out.array(2);
1693 out.int(i64::from(slot));
1694 out.map(1);
1695 out.bulk(b"key-count");
1696 out.int(counts[slot as usize]);
1697 }
1698 Ok(())
1699}
1700
1701fn migration(server: &Server, args: Args<'_>, out: &mut Out) -> Result<()> {
1709 let sub = args.get(1);
1710 let action = args.get(2);
1711 if args::is(action, b"status") || args::is(action, b"cancel") {
1712 let by_id = args::is(args.get(3), b"id");
1713 if by_id && args.len() != 5 {
1714 return Err(wrong_sub_arity(sub));
1715 }
1716 if !by_id && !args::is(args.get(3), b"all") {
1717 return Err(Error::new(Code::Invalid, "unknown argument"));
1718 }
1719 if !by_id && args.len() != 4 {
1720 return Err(wrong_sub_arity(sub));
1721 }
1722 let id = by_id.then(|| args.get(4));
1723 if args::is(action, b"status") {
1724 match id {
1725 Some(id) => server.cluster.asm.report_one(id, out),
1726 None => server.cluster.asm.report_all(out),
1727 }
1728 } else {
1729 out.int(server.cluster.asm.cancel(id, server.now_ms() as i64));
1730 server.asm_relax();
1733 }
1734 return Ok(());
1735 }
1736 if !args::is(action, b"import") {
1737 return Err(Error::new(Code::Invalid, "unknown argument"));
1738 }
1739 let ranges = slot_ranges(&args, 3)?;
1740 let map = server.cluster.map.lock();
1741 if ranges
1742 .iter()
1743 .flat_map(|(from, to)| *from..=*to)
1744 .all(|slot| map.mine(slot))
1745 {
1746 return Err(Error::new(
1747 Code::Invalid,
1748 "this node is already the owner of the slot range",
1749 ));
1750 }
1751 drop(map);
1752 Err(not_yet("CLUSTER MIGRATION IMPORT"))
1753}
1754
1755fn slot_ranges(args: &Args<'_>, from: usize) -> Result<Vec<(u16, u16)>> {
1764 let count = args.len().saturating_sub(from);
1765 if count < 2 || !count.is_multiple_of(2) {
1766 return Err(wrong_sub_arity(args.get(1)));
1767 }
1768 if count / 2 >= SLOTS {
1769 return Err(Error::fmt(
1770 Code::Invalid,
1771 format_args!("invalid number of slot ranges: {}", count / 2),
1772 ));
1773 }
1774 let mut ranges: Vec<(u16, u16)> = Vec::with_capacity(count / 2);
1775 let mut at = from;
1776 while at < args.len() {
1777 ranges.push((slot_arg(args, at)?, slot_arg(args, at + 1)?));
1778 at += 2;
1779 }
1780 ranges.sort_unstable();
1781 let mut joined: Vec<(u16, u16)> = Vec::with_capacity(ranges.len());
1782 for range in ranges {
1783 match joined.last_mut() {
1784 Some(last) if u32::from(last.1) + 1 == u32::from(range.0) => last.1 = range.1,
1785 _ => joined.push(range),
1786 }
1787 }
1788 let mut seen = vec![false; SLOTS];
1789 for &(start, end) in &joined {
1790 if start > end {
1791 return Err(Error::fmt(
1792 Code::Invalid,
1793 format_args!("start slot number {start} is greater than end slot number {end}"),
1794 ));
1795 }
1796 for slot in start..=end {
1797 if core::mem::replace(&mut seen[usize::from(slot)], true) {
1798 return Err(Error::fmt(
1799 Code::Invalid,
1800 format_args!("Slot {slot} specified multiple times"),
1801 ));
1802 }
1803 }
1804 }
1805 Ok(joined)
1806}
1807
1808fn syncslots(server: &Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Result<()> {
1824 if !session.internal() {
1825 session.hang_up();
1829 return Err(Error::new(
1830 Code::Invalid,
1831 "CLUSTER SYNCSLOTS subcommands are only allowed for internal clients",
1832 ));
1833 }
1834 let action = args.get(2);
1835 if !server.cluster.map.lock().nodes[0].is_master() {
1836 if !session.serving_master() {
1843 session.hang_up();
1844 return Err(Error::new(
1845 Code::Invalid,
1846 "CLUSTER SYNCSLOTS subcommands are only allowed for master",
1847 ));
1848 }
1849 if !args::is(action, b"conf") {
1850 return Ok(());
1851 }
1852 }
1853 if args::is(action, b"sync") && args.len() >= 6 {
1854 return sync(server, session, args, out);
1855 }
1856 if args::is(action, b"rdbchannel") && args.len() == 4 {
1857 return rdbchannel(server, session, args, out);
1858 }
1859 if (args::is(action, b"snapshot-eof") || args::is(action, b"stream-eof")) && args.len() == 3 {
1860 session.hang_up();
1864 return Ok(());
1865 }
1866 if args::is(action, b"ack") && args.len() == 5 {
1867 if let Some(offset) = yo_common::num::parse_i64(args.get(4))
1872 && offset >= 0
1873 {
1874 server.asm_ack(session.row().id, args.get(3), offset as u64);
1875 }
1876 return Ok(());
1877 }
1878 if args::is(action, b"fail") && args.len() == 4 {
1879 return Ok(());
1883 }
1884 if args::is(action, b"conf") && args.len() >= 5 {
1885 return conf(server, session, args, out);
1886 }
1887 Err(args::syntax())
1888}
1889
1890fn sync(server: &Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Result<()> {
1901 if !args.len().is_multiple_of(2) {
1902 return Err(wrong_sub_arity(args.get(1)));
1903 }
1904 let ranges = slot_ranges(&args, 4)?;
1905 {
1906 let map = server.cluster.map.lock();
1907 if (0..SLOTS).any(|at| map.migrating[at].is_some() || map.importing[at].is_some()) {
1911 return Err(Error::new(
1912 Code::Invalid,
1913 "all slot states must be STABLE to start a slot migration task.",
1914 ));
1915 }
1916 let mut source = None;
1917 for slot in ranges.iter().flat_map(|(from, to)| *from..=*to) {
1918 let Some(owner) = map.owner[usize::from(slot)] else {
1919 return Err(Error::fmt(
1920 Code::Invalid,
1921 format_args!("slot has no owner: {slot}"),
1922 ));
1923 };
1924 if *source.get_or_insert(owner) != owner {
1925 return Err(Error::new(
1926 Code::Invalid,
1927 "slots belong to different source nodes",
1928 ));
1929 }
1930 }
1931 if source != Some(0) {
1932 return Err(Error::new(
1933 Code::Invalid,
1934 "This node is not the owner of the slots",
1935 ));
1936 }
1937 let dest = session.node_id().to_vec();
1942 if !dest.is_empty()
1943 && !map
1944 .find(&dest)
1945 .is_some_and(|at| map.nodes[at as usize].is_master())
1946 {
1947 return Err(Error::fmt(
1948 Code::Invalid,
1949 format_args!(
1950 "Destination node {} is not a master",
1951 String::from_utf8_lossy(&dest)
1952 ),
1953 ));
1954 }
1955 }
1956 server.asm_begin_migrate(args.get(3), session.node_id(), ranges, session.row())?;
1957 out.simple(b"RDBCHANNELSYNCSLOTS");
1958 Ok(())
1959}
1960
1961fn rdbchannel(server: &Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Result<()> {
1970 let id = args.get(3);
1971 if id.len() != ID_LEN {
1972 return Err(Error::new(Code::Invalid, "Invalid task id"));
1973 }
1974 let ranges = server.asm_take_rdb_channel(id, session.row().id)?;
1975 out.simple(b"SLOTSSNAPSHOT");
1976 out.raw(&server.asm_snapshot(&ranges));
1977 Ok(())
1978}
1979
1980fn conf(server: &Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Result<()> {
1994 let mut at = 3;
1995 while at < args.len() {
1996 if at + 1 >= args.len() {
1997 super::write_error(out, &wrong_sub_arity(args.get(1)));
1998 return Ok(());
1999 }
2000 let name = args.get(at);
2001 let value = args.get(at + 1);
2002 if args::is(name, b"node-id") {
2003 if value.len() != ID_LEN {
2007 let len = value.len();
2008 super::write_error(
2009 out,
2010 &Error::fmt(Code::Invalid, format_args!("Invalid node id length {len}")),
2011 );
2012 return Ok(());
2013 }
2014 if server.cluster.map.lock().find(value).is_none() {
2015 super::write_error(
2016 out,
2017 &Error::fmt(
2018 Code::Invalid,
2019 format_args!(
2020 "Node {} not found in cluster",
2021 String::from_utf8_lossy(value)
2022 ),
2023 ),
2024 );
2025 return Ok(());
2026 }
2027 session.set_node_id(value);
2028 } else if args::is(name, b"slot-info") {
2029 if !slot_info(value) {
2030 super::write_error(
2031 out,
2032 &Error::fmt(
2033 Code::Invalid,
2034 format_args!("Invalid slot info: {}", String::from_utf8_lossy(value)),
2035 ),
2036 );
2037 return Ok(());
2038 }
2039 } else if args::is(name, b"asm-task") {
2040 if server.cluster.map.lock().nodes[0].is_master() {
2041 super::write_error(
2042 out,
2043 &Error::new(
2044 Code::Invalid,
2045 "CLUSTER SYNCSLOTS CONF ASM-TASK only allowed on replica",
2046 ),
2047 );
2048 return Ok(());
2049 }
2050 super::write_error(
2055 out,
2056 &Error::fmt(
2057 Code::Invalid,
2058 format_args!(
2059 "Failed to handle master task: {}",
2060 String::from_utf8_lossy(value)
2061 ),
2062 ),
2063 );
2064 } else if !args::is(name, b"capa") {
2065 super::write_error(
2066 out,
2067 &Error::fmt(
2068 Code::Invalid,
2069 format_args!("Unknown option {}", String::from_utf8_lossy(name)),
2070 ),
2071 );
2072 }
2073 at += 2;
2074 }
2075 out.ok();
2076 Ok(())
2077}
2078
2079fn slot_info(value: &[u8]) -> bool {
2087 let mut parts = value.split(|b| *b == b':');
2088 let Some(slot) = parts.next().and_then(yo_common::num::parse_i64) else {
2089 return false;
2090 };
2091 let Some(keys) = parts.next().and_then(yo_common::num::parse_i64) else {
2092 return false;
2093 };
2094 let Some(expires) = parts.next().and_then(yo_common::num::parse_i64) else {
2095 return false;
2096 };
2097 parts.next().is_none() && (0..SLOTS as i64).contains(&slot) && keys >= 0 && expires >= 0
2098}
2099
2100fn setslot(server: &Server, session_db: usize, args: Args<'_>, out: &mut Out) -> Result<()> {
2113 if !server.cluster.map.lock().nodes[0].is_master() {
2116 return Err(Error::new(
2117 Code::Invalid,
2118 "Please use SETSLOT only with masters.",
2119 ));
2120 }
2121 let slot = slot_arg(&args, 2)?;
2122 let action = args.get(3);
2123 let wrong = || {
2124 Error::new(
2125 Code::Invalid,
2126 "Invalid CLUSTER SETSLOT action or number of arguments. Try CLUSTER HELP",
2127 )
2128 };
2129 let mut announce = false;
2133 let mut follow: Option<u16> = None;
2136 {
2137 let mut map = server.cluster.map.lock();
2138 let at = usize::from(slot);
2139 if args::is(action, b"migrating") && args.len() == 5 {
2140 if !map.mine(slot) {
2141 return Err(Error::fmt(
2142 Code::Invalid,
2143 format_args!("I'm not the owner of hash slot {slot}"),
2144 ));
2145 }
2146 let Some(to) = map.find(args.get(4)) else {
2147 return Err(dont_know(args.get(4)));
2148 };
2149 map.migrating[at] = Some(to);
2150 } else if args::is(action, b"importing") && args.len() == 5 {
2151 if map.mine(slot) {
2152 return Err(Error::fmt(
2153 Code::Invalid,
2154 format_args!("I'm already the owner of hash slot {slot}"),
2155 ));
2156 }
2157 let Some(from) = map.find(args.get(4)) else {
2158 return Err(dont_know(args.get(4)));
2159 };
2160 map.importing[at] = Some(from);
2161 } else if args::is(action, b"stable") && args.len() == 4 {
2162 map.migrating[at] = None;
2163 map.importing[at] = None;
2164 } else if args::is(action, b"node") && args.len() == 5 {
2165 let Some(to) = map.find(args.get(4)) else {
2166 return Err(unknown_node(args.get(4)));
2167 };
2168 if !map.nodes[usize::from(to)].is_master() {
2169 return Err(Error::new(Code::Invalid, "Target node is not a master"));
2170 }
2171 let was_mine = map.owner[at] == Some(0);
2172 let held = keys_in_slot(server, session_db, slot);
2180 if was_mine && to != 0 && held != 0 {
2181 return Err(Error::fmt(
2182 Code::Invalid,
2183 format_args!(
2184 "Can't assign hashslot {slot} to a different node while I still hold keys for this hash slot."
2185 ),
2186 ));
2187 }
2188 if held == 0 {
2189 map.migrating[at] = None;
2190 }
2191 map.owner[at] = Some(to);
2192 if was_mine && to != 0 && map.runs(0).is_empty() {
2196 follow = Some(to);
2197 }
2198 if to == 0 && map.importing[at].is_some() {
2203 bump_without_consensus(server, &mut map);
2204 map.importing[at] = None;
2205 announce = true;
2206 }
2207 } else {
2208 return Err(wrong());
2209 }
2210 }
2211 server.recount_coverage();
2212 save(server)?;
2213 if let Some(to) = follow
2216 && let Some(shared) = server.myself()
2217 {
2218 shared.cluster_replicate(to);
2219 }
2220 if announce {
2221 server.cluster_broadcast_pong();
2222 }
2223 out.ok();
2224 Ok(())
2225}
2226
2227fn keys_in_slot(server: &Server, at: usize, slot: u16) -> usize {
2230 let mut found = 0;
2231 server.dbs[at].keys(|key| {
2232 if key_slot(key) == slot {
2233 found += 1;
2234 }
2235 });
2236 found
2237}
2238
2239fn bump_without_consensus(server: &Server, map: &mut Map) -> bool {
2253 let highest = map
2254 .nodes
2255 .iter()
2256 .map(|n| n.epoch)
2257 .max()
2258 .unwrap_or(0)
2259 .max(server.cluster.epoch.load(Relaxed));
2260 let mine = map.nodes[0].epoch;
2261 if mine != 0 && mine == highest {
2262 return false;
2263 }
2264 map.nodes[0].epoch = server.cluster.epoch.fetch_add(1, Relaxed) + 1;
2265 true
2266}
2267
2268fn flushslots(server: &Server, out: &mut Out) -> Result<()> {
2270 if server.dbs.iter().any(|db| !db.is_empty()) {
2271 return Err(Error::new(
2272 Code::Invalid,
2273 "DB must be empty to perform CLUSTER FLUSHSLOTS.",
2274 ));
2275 }
2276 {
2277 let mut map = server.cluster.map.lock();
2278 for slot in 0..SLOTS {
2279 if map.owner[slot] == Some(0) {
2280 map.owner[slot] = None;
2281 }
2282 map.migrating[slot] = None;
2283 map.importing[slot] = None;
2284 }
2285 }
2286 server.recount_coverage();
2287 save(server)?;
2288 out.ok();
2289 Ok(())
2290}
2291
2292fn bumpepoch(server: &Server, out: &mut Out) -> Result<()> {
2299 let (moved, epoch) = {
2300 let mut map = server.cluster.map.lock();
2301 let moved = bump_without_consensus(server, &mut map);
2302 (moved, map.nodes[0].epoch)
2303 };
2304 if moved {
2305 save(server)?;
2306 server.cluster_broadcast_pong();
2307 }
2308 let word = if moved { "BUMPED" } else { "STILL" };
2309 let text = yo_alloc::allow(|| format!("{word} {epoch}"));
2310 out.simple(text.as_bytes());
2311 Ok(())
2312}
2313
2314fn set_config_epoch(server: &Server, args: Args<'_>, out: &mut Out) -> Result<()> {
2317 let epoch = args.int(2)?;
2318 if epoch < 0 {
2319 return Err(Error::fmt(
2320 Code::Invalid,
2321 format_args!("Invalid config epoch specified: {epoch}"),
2322 ));
2323 }
2324 {
2325 let mut map = server.cluster.map.lock();
2326 if map.nodes[0].epoch != 0 {
2327 return Err(Error::new(
2328 Code::Invalid,
2329 "Node config epoch is already non-zero",
2330 ));
2331 }
2332 map.nodes[0].epoch = epoch as u64;
2333 }
2334 let epoch = epoch as u64;
2335 server.cluster.epoch.fetch_max(epoch, Relaxed);
2336 save(server)?;
2337 out.ok();
2338 Ok(())
2339}
2340
2341fn reset(server: &Server, args: Args<'_>, out: &mut Out) -> Result<()> {
2344 let hard = match args.opt(2) {
2345 None => false,
2346 Some(word) if args::is(word, b"hard") => true,
2347 Some(word) if args::is(word, b"soft") => false,
2348 Some(_) => return Err(args::syntax()),
2349 };
2350 if server.dbs.iter().any(|db| !db.is_empty()) {
2351 return Err(Error::new(
2352 Code::Invalid,
2353 "CLUSTER RESET can't be called with master nodes containing keys",
2354 ));
2355 }
2356 {
2357 let mut map = server.cluster.map.lock();
2358 for slot in 0..SLOTS {
2359 map.owner[slot] = None;
2360 map.migrating[slot] = None;
2361 map.importing[slot] = None;
2362 }
2363 map.nodes.truncate(1);
2364 map.nodes[0].epoch = 0;
2365 map.nodes[0].flags = FLAG_MYSELF | FLAG_MASTER;
2369 map.nodes[0].master = None;
2370 if hard {
2371 yo_alloc::allow(|| {
2372 map.nodes[0].id = String::from_utf8_lossy(&new_id()).into_owned();
2373 map.nodes[0].shard = String::from_utf8_lossy(&new_id()).into_owned();
2374 });
2375 }
2376 }
2377 if hard {
2378 server.cluster.epoch.store(0, Relaxed);
2379 }
2380 server.recount_coverage();
2381 save(server)?;
2382 out.ok();
2383 Ok(())
2384}
2385
2386fn count_keys(server: &Server, at: usize, args: Args<'_>, out: &mut Out) -> Result<()> {
2390 let n = args.int(2)?;
2391 let slot = u16::try_from(n)
2392 .ok()
2393 .filter(|s| usize::from(*s) < SLOTS)
2394 .ok_or_else(|| Error::new(Code::Invalid, "Invalid slot"))?;
2395 let mut found = 0i64;
2396 server.dbs[at].keys(|key| {
2397 if key_slot(key) == slot {
2398 found += 1;
2399 }
2400 });
2401 out.int(found);
2402 Ok(())
2403}
2404
2405fn get_keys(server: &Server, at: usize, args: Args<'_>, out: &mut Out) -> Result<()> {
2407 let slot = args.int(2)?;
2408 let count = args.int(3)?;
2409 let bad = || Error::new(Code::Invalid, "Invalid slot or number of keys");
2410 if !(0..SLOTS as i64).contains(&slot) || count < 0 {
2411 return Err(bad());
2412 }
2413 let slot = slot as u16;
2414 let want = count as usize;
2415 let start = out.len();
2416 let mut n = 0;
2417 server.dbs[at].keys(|key| {
2418 if n < want && key_slot(key) == slot {
2419 out.bulk(key);
2420 n += 1;
2421 }
2422 });
2423 out.close_array(start, n);
2424 Ok(())
2425}
2426
2427fn save(server: &Server) -> Result<()> {
2436 let path = {
2437 let file = server.cluster.file.lock();
2438 if file.is_empty() {
2439 return Ok(());
2440 }
2441 yo_alloc::allow(|| file.clone())
2442 };
2443 let epoch = server.cluster.epoch.load(Relaxed);
2444 let text = yo_alloc::allow(|| {
2445 let mut s = lines(server, true);
2446 let _ = writeln!(s, "vars currentEpoch {epoch} lastVoteEpoch 0");
2447 s
2448 });
2449 yo_alloc::allow(|| {
2450 let temp = format!("{path}.tmp");
2451 let wrote =
2452 std::fs::write(&temp, text.as_bytes()).and_then(|()| std::fs::rename(&temp, &path));
2453 match wrote {
2454 Ok(()) => Ok(()),
2455 Err(e) => Err(Error::fmt(
2456 Code::Invalid,
2457 format_args!("cluster config file could not be written: {e}"),
2458 )),
2459 }
2460 })
2461}
2462
2463impl Server {
2464 fn reload_cluster(&mut self) -> Result<()> {
2472 let path = self.cluster_file();
2473 if path.is_empty() {
2474 return Ok(());
2475 }
2476 let text = match yo_alloc::allow(|| std::fs::read_to_string(&path)) {
2477 Ok(text) => text,
2478 Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(()),
2479 Err(e) => {
2480 return Err(Error::fmt(Code::Invalid, format_args!("{path}: {e}")));
2481 }
2482 };
2483 yo_alloc::allow(|| self.absorb_cluster(&text))
2484 }
2485
2486 fn absorb_cluster(&mut self, text: &str) -> Result<()> {
2488 let bad = |what: &str| Error::fmt(Code::Invalid, format_args!("{what} in cluster config"));
2489 let now = self.now_ms();
2490 let mut nodes: Vec<Node> = Vec::new();
2491 let mut owned: Vec<(String, u16, u16)> = Vec::new();
2495 let mut follows: Vec<(String, String)> = Vec::new();
2498 for line in text.lines() {
2499 let line = line.trim();
2500 if line.is_empty() {
2501 continue;
2502 }
2503 let mut words = line.split(' ');
2504 let first = words.next().unwrap_or_default();
2505 if first == "vars" {
2506 let rest: Vec<&str> = words.collect();
2509 for pair in rest.chunks(2) {
2510 if pair.len() == 2 && pair[0] == "currentEpoch" {
2511 let epoch = pair[1].parse::<u64>().map_err(|_| bad("bad epoch"))?;
2512 self.cluster.epoch.store(epoch, Relaxed);
2513 }
2514 }
2515 continue;
2516 }
2517 if first.len() != ID_LEN {
2518 return Err(bad("bad node id"));
2519 }
2520 let address = words.next().ok_or_else(|| bad("missing address"))?;
2521 let named = words.next().ok_or_else(|| bad("missing flags"))?;
2522 let master = words.next().ok_or_else(|| bad("missing master"))?;
2523 let ping = words.next().ok_or_else(|| bad("missing ping time"))?;
2524 let pong = words.next().ok_or_else(|| bad("missing pong time"))?;
2525 let epoch = words
2526 .next()
2527 .and_then(|w| w.parse::<u64>().ok())
2528 .ok_or_else(|| bad("bad config epoch"))?;
2529 let _link = words.next();
2531 let (host, port, bus, shard) =
2532 split_address(address).ok_or_else(|| bad("bad address"))?;
2533 for word in words {
2534 if word.starts_with('[') {
2538 continue;
2539 }
2540 let (from, to) = match word.split_once('-') {
2541 Some((a, b)) => (
2542 a.parse::<u16>().map_err(|_| bad("bad slot"))?,
2543 b.parse::<u16>().map_err(|_| bad("bad slot"))?,
2544 ),
2545 None => {
2546 let one = word.parse::<u16>().map_err(|_| bad("bad slot"))?;
2547 (one, one)
2548 }
2549 };
2550 if usize::from(from) >= SLOTS || usize::from(to) >= SLOTS || from > to {
2551 return Err(bad("slot out of range"));
2552 }
2553 owned.push((first.to_owned(), from, to));
2554 }
2555 let mut flags = 0u16;
2556 for name in named.split(',') {
2557 flags |= match name {
2558 "myself" => FLAG_MYSELF,
2559 "master" => FLAG_MASTER,
2560 "slave" => FLAG_SLAVE,
2561 "fail?" => FLAG_PFAIL,
2562 "fail" => FLAG_FAIL,
2563 "handshake" => FLAG_HANDSHAKE,
2564 "noaddr" => FLAG_NOADDR,
2565 "nofailover" => FLAG_NOFAILOVER,
2566 _ => 0,
2567 };
2568 }
2569 if master != "-" {
2570 if master.len() != ID_LEN {
2571 return Err(bad("bad master id"));
2572 }
2573 follows.push((first.to_owned(), master.to_owned()));
2574 }
2575 let node = Node {
2576 id: first.to_owned(),
2577 host,
2578 port,
2579 bus,
2580 shard,
2581 epoch,
2582 flags,
2583 master: None,
2584 ping_sent: stamp(ping, now),
2589 pong_recv: stamp(pong, now),
2590 data_recv: now,
2591 fail_time: 0,
2592 offset: 0,
2593 linked: false,
2597 reports: Vec::new(),
2598 };
2599 if named.split(',').any(|f| f == "myself") {
2600 nodes.insert(0, node);
2602 } else {
2603 nodes.push(node);
2604 }
2605 }
2606 if nodes.is_empty() {
2607 return Ok(());
2608 }
2609 let mut map = Map::new(nodes.remove(0));
2610 map.nodes.append(&mut nodes);
2611 for (id, from, to) in owned {
2612 let Some(at) = map.find(id.as_bytes()) else {
2613 return Err(bad("slots for a node nobody knows"));
2614 };
2615 for slot in from..=to {
2616 map.owner[usize::from(slot)] = Some(at);
2617 }
2618 }
2619 for (who, whose) in follows {
2620 let (Some(who), Some(whose)) = (map.find(who.as_bytes()), map.find(whose.as_bytes()))
2621 else {
2622 return Err(bad("master id nobody knows"));
2623 };
2624 map.nodes[usize::from(who)].master = Some(whose);
2625 map.nodes[usize::from(who)].epoch = 0;
2630 }
2631 *self.cluster.map.lock() = map;
2632 Ok(())
2633 }
2634}
2635
2636fn stamp(field: &str, now: u64) -> u64 {
2643 match field.parse::<u64>() {
2644 Ok(0) | Err(_) => 0,
2645 Ok(_) => now,
2646 }
2647}
2648
2649fn split_address(field: &str) -> Option<(String, u16, u16, String)> {
2656 let (address, aux) = match field.split_once(',') {
2657 Some((address, aux)) => (address, aux),
2658 None => (field, ""),
2659 };
2660 let (host, ports) = address.rsplit_once(':')?;
2661 let (client, bus) = match ports.split_once('@') {
2662 Some((client, bus)) => (client, bus.parse::<u16>().ok()?),
2663 None => (ports, 0),
2664 };
2665 let port = client.parse::<u16>().ok()?;
2666 let bus = if bus == 0 { port + BUS_OFFSET } else { bus };
2667 let shard = aux
2668 .split(',')
2669 .find_map(|pair| pair.strip_prefix("shard-id="))
2670 .map_or_else(
2671 || String::from_utf8_lossy(&new_id()).into_owned(),
2672 str::to_owned,
2673 );
2674 Some((host.to_owned(), port, bus, shard))
2675}
2676
2677#[cfg(test)]
2687impl Server {
2688 pub(super) fn cluster_own_everything(&self) {
2690 {
2691 let mut map = self.cluster.map.lock();
2692 for slot in 0..SLOTS {
2693 map.owner[slot] = Some(0);
2694 }
2695 }
2696 self.recount_coverage();
2697 self.cluster.was_down.store(false, Relaxed);
2698 self.cluster.booted_at.store(0, Relaxed);
2699 }
2700
2701 pub(super) fn cluster_pretend_node(&self, id: &str, host: &str, port: u16) -> u16 {
2703 let mut map = self.cluster.map.lock();
2704 let mut node = Node::new(
2705 id.to_owned(),
2706 host.to_owned(),
2707 port,
2708 port + BUS_OFFSET,
2709 FLAG_MASTER,
2710 0,
2711 );
2712 node.shard = id.to_owned();
2713 node.linked = true;
2714 map.nodes.push(node);
2715 (map.nodes.len() - 1) as u16
2716 }
2717
2718 pub(super) fn cluster_hand_over(&self, slot: u16, node: u16) {
2720 let mut map = self.cluster.map.lock();
2721 map.owner[usize::from(slot)] = Some(node);
2722 }
2723
2724 pub(super) fn cluster_pretend_follower(&self, of: u16) {
2730 let mut map = self.cluster.map.lock();
2731 map.nodes[0].flags &= !FLAG_MASTER;
2732 map.nodes[0].flags |= FLAG_SLAVE;
2733 map.nodes[0].master = Some(of);
2734 }
2735
2736 pub(super) fn cluster_moving(&self, slot: u16, to: Option<u16>, from: Option<u16>) {
2738 let mut map = self.cluster.map.lock();
2739 map.migrating[usize::from(slot)] = to;
2740 map.importing[usize::from(slot)] = from;
2741 }
2742}
2743
2744#[cfg(test)]
2747mod tests {
2748 use super::{SLOTS, key_slot};
2749
2750 #[test]
2753 fn a_key_lands_in_the_slot_a_real_server_puts_it_in() {
2754 assert_eq!(key_slot(b"foo"), 12182);
2755 assert_eq!(key_slot(b"1234"), 6025);
2756 assert_eq!(key_slot(b""), 0);
2757 assert_eq!(key_slot(b"{user1000}.following"), 3443);
2758 }
2759
2760 #[test]
2763 fn the_hash_tag_rules_are_the_reference_rules() {
2764 assert_eq!(
2766 key_slot(b"{user1000}.following"),
2767 key_slot(b"{user1000}.followers")
2768 );
2769 assert_eq!(key_slot(b"{}foo"), key_slot(b"{}foo"));
2771 assert_ne!(key_slot(b"{}foo"), key_slot(b"foo"));
2772 assert_ne!(key_slot(b"{foo"), key_slot(b"foo"));
2774 assert_eq!(key_slot(b"{a}{b}"), key_slot(b"a"));
2776 assert_eq!(key_slot(b"foo{{bar}}zap"), key_slot(b"{bar"));
2778 }
2779
2780 #[test]
2783 fn every_slot_is_in_range() {
2784 let mut seen = vec![false; SLOTS];
2785 for i in 0..200_000u32 {
2786 let key = i.to_string();
2787 let slot = key_slot(key.as_bytes());
2788 assert!(usize::from(slot) < SLOTS);
2789 seen[usize::from(slot)] = true;
2790 }
2791 assert!(seen.iter().all(|s| *s), "200k keys reach all 16384 slots");
2792 }
2793
2794 #[test]
2803 fn a_config_file_puts_every_run_on_the_node_that_owns_it() {
2804 let mut server = super::Server::new();
2805 server.enable_cluster("", 7355);
2806 let text = "\
28073b80b05445f38bc7214f083696a2bbf90e3f30e3 127.0.0.1:7356@17356 master - 0 0 0 connected 10923-16383
280830d0651b0ec5e178e082634c44fb9adcc6e4021b 127.0.0.1:7355@17355 myself,master - 0 0 1 connected 5461-10922
280919a9e69b8b66016ac43c55ccdeed0283e0148e17 127.0.0.1:7354@17354 master - 0 0 2 connected 0-5460
2810vars currentEpoch 2 lastVoteEpoch 0
2811";
2812 server.absorb_cluster(text).expect("the file parses");
2813 let map = server.cluster.map.lock();
2814 let at = |id: &str| map.find(id.as_bytes()).expect("the node is in the table");
2815 assert_eq!(at("30d0651b0ec5e178e082634c44fb9adcc6e4021b"), 0, "myself");
2816 for (id, from, to) in [
2817 ("19a9e69b8b66016ac43c55ccdeed0283e0148e17", 0, 5460),
2818 ("30d0651b0ec5e178e082634c44fb9adcc6e4021b", 5461, 10922),
2819 ("3b80b05445f38bc7214f083696a2bbf90e3f30e3", 10923, 16383),
2820 ] {
2821 let owner = Some(at(id));
2822 for slot in from..=to {
2823 assert_eq!(map.owner[slot], owner, "slot {slot} belongs to {id}");
2824 }
2825 }
2826 }
2827
2828 #[test]
2832 fn a_config_file_is_not_read_back_as_live_state() {
2833 let mut server = super::Server::new();
2834 server.enable_cluster("", 7357);
2835 let text = "\
28369fcbb7624dedbb2fd0020dd2fbf86a5eb8cec31b 127.0.0.1:7355@17355 master - 0 1789005531306 3 connected 5461-10922
2837ac6dd51a69741dc5130637c594866c9b7e0cfc4e 127.0.0.1:7357@17357 myself,slave 9fcbb7624dedbb2fd0020dd2fbf86a5eb8cec31b 1789005400000 1789005532315 3 connected
2838";
2839 server.absorb_cluster(text).expect("the file parses");
2840 let now = server.now_ms();
2841 let map = server.cluster.map.lock();
2842 for node in &map.nodes {
2843 assert!(!node.linked, "nothing is linked before the bus dials out");
2844 }
2845 assert_eq!(map.nodes[0].ping_sent, now, "myself had a ping in flight");
2848 assert_eq!(map.nodes[1].ping_sent, 0, "the master did not");
2849 assert_eq!(map.nodes[0].pong_recv, now);
2850 assert_eq!(map.nodes[0].epoch, 0, "the replica's epoch is dropped");
2852 assert_eq!(map.nodes[1].epoch, 3, "the master's is kept");
2853 }
2854}