1use yo_common::{Code, Error, Result};
34
35use crate::keyspace::Keyspace;
36use crate::stream::groups::Filter;
37use crate::stream::{Fate, Fields, Group, Id, Refs, Refused, Retry, Stream};
38use crate::value::{self, Kind};
39
40pub const BAD_ID: &str = "Invalid stream ID specified as stream command argument";
42
43pub const NOT_GREATER: &str =
45 "The ID specified in XADD is equal or smaller than the target stream top item";
46
47pub const ZERO_ID: &str = "The ID specified in XADD must be greater than 0-0";
49
50pub const EXHAUSTED: &str =
52 "The stream has exhausted the last possible ID, unable to add more items";
53
54pub const NO_KEY_FOR_GROUP: &str = "The XGROUP subcommand requires the key to exist. Note that for CREATE you may want to use the MKSTREAM option to create an empty stream automatically.";
59
60pub const GROUP_EXISTS: &str = "Consumer Group name already exists";
66
67pub const SETID_TOO_SMALL: &str =
69 "The ID specified in XSETID is smaller than the target stream top item";
70
71pub const SETID_BELOW_MAX_DELETED: &str =
78 "The ID specified in XSETID is smaller than the provided max_deleted_entry_id";
79
80pub const NO_SUCH_KEY: &str = "no such key";
82
83#[must_use]
95pub fn no_group(group: &[u8], key: &[u8]) -> String {
96 format!(
97 "No such consumer group '{}' for key name '{}'",
98 String::from_utf8_lossy(group),
99 String::from_utf8_lossy(key)
100 )
101}
102
103#[must_use]
109pub fn no_key_or_group(key: &[u8], group: &[u8]) -> String {
110 format!(
111 "No such key '{}' or consumer group '{}'",
112 String::from_utf8_lossy(key),
113 String::from_utf8_lossy(group)
114 )
115}
116
117#[must_use]
123pub fn no_group_for_read(key: &[u8], group: &[u8]) -> String {
124 format!(
125 "No such key '{}' or consumer group '{}' in XREADGROUP with GROUP option",
126 String::from_utf8_lossy(key),
127 String::from_utf8_lossy(group)
128 )
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
133pub enum Add {
134 Auto,
137 Seq(u64),
139 At(Id),
141}
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq)]
154pub enum Trim {
155 None,
157 MaxLen {
159 len: u64,
161 exact: bool,
163 limit: Option<u64>,
165 },
166 MinId {
168 id: Id,
170 exact: bool,
172 limit: Option<u64>,
174 },
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq)]
179pub enum Start {
180 Last,
183 At(Id),
185}
186
187#[derive(Debug, Clone, Copy, PartialEq, Eq)]
189pub enum From {
190 New,
192 Pending(Id),
197}
198
199#[derive(Debug, Clone, Copy)]
205pub struct Read<'a> {
206 pub group: &'a [u8],
208 pub consumer: &'a [u8],
210 pub from: From,
212 pub count: Option<usize>,
214 pub noack: bool,
216}
217
218#[derive(Debug, Clone, Copy)]
224pub struct Claim<'a> {
225 pub group: &'a [u8],
227 pub consumer: &'a [u8],
229 pub min_idle: u64,
231 pub time: u64,
233 pub retry: Option<u64>,
235 pub bump: bool,
238 pub force: bool,
241}
242
243impl Default for Claim<'_> {
244 fn default() -> Claim<'static> {
245 Claim {
246 group: b"",
247 consumer: b"",
248 min_idle: 0,
249 time: 0,
250 retry: None,
251 bump: true,
252 force: false,
253 }
254 }
255}
256
257impl Keyspace {
258 pub fn stream(&mut self, key: &[u8]) -> Result<Option<&Stream>> {
269 let Some(at) = self.live_slot(key, Kind::Stream)? else {
270 return Ok(None);
271 };
272 Ok(self.streams.get(at))
273 }
274
275 pub fn stream_mut(&mut self, key: &[u8]) -> Result<Option<&mut Stream>> {
281 let Some(at) = self.live_slot(key, Kind::Stream)? else {
282 return Ok(None);
283 };
284 Ok(self.streams.get_mut(at))
285 }
286
287 pub fn xadd(
302 &mut self,
303 key: &[u8],
304 id: Add,
305 fields: &[(&[u8], &[u8])],
306 trim: Trim,
307 mkstream: bool,
308 now: u64,
309 ) -> Result<Option<Id>> {
310 let limits = self.stream_limits;
311 let at = match self.live_slot(key, Kind::Stream)? {
312 Some(at) => at,
313 None if mkstream => self.new_stream(key),
314 None => return Ok(None),
315 };
316 let s = self.stream_at(at);
317 let want = match id {
320 Add::Auto => s.auto_id(now).ok_or_else(exhausted)?,
321 Add::Seq(ms) => s.auto_seq(ms).ok_or_else(|| {
322 if ms < s.last_id().ms {
323 Error::new(Code::Invalid, NOT_GREATER)
324 } else {
325 exhausted()
326 }
327 })?,
328 Add::At(id) => id,
329 };
330 s.append(want, fields, limits).map_err(refused)?;
331 cut(s, trim);
332 Ok(Some(want))
333 }
334
335 pub fn xdel(&mut self, key: &[u8], ids: impl Iterator<Item = Id>) -> Result<u64> {
341 let Some(at) = self.live_slot(key, Kind::Stream)? else {
342 return Ok(0);
343 };
344 let s = self.stream_at(at);
345 Ok(ids.filter(|&id| s.delete(id)).count() as u64)
346 }
347
348 pub fn xdelex<F>(
359 &mut self,
360 key: &[u8],
361 refs: Refs,
362 ids: impl Iterator<Item = Id>,
363 mut f: F,
364 ) -> Result<()>
365 where
366 F: FnMut(Fate),
367 {
368 let Some(at) = self.live_slot(key, Kind::Stream)? else {
369 ids.for_each(|_| f(Fate::Missing));
370 return Ok(());
371 };
372 let s = self.stream_at(at);
373 ids.for_each(|id| f(s.delete_ref(id, refs)));
374 Ok(())
375 }
376
377 pub fn xackdel<F>(
388 &mut self,
389 key: &[u8],
390 group: &[u8],
391 refs: Refs,
392 ids: impl Iterator<Item = Id>,
393 mut f: F,
394 ) -> Result<()>
395 where
396 F: FnMut(Fate),
397 {
398 let Some(at) = self.live_slot(key, Kind::Stream)? else {
399 ids.for_each(|_| f(Fate::Missing));
400 return Ok(());
401 };
402 let s = self.stream_at(at);
403 ids.for_each(|id| f(s.ack_delete(group, id, refs)));
404 Ok(())
405 }
406
407 pub fn xnack(
416 &mut self,
417 key: &[u8],
418 group: &[u8],
419 retry: Retry,
420 force: bool,
421 ids: impl Iterator<Item = Id>,
422 ) -> Result<Option<u64>> {
423 let Some(at) = self.live_slot(key, Kind::Stream)? else {
424 return Ok(None);
425 };
426 let s = self.stream_at(at);
427 if s.group(group).is_none() {
428 return Ok(None);
429 }
430 let mut done = 0;
431 for id in ids {
432 done += u64::from(s.nack(group, id, retry, force).unwrap_or(false));
433 }
434 Ok(Some(done))
435 }
436
437 pub fn xtrim(&mut self, key: &[u8], trim: Trim) -> Result<u64> {
443 let Some(at) = self.live_slot(key, Kind::Stream)? else {
444 return Ok(0);
445 };
446 Ok(cut(self.stream_at(at), trim))
447 }
448
449 pub fn xsetid(
457 &mut self,
458 key: &[u8],
459 last: Id,
460 added: Option<u64>,
461 max_deleted: Option<Id>,
462 ) -> Result<()> {
463 let Some(at) = self.live_slot(key, Kind::Stream)? else {
464 return Err(Error::new(Code::NotFound, NO_SUCH_KEY));
465 };
466 if max_deleted.is_some_and(|id| last < id) {
470 return Err(Error::new(Code::Invalid, SETID_BELOW_MAX_DELETED));
471 }
472 self.stream_at(at)
473 .set_id(last, added, max_deleted)
474 .map_err(|_| Error::new(Code::Invalid, SETID_TOO_SMALL))
475 }
476
477 pub fn xrange_into<F>(
487 &mut self,
488 key: &[u8],
489 start: Id,
490 end: Id,
491 count: Option<usize>,
492 rev: bool,
493 f: F,
494 ) -> Result<usize>
495 where
496 F: FnMut(Id, Fields<'_>) -> bool,
497 {
498 let Some(s) = self.stream(key)? else {
499 return Ok(0);
500 };
501 Ok(if rev {
502 s.rev_range(start, end, count, f)
503 } else {
504 s.range(start, end, count, f)
505 })
506 }
507
508 pub fn xread_into<F>(
518 &mut self,
519 key: &[u8],
520 after: Id,
521 count: Option<usize>,
522 f: F,
523 ) -> Result<usize>
524 where
525 F: FnMut(Id, Fields<'_>) -> bool,
526 {
527 let Some(from) = after.next() else {
528 return Ok(0);
529 };
530 self.xrange_into(key, from, Id::MAX, count, false, f)
531 }
532
533 pub fn xgroup_create(
543 &mut self,
544 key: &[u8],
545 group: &[u8],
546 at: Start,
547 mkstream: bool,
548 read: Option<u64>,
549 ) -> Result<bool> {
550 let slot = match self.live_slot(key, Kind::Stream)? {
551 Some(slot) => slot,
552 None if mkstream => self.new_stream(key),
553 None => return Err(Error::new(Code::NotFound, NO_KEY_FOR_GROUP)),
554 };
555 let s = self.stream_at(slot);
556 let last = position(s, at);
557 let read = capped(read, s);
563 Ok(s.create_group(group, last, read))
564 }
565
566 pub fn xgroup_destroy(&mut self, key: &[u8], group: &[u8]) -> Result<bool> {
579 let Some(at) = self.live_slot(key, Kind::Stream)? else {
580 return Err(Error::new(Code::NotFound, NO_KEY_FOR_GROUP));
581 };
582 Ok(self.stream_at(at).destroy_group(group))
583 }
584
585 pub fn xgroup_setid(
594 &mut self,
595 key: &[u8],
596 group: &[u8],
597 at: Start,
598 read: Option<u64>,
599 ) -> Result<Option<()>> {
600 let Some(slot) = self.live_slot(key, Kind::Stream)? else {
601 return Err(Error::new(Code::NotFound, NO_KEY_FOR_GROUP));
602 };
603 let s = self.stream_at(slot);
604 let last = position(s, at);
605 let read = capped(read, s);
606 let Some(g) = s.group_mut(group) else {
607 return Ok(None);
608 };
609 g.set_id(last, read);
616 Ok(Some(()))
617 }
618
619 pub fn xgroup_create_consumer(
629 &mut self,
630 key: &[u8],
631 group: &[u8],
632 consumer: &[u8],
633 now: u64,
634 ) -> Result<Option<bool>> {
635 let Some(g) = self.group_mut_of(key, group)? else {
636 return Ok(None);
637 };
638 Ok(Some(g.create_consumer(consumer, now)))
639 }
640
641 pub fn xgroup_del_consumer(
651 &mut self,
652 key: &[u8],
653 group: &[u8],
654 consumer: &[u8],
655 ) -> Result<Option<u64>> {
656 let Some(g) = self.group_mut_of(key, group)? else {
657 return Ok(None);
658 };
659 Ok(Some(g.delete_consumer(consumer)))
660 }
661
662 pub fn xreadgroup_into<F>(
675 &mut self,
676 key: &[u8],
677 want: Read<'_>,
678 now: u64,
679 mut f: F,
680 ) -> Result<Option<usize>>
681 where
682 F: FnMut(Id, Option<Fields<'_>>) -> bool,
683 {
684 let Some(at) = self.live_slot(key, Kind::Stream)? else {
685 return Ok(None);
686 };
687 let s = self.stream_at(at);
688 Ok(match want.from {
689 From::New => s.read_group(
690 want.group,
691 want.consumer,
692 want.count,
693 want.noack,
694 now,
695 |id, fields| f(id, Some(fields)),
696 ),
697 From::Pending(after) => {
698 s.read_group_pending(want.group, want.consumer, after, want.count, now, &mut f)
699 }
700 })
701 }
702
703 pub fn xack(&mut self, key: &[u8], group: &[u8], ids: impl Iterator<Item = Id>) -> Result<u64> {
709 let Some(at) = self.live_slot(key, Kind::Stream)? else {
710 return Ok(0);
711 };
712 let Some(g) = self.stream_at(at).group_mut(group) else {
713 return Ok(0);
714 };
715 Ok(ids.filter(|&id| g.ack(id)).count() as u64)
716 }
717
718 pub fn xpending_into<F>(
727 &mut self,
728 key: &[u8],
729 group: &[u8],
730 want: Filter,
731 now: u64,
732 f: F,
733 ) -> Result<Option<usize>>
734 where
735 F: FnMut(Id, &crate::stream::Nack, Option<&crate::stream::Consumer>) -> bool,
736 {
737 let Some(s) = self.stream(key)? else {
738 return Ok(None);
739 };
740 let Some(g) = s.group(group) else {
741 return Ok(None);
742 };
743 Ok(Some(g.pending_range(want, now, f)))
744 }
745
746 pub fn xclaim(
757 &mut self,
758 key: &[u8],
759 ids: &[Id],
760 how: Claim<'_>,
761 now: u64,
762 gone: &mut Vec<Id>,
763 ) -> Result<Option<Vec<Id>>> {
764 let Some(at) = self.live_slot(key, Kind::Stream)? else {
765 return Ok(None);
766 };
767 Ok(self.stream_at(at).claim(
768 how.group,
769 how.consumer,
770 ids,
771 how.min_idle,
772 how.time,
773 how.retry,
774 how.bump,
775 how.force,
776 now,
777 gone,
778 ))
779 }
780
781 pub fn xautoclaim(
791 &mut self,
792 key: &[u8],
793 start: Id,
794 how: Claim<'_>,
795 count: usize,
796 now: u64,
797 gone: &mut Vec<Id>,
798 ) -> Result<Option<(Option<Id>, Vec<Id>)>> {
799 let Some(at) = self.live_slot(key, Kind::Stream)? else {
800 return Ok(None);
801 };
802 Ok(self.stream_at(at).autoclaim(
803 how.group,
804 how.consumer,
805 start,
806 how.min_idle,
807 count,
808 how.bump,
809 now,
810 gone,
811 ))
812 }
813
814 fn group_mut_of(&mut self, key: &[u8], group: &[u8]) -> Result<Option<&mut Group>> {
822 let Some(at) = self.live_slot(key, Kind::Stream)? else {
823 return Err(Error::new(Code::NotFound, NO_KEY_FOR_GROUP));
824 };
825 Ok(self.stream_at(at).group_mut(group))
826 }
827
828 fn stream_at(&mut self, at: u32) -> &mut Stream {
829 self.streams
830 .get_mut(at)
831 .expect("the record points at its body")
832 }
833
834 fn new_stream(&mut self, key: &[u8]) -> u32 {
835 let at = self.streams.insert(Stream::new());
836 let len = value::slot_record_len(false);
837 self.write_rec(key, len, |out| {
838 value::write_slot_record(out, Kind::Stream, at, None);
839 });
840 self.bodies += 1;
841 at
842 }
843}
844
845fn position(s: &Stream, at: Start) -> Id {
847 match at {
848 Start::Last => s.last_id(),
849 Start::At(id) => id,
850 }
851}
852
853fn capped(read: Option<u64>, s: &Stream) -> Option<u64> {
862 read.map(|n| n.min(s.added()))
863}
864
865fn cut(s: &mut Stream, trim: Trim) -> u64 {
867 match trim {
868 Trim::None => 0,
869 Trim::MaxLen { len, exact, limit } => s.trim_maxlen(len, exact, limit),
870 Trim::MinId { id, exact, limit } => s.trim_minid(id, exact, limit),
871 }
872}
873
874fn exhausted() -> Error {
875 Error::new(Code::Invalid, EXHAUSTED)
876}
877
878fn refused(why: Refused) -> Error {
879 match why {
880 Refused::Zero => Error::new(Code::Invalid, ZERO_ID),
881 Refused::NotGreater => Error::new(Code::Invalid, NOT_GREATER),
882 Refused::Full => exhausted(),
883 }
884}
885
886#[cfg(test)]
887mod tests {
888 use super::*;
889
890 fn db() -> Keyspace {
891 Keyspace::new()
892 }
893
894 fn add(d: &mut Keyspace, key: &[u8], id: Add) -> Id {
896 d.xadd(
897 key,
898 id,
899 &[(b"sensor", b"a4"), (b"reading", b"21.5")],
900 Trim::None,
901 true,
902 1_000,
903 )
904 .expect("a stream")
905 .expect("an ID")
906 }
907
908 fn ids(d: &mut Keyspace, key: &[u8]) -> Vec<Id> {
910 let mut out = Vec::new();
911 d.xrange_into(key, Id::MIN, Id::MAX, None, false, |id, _| {
912 out.push(id);
913 true
914 })
915 .expect("a stream");
916 out
917 }
918
919 #[test]
920 fn a_write_makes_the_key_and_a_read_finds_it() {
921 let mut d = db();
922 assert_eq!(d.kind_of(b"s"), None);
923 let id = add(&mut d, b"s", Add::At(Id::new(5, 0)));
924 assert_eq!(id, Id::new(5, 0));
925 assert_eq!(d.kind_of(b"s"), Some(Kind::Stream));
926 assert_eq!(d.type_name(b"s"), Some("stream"));
927 assert_eq!(d.encoding_name(b"s"), Some("stream"));
928 assert_eq!(ids(&mut d, b"s"), vec![Id::new(5, 0)]);
929 }
930
931 #[test]
932 fn nomkstream_leaves_a_missing_key_missing() {
933 let mut d = db();
934 let got = d
935 .xadd(b"s", Add::Auto, &[(b"f", b"v")], Trim::None, false, 1_000)
936 .expect("a stream");
937 assert_eq!(got, None);
938 assert_eq!(d.kind_of(b"s"), None);
939 }
940
941 #[test]
942 fn an_auto_id_follows_the_clock_and_then_the_last_id() {
943 let mut d = db();
944 assert_eq!(add(&mut d, b"s", Add::Auto), Id::new(1_000, 0));
945 assert_eq!(add(&mut d, b"s", Add::Auto), Id::new(1_000, 1));
947 assert_eq!(add(&mut d, b"s", Add::Seq(1_000)), Id::new(1_000, 2));
948 assert_eq!(add(&mut d, b"s", Add::Seq(2_000)), Id::new(2_000, 0));
949 }
950
951 #[test]
952 fn an_id_that_is_not_above_the_last_one_is_refused() {
953 let mut d = db();
954 add(&mut d, b"s", Add::At(Id::new(5, 0)));
955 let e = d
956 .xadd(
957 b"s",
958 Add::At(Id::new(5, 0)),
959 &[(b"f", b"v")],
960 Trim::None,
961 true,
962 1_000,
963 )
964 .expect_err("not above the last one");
965 assert_eq!(e.message(), NOT_GREATER);
966 let e = d
968 .xadd(b"s", Add::Seq(4), &[(b"f", b"v")], Trim::None, true, 1_000)
969 .expect_err("a millisecond that has gone by");
970 assert_eq!(e.message(), NOT_GREATER);
971 }
972
973 #[test]
974 fn zero_is_refused_and_says_so_in_its_own_words() {
975 let mut d = db();
976 let e = d
977 .xadd(
978 b"s",
979 Add::At(Id::MIN),
980 &[(b"f", b"v")],
981 Trim::None,
982 true,
983 1_000,
984 )
985 .expect_err("nothing sorts below zero");
986 assert_eq!(e.message(), ZERO_ID);
987 }
988
989 #[test]
990 fn a_stream_is_not_deleted_when_the_last_entry_goes() {
991 let mut d = db();
992 let id = add(&mut d, b"s", Add::At(Id::new(5, 0)));
993 assert_eq!(d.xdel(b"s", [id].into_iter()).expect("a stream"), 1);
994 assert_eq!(d.xdel(b"s", [id].into_iter()).expect("a stream"), 0);
995 assert_eq!(d.kind_of(b"s"), Some(Kind::Stream), "the key is still here");
996 assert_eq!(add(&mut d, b"s", Add::Auto), Id::new(1_000, 0));
999 }
1000
1001 #[test]
1002 fn a_trim_runs_after_the_append() {
1003 let mut d = db();
1004 for ms in 1..=10u64 {
1005 add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1006 }
1007 let trim = Trim::MaxLen {
1008 len: 1,
1009 exact: true,
1010 limit: None,
1011 };
1012 let id = d
1013 .xadd(
1014 b"s",
1015 Add::At(Id::new(11, 0)),
1016 &[(b"f", b"v")],
1017 trim,
1018 true,
1019 1,
1020 )
1021 .expect("a stream")
1022 .expect("an ID");
1023 assert_eq!(ids(&mut d, b"s"), vec![id]);
1026 }
1027
1028 #[test]
1029 fn a_limit_stops_a_trim_at_the_next_node_boundary() {
1030 let mut d = db();
1031 for ms in 1..=1_000u64 {
1032 add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1033 }
1034 let trim = Trim::MaxLen {
1035 len: 0,
1036 exact: false,
1037 limit: Some(10),
1038 };
1039 assert_eq!(d.xtrim(b"s", trim).expect("a stream"), 100);
1045 assert_eq!(
1046 d.stream(b"s").expect("a stream").expect("the key").len(),
1047 900
1048 );
1049 }
1050
1051 #[test]
1052 fn setid_moves_the_bookmark_and_refuses_to_go_below_an_entry() {
1053 let mut d = db();
1054 add(&mut d, b"s", Add::At(Id::new(5, 0)));
1055 let e = d
1056 .xsetid(b"s", Id::new(4, 0), None, None)
1057 .expect_err("below an entry that is still there");
1058 assert_eq!(e.message(), SETID_TOO_SMALL);
1059 d.xsetid(b"s", Id::new(9, 0), Some(41), None)
1060 .expect("above it");
1061 let s = d.stream(b"s").expect("a stream").expect("the key");
1062 assert_eq!((s.last_id(), s.added()), (Id::new(9, 0), 41));
1063 }
1064
1065 #[test]
1066 fn setid_on_a_key_that_is_not_there_says_so() {
1067 let mut d = db();
1068 let e = d
1069 .xsetid(b"s", Id::new(1, 0), None, None)
1070 .expect_err("no key");
1071 assert_eq!((e.code(), e.message()), (Code::NotFound, NO_SUCH_KEY));
1072 }
1073
1074 #[test]
1075 fn every_command_sees_the_wrong_type() {
1076 let mut d = db();
1077 d.set_plain(b"s", b"a string").expect("a string");
1078 for e in [
1079 d.xadd(b"s", Add::Auto, &[(b"f", b"v")], Trim::None, true, 1)
1080 .expect_err("a string"),
1081 d.xdel(b"s", [Id::new(1, 0)].into_iter())
1082 .expect_err("a string"),
1083 d.xtrim(b"s", Trim::None).expect_err("a string"),
1084 d.xrange_into(b"s", Id::MIN, Id::MAX, None, false, |_, _| true)
1085 .expect_err("a string"),
1086 d.xack(b"s", b"g", [Id::new(1, 0)].into_iter())
1087 .expect_err("a string"),
1088 ] {
1089 assert_eq!(e.code(), Code::WrongType);
1090 }
1091 }
1092
1093 #[test]
1100 fn a_new_group_has_no_read_counter() {
1101 let mut d = db();
1102 for ms in 1..=5 {
1103 add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1104 }
1105
1106 assert!(
1107 d.xgroup_create(b"s", b"early", Start::At(Id::MIN), false, None)
1108 .expect("a stream")
1109 );
1110 assert!(
1111 d.xgroup_create(b"s", b"late", Start::Last, false, None)
1112 .expect("a stream")
1113 );
1114 let s = d.stream(b"s").expect("a stream").expect("the key");
1115 let early = s.group(b"early").expect("the early group");
1116 assert_eq!(early.entries_read(), None);
1117 assert_eq!(s.lag(early), Some(5), "everything is still in front of it");
1118 let late = s.group(b"late").expect("the late group");
1119 assert_eq!(late.entries_read(), None);
1120 assert_eq!(s.lag(late), Some(0), "and nothing is in front of this one");
1121 }
1122
1123 #[test]
1131 fn a_read_counter_that_is_too_big_is_brought_back_down() {
1132 let mut d = db();
1133 for ms in 1..=3 {
1134 add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1135 }
1136
1137 d.xgroup_create(b"s", b"g", Start::At(Id::MIN), false, Some(99))
1138 .expect("a stream");
1139 assert_eq!(
1140 counter(&mut d, b"g"),
1141 Some(3),
1142 "held down to what was added"
1143 );
1144
1145 d.xgroup_setid(b"s", b"g", Start::At(Id::MIN), Some(2))
1146 .expect("a stream")
1147 .expect("the group");
1148 assert_eq!(counter(&mut d, b"g"), Some(2), "and left alone below that");
1149
1150 d.xgroup_setid(b"s", b"g", Start::At(Id::MIN), None)
1151 .expect("a stream")
1152 .expect("the group");
1153 assert_eq!(
1154 counter(&mut d, b"g"),
1155 None,
1156 "and given up when none is named"
1157 );
1158 }
1159
1160 fn counter(d: &mut Keyspace, group: &[u8]) -> Option<u64> {
1162 d.stream(b"s")
1163 .expect("a stream")
1164 .expect("the key")
1165 .group(group)
1166 .expect("the group")
1167 .entries_read()
1168 }
1169
1170 #[test]
1177 fn setid_refuses_a_last_id_under_its_own_deletion_mark() {
1178 let mut d = db();
1179 add(&mut d, b"s", Add::At(Id::new(5, 0)));
1180
1181 let e = d
1182 .xsetid(b"s", Id::new(9, 9), None, Some(Id::new(99, 99)))
1183 .expect_err("the pair contradicts itself");
1184 assert_eq!(e.message(), SETID_BELOW_MAX_DELETED);
1185
1186 d.xsetid(b"s", Id::new(99, 99), None, Some(Id::new(9, 9)))
1187 .expect("the other way round is fine");
1188 let s = d.stream(b"s").expect("a stream").expect("the key");
1189 assert_eq!(s.last_id(), Id::new(99, 99));
1190 assert_eq!(s.max_deleted_id(), Id::new(9, 9));
1191 }
1192
1193 #[test]
1200 fn a_history_read_by_a_name_nobody_has_used_makes_the_consumer() {
1201 let mut d = db();
1202 add(&mut d, b"s", Add::At(Id::new(1, 0)));
1203 d.xgroup_create(b"s", b"g", Start::At(Id::MIN), false, None)
1204 .expect("a stream");
1205
1206 let want = Read {
1207 group: b"g",
1208 consumer: b"newbie",
1209 from: From::Pending(Id::MIN),
1210 count: None,
1211 noack: false,
1212 };
1213 let seen = d
1214 .xreadgroup_into(b"s", want, 500, |_, _| true)
1215 .expect("a stream")
1216 .expect("the group is there");
1217 assert_eq!(seen, 0, "nothing was ever handed to this name");
1218
1219 let s = d.stream(b"s").expect("a stream").expect("the key");
1220 let c = s
1221 .group(b"g")
1222 .expect("the group")
1223 .consumer_named(b"newbie")
1224 .expect("the read made it");
1225 assert_eq!(c.seen(), 500, "it was heard from");
1226 assert_eq!(c.active(), None, "and it has never had anything");
1227 }
1228
1229 #[test]
1230 fn a_group_reads_what_arrives_after_it_was_made() {
1231 let mut d = db();
1232 add(&mut d, b"s", Add::At(Id::new(1, 0)));
1233 assert!(
1234 d.xgroup_create(b"s", b"workers", Start::Last, false, None)
1235 .expect("a stream")
1236 );
1237 assert!(
1240 !d.xgroup_create(b"s", b"workers", Start::Last, false, None)
1241 .expect("a stream")
1242 );
1243 add(&mut d, b"s", Add::At(Id::new(2, 0)));
1244
1245 let mut got = Vec::new();
1246 let seen = d
1247 .xreadgroup_into(
1248 b"s",
1249 Read {
1250 group: b"workers",
1251 consumer: b"alice",
1252 from: From::New,
1253 count: None,
1254 noack: false,
1255 },
1256 1_000,
1257 |id, fields| {
1258 got.push((id, fields.is_some()));
1259 true
1260 },
1261 )
1262 .expect("a stream")
1263 .expect("a group");
1264 assert_eq!(seen, 1, "only what arrived after the group was made");
1265 assert_eq!(got, vec![(Id::new(2, 0), true)]);
1266 assert_eq!(
1267 d.xack(b"s", b"workers", [Id::new(2, 0)].into_iter())
1268 .expect("a stream"),
1269 1
1270 );
1271 }
1272
1273 #[test]
1274 fn noack_hands_the_entry_over_without_writing_it_down() {
1275 let mut d = db();
1276 add(&mut d, b"s", Add::At(Id::new(1, 0)));
1277 d.xgroup_create(b"s", b"workers", Start::At(Id::MIN), false, None)
1278 .expect("a stream");
1279 let seen = d
1280 .xreadgroup_into(
1281 b"s",
1282 Read {
1283 group: b"workers",
1284 consumer: b"alice",
1285 from: From::New,
1286 count: None,
1287 noack: true,
1288 },
1289 1_000,
1290 |_, _| true,
1291 )
1292 .expect("a stream")
1293 .expect("a group");
1294 assert_eq!(seen, 1);
1295 let s = d.stream(b"s").expect("a stream").expect("the key");
1296 let g = s.group(b"workers").expect("the group");
1297 assert_eq!(g.pending_len(), 0, "nothing was written down");
1298 assert_eq!(g.last_id(), Id::new(1, 0), "the bookmark still moved");
1299 assert_eq!(s.lag(g), Some(0), "and the group has caught up");
1300 }
1301
1302 #[test]
1303 fn a_missing_group_is_a_none_and_not_an_error() {
1304 let mut d = db();
1305 add(&mut d, b"s", Add::At(Id::new(1, 0)));
1306 let got = d
1307 .xreadgroup_into(
1308 b"s",
1309 Read {
1310 group: b"nope",
1311 consumer: b"alice",
1312 from: From::New,
1313 count: None,
1314 noack: false,
1315 },
1316 1,
1317 |_, _| true,
1318 )
1319 .expect("a stream");
1320 assert_eq!(got, None);
1321 assert!(!d.xgroup_destroy(b"s", b"nope").expect("a stream"));
1322 assert_eq!(
1323 d.xgroup_create_consumer(b"s", b"nope", b"alice", 1)
1324 .expect("a stream"),
1325 None
1326 );
1327 }
1328
1329 #[test]
1330 fn xgroup_needs_the_key_and_says_which_option_makes_one() {
1331 let mut d = db();
1332 let e = d
1333 .xgroup_create(b"s", b"workers", Start::Last, false, None)
1334 .expect_err("no key");
1335 assert_eq!((e.code(), e.message()), (Code::NotFound, NO_KEY_FOR_GROUP));
1336 assert!(
1337 d.xgroup_create(b"s", b"workers", Start::Last, true, None)
1338 .expect("mkstream made one")
1339 );
1340 assert_eq!(d.kind_of(b"s"), Some(Kind::Stream));
1341 }
1342
1343 #[test]
1344 fn a_claim_moves_an_idle_entry_and_drops_one_that_has_gone() {
1345 let mut d = db();
1346 for ms in 1..=2u64 {
1347 add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1348 }
1349 d.xgroup_create(b"s", b"workers", Start::At(Id::MIN), false, None)
1350 .expect("a stream");
1351 d.xreadgroup_into(
1352 b"s",
1353 Read {
1354 group: b"workers",
1355 consumer: b"alice",
1356 from: From::New,
1357 count: None,
1358 noack: false,
1359 },
1360 100,
1361 |_, _| true,
1362 )
1363 .expect("a stream")
1364 .expect("a group");
1365 d.xdel(b"s", [Id::new(1, 0)].into_iter()).expect("a stream");
1366
1367 let mut gone = Vec::new();
1368 let how = Claim {
1369 group: b"workers",
1370 consumer: b"bob",
1371 min_idle: 500,
1372 time: 1_000,
1373 ..Claim::default()
1374 };
1375 let took = d
1376 .xclaim(b"s", &[Id::new(1, 0), Id::new(2, 0)], how, 1_000, &mut gone)
1377 .expect("a stream")
1378 .expect("a group");
1379 assert_eq!(took, vec![Id::new(2, 0)]);
1380 assert_eq!(gone, vec![Id::new(1, 0)], "no one can ever finish that one");
1381 }
1382
1383 #[test]
1384 fn an_autoclaim_sweeps_from_a_cursor() {
1385 let mut d = db();
1386 for ms in 1..=10u64 {
1387 add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1388 }
1389 d.xgroup_create(b"s", b"workers", Start::At(Id::MIN), false, None)
1390 .expect("a stream");
1391 d.xreadgroup_into(
1392 b"s",
1393 Read {
1394 group: b"workers",
1395 consumer: b"alice",
1396 from: From::New,
1397 count: None,
1398 noack: false,
1399 },
1400 100,
1401 |_, _| true,
1402 )
1403 .expect("a stream")
1404 .expect("a group");
1405
1406 let mut gone = Vec::new();
1407 let how = Claim {
1408 group: b"workers",
1409 consumer: b"bob",
1410 min_idle: 500,
1411 time: 1_000,
1412 ..Claim::default()
1413 };
1414 let (cursor, took) = d
1415 .xautoclaim(b"s", Id::MIN, how, 4, 1_000, &mut gone)
1416 .expect("a stream")
1417 .expect("a group");
1418 assert_eq!(took.len(), 4);
1419 assert_eq!(cursor, Some(Id::new(5, 0)), "where the next call starts");
1420 assert!(gone.is_empty());
1421 }
1422
1423 #[test]
1424 fn a_pending_window_carries_the_owner_and_the_counts() {
1425 let mut d = db();
1426 for ms in 1..=3u64 {
1427 add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1428 }
1429 d.xgroup_create(b"s", b"workers", Start::At(Id::MIN), false, None)
1430 .expect("a stream");
1431 d.xreadgroup_into(
1432 b"s",
1433 Read {
1434 group: b"workers",
1435 consumer: b"alice",
1436 from: From::New,
1437 count: None,
1438 noack: false,
1439 },
1440 100,
1441 |_, _| true,
1442 )
1443 .expect("a stream")
1444 .expect("a group");
1445
1446 let mut out = Vec::new();
1447 let seen = d
1448 .xpending_into(b"s", b"workers", Filter::default(), 600, |id, nack, c| {
1449 out.push((
1450 id,
1451 nack.count(),
1452 nack.idle(600),
1453 c.expect("an owner").name().to_vec(),
1454 ));
1455 true
1456 })
1457 .expect("a stream")
1458 .expect("a group");
1459 assert_eq!(seen, 3);
1460 assert_eq!(out[0], (Id::new(1, 0), 1, 500, b"alice".to_vec()));
1461 }
1462
1463 #[test]
1464 fn a_history_read_hands_back_a_null_for_an_entry_that_has_gone() {
1465 let mut d = db();
1466 for ms in 1..=2u64 {
1467 add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1468 }
1469 d.xgroup_create(b"s", b"workers", Start::At(Id::MIN), false, None)
1470 .expect("a stream");
1471 d.xreadgroup_into(
1472 b"s",
1473 Read {
1474 group: b"workers",
1475 consumer: b"alice",
1476 from: From::New,
1477 count: None,
1478 noack: false,
1479 },
1480 100,
1481 |_, _| true,
1482 )
1483 .expect("a stream")
1484 .expect("a group");
1485 d.xdel(b"s", [Id::new(1, 0)].into_iter()).expect("a stream");
1486
1487 let mut out = Vec::new();
1488 d.xreadgroup_into(
1489 b"s",
1490 Read {
1491 group: b"workers",
1492 consumer: b"alice",
1493 from: From::Pending(Id::MIN),
1494 count: None,
1495 noack: false,
1496 },
1497 2_000,
1498 |id, fields| {
1499 out.push((id, fields.is_some()));
1500 true
1501 },
1502 )
1503 .expect("a stream")
1504 .expect("a group");
1505 assert_eq!(out, vec![(Id::new(1, 0), false), (Id::new(2, 0), true)]);
1506 }
1507
1508 #[test]
1509 fn a_stream_counts_against_the_memory_total() {
1510 let mut d = db();
1511 let before = d.memory_bytes();
1512 for ms in 1..=1_000u64 {
1513 add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1514 }
1515 let after = d.memory_bytes();
1516 assert!(after > before + 1_000, "{before} then {after}");
1517 assert!(d.del(b"s"), "the key was there");
1518 assert_eq!(d.kind_of(b"s"), None);
1519 }
1520
1521 #[test]
1522 fn a_deadline_goes_on_a_stream_the_same_as_on_anything_else() {
1523 let mut d = db();
1524 add(&mut d, b"s", Add::At(Id::new(1, 0)));
1525 assert!(d.set_expiry(b"s", Some(1 << 45)));
1526 assert_eq!(d.kind_of(b"s"), Some(Kind::Stream));
1527 assert_eq!(ids(&mut d, b"s"), vec![Id::new(1, 0)], "the body is intact");
1528 }
1529}