1use std::cmp::Ordering;
108use std::collections::VecDeque;
109
110use yo_common::num::{DIGITS_MAX, i64_digits, push_u64, u64_digits};
111
112use crate::frozen::{self, Broken};
113use crate::listpack::{self, Entry, Listpack};
114
115pub mod groups;
116
117pub use groups::{Consumer, Filter, Group, Nack, Retry};
118
119pub const NODE_BYTES: usize = 4096;
126
127pub const NODE_ENTRIES: usize = 100;
131
132const LIVE: i64 = 0;
134
135const DELETED: i64 = 1;
137
138const SAME_FIELDS: i64 = 2;
140
141const MASTER_FIELDS: usize = 3;
145
146#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
152pub struct Id {
153 pub ms: u64,
155 pub seq: u64,
157}
158
159impl Id {
160 pub const MIN: Id = Id { ms: 0, seq: 0 };
162
163 pub const MAX: Id = Id {
165 ms: u64::MAX,
166 seq: u64::MAX,
167 };
168
169 #[must_use]
171 #[inline]
172 pub const fn new(ms: u64, seq: u64) -> Id {
173 Id { ms, seq }
174 }
175
176 #[must_use]
181 pub const fn next(self) -> Option<Id> {
182 if self.seq != u64::MAX {
183 Some(Id {
184 ms: self.ms,
185 seq: self.seq + 1,
186 })
187 } else if self.ms != u64::MAX {
188 Some(Id {
189 ms: self.ms + 1,
190 seq: 0,
191 })
192 } else {
193 None
194 }
195 }
196
197 #[must_use]
199 pub const fn prev(self) -> Option<Id> {
200 if self.seq != 0 {
201 Some(Id {
202 ms: self.ms,
203 seq: self.seq - 1,
204 })
205 } else if self.ms != 0 {
206 Some(Id {
207 ms: self.ms - 1,
208 seq: u64::MAX,
209 })
210 } else {
211 None
212 }
213 }
214
215 #[must_use]
220 pub fn to_bytes(self) -> [u8; 16] {
221 let mut out = [0u8; 16];
222 out[..8].copy_from_slice(&self.ms.to_be_bytes());
223 out[8..].copy_from_slice(&self.seq.to_be_bytes());
224 out
225 }
226
227 #[must_use]
229 pub fn from_bytes(bytes: [u8; 16]) -> Id {
230 let mut ms = [0u8; 8];
231 let mut seq = [0u8; 8];
232 ms.copy_from_slice(&bytes[..8]);
233 seq.copy_from_slice(&bytes[8..]);
234 Id {
235 ms: u64::from_be_bytes(ms),
236 seq: u64::from_be_bytes(seq),
237 }
238 }
239
240 pub fn write_to(self, out: &mut Vec<u8>) {
242 push_u64(out, self.ms);
243 out.push(b'-');
244 push_u64(out, self.seq);
245 }
246
247 #[must_use]
249 pub fn to_vec(self) -> Vec<u8> {
250 let mut out = Vec::with_capacity(41);
251 self.write_to(&mut out);
252 out
253 }
254
255 #[must_use]
262 pub fn parse(s: &[u8], default: u64) -> Option<Id> {
263 let (ms, seq) = match s.iter().position(|c| *c == b'-') {
264 Some(at) => (&s[..at], Some(&s[at + 1..])),
265 None => (s, None),
266 };
267 Some(Id {
268 ms: digits(ms)?,
269 seq: match seq {
270 Some(seq) => digits(seq)?,
271 None => default,
272 },
273 })
274 }
275}
276
277fn digits(s: &[u8]) -> Option<u64> {
282 if s.is_empty() || s.len() > 20 {
283 return None;
284 }
285 let mut n = 0u64;
286 for c in s {
287 let d = c.wrapping_sub(b'0');
288 if d > 9 {
289 return None;
290 }
291 n = n.checked_mul(10)?.checked_add(u64::from(d))?;
292 }
293 Some(n)
294}
295
296#[derive(Debug, Clone, Copy, PartialEq, Eq)]
298pub enum Refused {
299 NotGreater,
301 Zero,
303 Full,
305}
306
307#[derive(Debug, Clone, Copy, PartialEq, Eq)]
309pub struct Limits {
310 pub max_node_bytes: usize,
312 pub max_node_entries: usize,
314}
315
316impl Default for Limits {
317 fn default() -> Limits {
319 Limits {
320 max_node_bytes: NODE_BYTES,
321 max_node_entries: NODE_ENTRIES,
322 }
323 }
324}
325
326#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
333pub enum Refs {
334 #[default]
337 Keep,
338 Drop,
340 Acked,
342}
343
344#[derive(Debug, Clone, Copy, PartialEq, Eq)]
350pub enum Fate {
351 Missing,
353 Gone,
355 Held,
357}
358
359impl Fate {
360 #[must_use]
362 #[inline]
363 pub fn code(self) -> i64 {
364 match self {
365 Fate::Missing => -1,
366 Fate::Gone => 1,
367 Fate::Held => 2,
368 }
369 }
370}
371
372#[derive(Debug, Clone, PartialEq, Eq)]
374struct Node {
375 master: Id,
380 lp: Listpack,
381}
382
383#[derive(Debug, Clone, Copy)]
391struct Edges {
392 added: u64,
393 length: u64,
394 first: Option<Id>,
395 last: Id,
396 max_deleted: Id,
397}
398
399impl Edges {
400 fn estimate(&self, id: Id) -> Option<u64> {
412 if self.added == 0 {
413 return Some(0);
414 }
415 if id >= self.last {
416 return Some(self.added);
417 }
418 let first = self.first?;
419 if self.max_deleted != Id::MIN && self.max_deleted >= first {
423 return None;
424 }
425 let behind = self.added - self.length;
426 match id.cmp(&first) {
427 Ordering::Less => Some(behind),
428 Ordering::Equal => Some(behind + 1),
429 Ordering::Greater => None,
430 }
431 }
432
433 fn holed_from(&self, id: Id) -> bool {
439 if self.length == 0 || self.max_deleted == Id::MIN {
440 return false;
441 }
442 if self.first.is_some_and(|first| first > self.max_deleted) {
443 return false;
444 }
445 id <= self.max_deleted
446 }
447
448 fn lag(&self, group: &Group) -> Option<u64> {
450 if let Some(read) = self.estimate(group.last_id()) {
451 return Some(self.added.saturating_sub(read));
452 }
453 let read = group.entries_read()?;
454 if self.holed_from(group.last_id()) {
455 return None;
456 }
457 Some(self.added.saturating_sub(read))
458 }
459
460 fn on_deliver(&self, group: &Group, id: Id) -> Option<u64> {
467 match group.entries_read() {
468 Some(read) if !self.holed_from(id) => Some(read + 1),
469 _ => self.estimate(id),
470 }
471 }
472}
473
474const FORM_NODES: u8 = 1;
480
481#[derive(Debug, Clone, Copy)]
495pub(crate) struct Cursor {
496 epoch: u64,
498 next: Id,
502 master: Id,
504 byte: usize,
506}
507
508#[derive(Debug, Clone, Default)]
510pub struct Stream {
511 nodes: VecDeque<Node>,
512 length: u64,
514 last: Id,
516 max_deleted: Id,
518 added: u64,
523 groups: Vec<(Vec<u8>, Group)>,
529 epoch: u64,
537}
538
539impl PartialEq for Stream {
545 fn eq(&self, other: &Stream) -> bool {
546 self.nodes == other.nodes
547 && self.length == other.length
548 && self.last == other.last
549 && self.max_deleted == other.max_deleted
550 && self.added == other.added
551 && self.groups == other.groups
552 }
553}
554
555impl Eq for Stream {}
556
557impl Stream {
558 #[must_use]
560 pub fn new() -> Stream {
561 Stream::default()
562 }
563
564 pub(crate) fn raw_nodes(&self) -> impl Iterator<Item = (Id, &[u8])> + '_ {
572 self.nodes.iter().map(|n| (n.master, n.lp.as_bytes()))
573 }
574
575 pub(crate) fn push_raw_node(&mut self, master: Id, lp: Listpack) -> bool {
582 if self.nodes.back().is_some_and(|n| n.master >= master) {
583 return false;
584 }
585 self.nodes.push_back(Node { master, lp });
586 true
587 }
588
589 pub(crate) fn set_counters(
594 &mut self,
595 length: u64,
596 last: Id,
597 max_deleted: Id,
598 added: u64,
599 ) -> bool {
600 if length > added || max_deleted > last || (self.nodes.is_empty() && length != 0) {
601 return false;
602 }
603 self.length = length;
604 self.last = last;
605 self.max_deleted = max_deleted;
606 self.added = added;
607 true
608 }
609
610 pub(crate) fn push_group(&mut self, name: &[u8], group: Group) -> bool {
612 if self.groups.iter().any(|(had, _)| had == name) {
613 return false;
614 }
615 self.groups.push((name.to_vec(), group));
616 true
617 }
618
619 pub fn freeze(&self, out: &mut Vec<u8>) {
628 out.push(FORM_NODES);
629 frozen::put_uint(out, self.length);
630 frozen::put_uint(out, self.last.ms);
631 frozen::put_uint(out, self.last.seq);
632 frozen::put_uint(out, self.max_deleted.ms);
633 frozen::put_uint(out, self.max_deleted.seq);
634 frozen::put_uint(out, self.added);
635
636 frozen::put_uint(out, self.nodes.len() as u64);
637 for node in &self.nodes {
638 frozen::put_uint(out, node.master.ms);
639 frozen::put_uint(out, node.master.seq);
640 frozen::put_bytes(out, node.lp.as_bytes());
641 }
642
643 frozen::put_uint(out, self.groups.len() as u64);
644 for (name, group) in &self.groups {
645 frozen::put_bytes(out, name);
646 group.freeze(out);
647 }
648 }
649
650 pub fn thaw(bytes: &[u8]) -> Result<Stream, Broken> {
660 let mut cut = frozen::Cut::new(bytes);
661 if cut.byte()? != FORM_NODES {
662 return Err(Broken::Form);
663 }
664 let length = cut.uint()?;
665 let last = Id::new(cut.uint()?, cut.uint()?);
666 let max_deleted = Id::new(cut.uint()?, cut.uint()?);
667 let added = cut.uint()?;
668 if length > added || max_deleted > last {
672 return Err(Broken::Body);
673 }
674
675 let n = usize::try_from(cut.uint()?).map_err(|_| Broken::Short)?;
676 if n > cut.rest().len() {
679 return Err(Broken::Short);
680 }
681 let mut nodes = VecDeque::with_capacity(n);
682 let mut prev: Option<Id> = None;
683 for _ in 0..n {
684 let master = Id::new(cut.uint()?, cut.uint()?);
685 if prev.is_some_and(|p| p >= master) {
689 return Err(Broken::Body);
690 }
691 prev = Some(master);
692 let lp = Listpack::from_bytes(cut.bytes()?).map_err(|_| Broken::Body)?;
693 nodes.push_back(Node { master, lp });
694 }
695 if nodes.is_empty() && length != 0 {
696 return Err(Broken::Body);
697 }
698
699 let n = usize::try_from(cut.uint()?).map_err(|_| Broken::Short)?;
700 if n > cut.rest().len() {
701 return Err(Broken::Short);
702 }
703 let mut groups: Vec<(Vec<u8>, Group)> = Vec::with_capacity(n);
704 for _ in 0..n {
705 let name = cut.bytes()?;
706 if groups.iter().any(|(had, _)| had == name) {
709 return Err(Broken::Body);
710 }
711 groups.push((name.to_vec(), Group::thaw(&mut cut)?));
712 }
713
714 Ok(Stream {
715 nodes,
716 length,
717 last,
718 max_deleted,
719 added,
720 groups,
721 epoch: 0,
722 })
723 }
724
725 #[must_use]
727 #[inline]
728 pub fn len(&self) -> u64 {
729 self.length
730 }
731
732 #[must_use]
738 #[inline]
739 pub fn is_empty(&self) -> bool {
740 self.length == 0
741 }
742
743 #[must_use]
745 #[inline]
746 pub fn last_id(&self) -> Id {
747 self.last
748 }
749
750 #[must_use]
752 #[inline]
753 pub fn max_deleted_id(&self) -> Id {
754 self.max_deleted
755 }
756
757 #[must_use]
759 #[inline]
760 pub fn added(&self) -> u64 {
761 self.added
762 }
763
764 #[must_use]
770 pub fn first_id(&self) -> Option<Id> {
771 let mut found = None;
772 self.walk(Id::MIN, Id::MAX, Some(1), &mut |id, _| {
773 found = Some(id);
774 false
775 });
776 found
777 }
778
779 #[must_use]
787 pub fn top_id(&self) -> Option<Id> {
788 let mut found = None;
789 self.rev_range(Id::MIN, Id::MAX, Some(1), |id, _| {
790 found = Some(id);
791 false
792 });
793 found
794 }
795
796 pub fn set_id(
810 &mut self,
811 last: Id,
812 added: Option<u64>,
813 max_deleted: Option<Id>,
814 ) -> Result<(), Refused> {
815 if self.top_id().is_some_and(|top| last < top) {
816 return Err(Refused::NotGreater);
817 }
818 self.last = last;
819 if let Some(added) = added {
820 self.added = added;
821 }
822 if let Some(id) = max_deleted {
823 self.max_deleted = id;
824 }
825 Ok(())
826 }
827
828 #[must_use]
834 pub fn auto_id(&self, now: u64) -> Option<Id> {
835 if now > self.last.ms {
836 Some(Id { ms: now, seq: 0 })
837 } else {
838 self.last.next()
839 }
840 }
841
842 #[must_use]
844 pub fn auto_seq(&self, ms: u64) -> Option<Id> {
845 if ms > self.last.ms {
846 Some(Id { ms, seq: 0 })
847 } else if ms == self.last.ms {
848 self.last.next().filter(|id| id.ms == ms)
849 } else {
850 None
851 }
852 }
853
854 pub fn append(
862 &mut self,
863 id: Id,
864 fields: &[(&[u8], &[u8])],
865 limits: Limits,
866 ) -> Result<(), Refused> {
867 if id == Id::MIN {
868 return Err(Refused::Zero);
869 }
870 if id <= self.last {
871 return Err(Refused::NotGreater);
872 }
873
874 let size: usize = fields
878 .iter()
879 .map(|(f, v)| f.len() + v.len() + 11)
880 .sum::<usize>()
881 + 32;
882
883 let fits = match self.nodes.back() {
884 Some(node) => {
885 let (count, deleted) = counts(&node.lp);
886 node.lp.byte_len() + size < limits.max_node_bytes
887 && (count + deleted) < limits.max_node_entries as u64
888 }
889 None => false,
890 };
891
892 if !fits {
893 self.nodes.push_back(Node {
894 master: id,
895 lp: master_of(fields),
896 });
897 }
898 let node = self.nodes.back_mut().expect("a node was just made sure of");
899 let same = same_fields(&node.lp, fields);
900 write_entry(&mut node.lp, node.master, id, fields, same);
901 if bump(&mut node.lp, 1, 0) {
906 self.epoch = self.epoch.wrapping_add(1);
907 }
908
909 self.length += 1;
910 self.added += 1;
911 self.last = id;
912 Ok(())
913 }
914
915 pub fn delete(&mut self, id: Id) -> bool {
920 if !self.remove(id) {
921 return false;
922 }
923 self.max_deleted = self.max_deleted.max(id);
924 true
925 }
926
927 pub fn delete_ref(&mut self, id: Id, refs: Refs) -> Fate {
936 if refs == Refs::Acked && self.still_wanted(id) {
937 return Fate::Held;
938 }
939 if !self.delete(id) {
940 return Fate::Missing;
941 }
942 if refs == Refs::Drop {
943 self.drop_refs(id);
944 }
945 Fate::Gone
946 }
947
948 pub fn ack_delete(&mut self, group: &[u8], id: Id, refs: Refs) -> Fate {
955 let Some(g) = self.group_mut(group) else {
956 return Fate::Missing;
957 };
958 if !g.ack(id) {
959 return Fate::Missing;
960 }
961 if refs == Refs::Acked && self.still_wanted(id) {
962 return Fate::Held;
963 }
964 self.delete(id);
965 if refs == Refs::Drop {
966 self.drop_refs(id);
967 }
968 Fate::Gone
969 }
970
971 pub fn nack(&mut self, group: &[u8], id: Id, retry: Retry, force: bool) -> Option<bool> {
978 let here = self.contains(id);
979 let g = self.group_mut(group)?;
980 if g.release(id, retry) {
981 return Some(true);
982 }
983 if force && here {
984 g.force_release(id, retry);
985 return Some(true);
986 }
987 Some(false)
988 }
989
990 fn still_wanted(&self, id: Id) -> bool {
992 self.groups
993 .iter()
994 .any(|(_, g)| g.nack(id).is_some() || id > g.last_id())
995 }
996
997 fn drop_refs(&mut self, id: Id) {
999 for (_, g) in &mut self.groups {
1000 g.forget(id);
1001 }
1002 }
1003
1004 fn remove(&mut self, id: Id) -> bool {
1011 let Some(at) = self.node_of(id) else {
1012 return false;
1013 };
1014 let node = &self.nodes[at];
1015 let Some((offset, flags)) = find(&node.lp, node.master, id) else {
1016 return false;
1017 };
1018 if flags & DELETED != 0 {
1019 return false;
1020 }
1021
1022 let (count, _) = counts(&node.lp);
1023 if count == 1 {
1024 self.nodes.remove(at);
1025 } else {
1026 let node = &mut self.nodes[at];
1027 set_int(&mut node.lp, offset, flags | DELETED);
1028 bump(&mut node.lp, -1, 1);
1029 }
1030 self.epoch = self.epoch.wrapping_add(1);
1035 self.length -= 1;
1036 true
1037 }
1038
1039 fn edges(&self) -> Edges {
1045 Edges {
1046 added: self.added,
1047 length: self.length,
1048 first: self.first_id(),
1049 last: self.last,
1050 max_deleted: self.max_deleted,
1051 }
1052 }
1053
1054 #[must_use]
1070 pub fn lag(&self, group: &Group) -> Option<u64> {
1071 self.edges().lag(group)
1072 }
1073
1074 pub fn trim_maxlen(&mut self, len: u64, exact: bool, limit: Option<u64>) -> u64 {
1087 let mut gone = 0;
1088 while self.length > len && !limit.is_some_and(|cap| gone >= cap) {
1089 let Some(node) = self.nodes.front() else {
1090 break;
1091 };
1092 let (count, _) = counts(&node.lp);
1093 if self.length - count >= len {
1094 self.length -= count;
1095 gone += count;
1096 self.nodes.pop_front();
1097 continue;
1098 }
1099 if !exact {
1100 break;
1101 }
1102 let Some(id) = self.first_id() else { break };
1103 self.remove(id);
1104 gone += 1;
1105 }
1106 gone
1107 }
1108
1109 pub fn trim_minid(&mut self, id: Id, exact: bool, limit: Option<u64>) -> u64 {
1114 let mut gone = 0;
1115 while let Some(node) = self.nodes.front() {
1116 if limit.is_some_and(|cap| gone >= cap) {
1117 break;
1118 }
1119 let (count, _) = counts(&node.lp);
1120 if last_of(node) < id {
1121 self.length -= count;
1122 gone += count;
1123 self.nodes.pop_front();
1124 continue;
1125 }
1126 if !exact {
1127 break;
1128 }
1129 let Some(first) = self.first_id() else { break };
1130 if first >= id {
1131 break;
1132 }
1133 self.remove(first);
1134 gone += 1;
1135 }
1136 gone
1137 }
1138
1139 pub fn range<F>(&self, start: Id, end: Id, count: Option<usize>, mut f: F) -> usize
1146 where
1147 F: FnMut(Id, Fields<'_>) -> bool,
1148 {
1149 self.walk(start, end, count, &mut f)
1150 }
1151
1152 pub fn rev_range<'s, F>(&'s self, start: Id, end: Id, count: Option<usize>, mut f: F) -> usize
1158 where
1159 F: FnMut(Id, Fields<'_>) -> bool,
1160 {
1161 let mut seen = 0;
1162 let mut buf: Vec<(Id, Fields<'s>)> = Vec::new();
1167 let last = self.node_from(end);
1173 for node in self.nodes.iter().take(last + 1).rev() {
1174 if node.master > end {
1177 continue;
1178 }
1179 if last_of(node) < start {
1180 break;
1181 }
1182 buf.clear();
1183 each(&node.lp, node.master, None, &mut |id, _, fields| {
1184 if id >= start && id <= end {
1185 buf.push((id, fields));
1186 }
1187 id <= end
1188 });
1189 for (id, fields) in buf.drain(..).rev() {
1190 if count.is_some_and(|want| seen >= want) {
1191 return seen;
1192 }
1193 seen += 1;
1194 if !f(id, fields) {
1195 return seen;
1196 }
1197 }
1198 }
1199 seen
1200 }
1201
1202 #[must_use]
1207 pub fn contains(&self, id: Id) -> bool {
1208 let Some(at) = self.node_of(id) else {
1209 return false;
1210 };
1211 let node = &self.nodes[at];
1212 find(&node.lp, node.master, id).is_some_and(|(_, flags)| flags & DELETED == 0)
1213 }
1214
1215 pub fn create_group(&mut self, name: &[u8], last: Id, read: Option<u64>) -> bool {
1220 if self.group(name).is_some() {
1221 return false;
1222 }
1223 self.groups.push((name.to_vec(), Group::new(last, read)));
1224 true
1225 }
1226
1227 pub fn destroy_group(&mut self, name: &[u8]) -> bool {
1229 let Some(at) = self.groups.iter().position(|(n, _)| n == name) else {
1230 return false;
1231 };
1232 self.groups.remove(at);
1233 true
1234 }
1235
1236 #[must_use]
1238 pub fn group(&self, name: &[u8]) -> Option<&Group> {
1239 self.groups
1240 .iter()
1241 .find(|(n, _)| n.as_slice() == name)
1242 .map(|(_, g)| g)
1243 }
1244
1245 pub fn group_mut(&mut self, name: &[u8]) -> Option<&mut Group> {
1247 self.groups
1248 .iter_mut()
1249 .find(|(n, _)| n.as_slice() == name)
1250 .map(|(_, g)| g)
1251 }
1252
1253 pub fn groups(&self) -> impl Iterator<Item = (&[u8], &Group)> + '_ {
1255 self.groups.iter().map(|(n, g)| (n.as_slice(), g))
1256 }
1257
1258 pub fn read_group<F>(
1272 &mut self,
1273 group: &[u8],
1274 consumer: &[u8],
1275 count: Option<usize>,
1276 noack: bool,
1277 now: u64,
1278 mut f: F,
1279 ) -> Option<usize>
1280 where
1281 F: FnMut(Id, Fields<'_>) -> bool,
1282 {
1283 let edges = self.edges();
1286 let Stream {
1289 nodes,
1290 groups,
1291 epoch,
1292 ..
1293 } = self;
1294 let epoch = *epoch;
1295 let (_, g) = groups.iter_mut().find(|(n, _)| n.as_slice() == group)?;
1296 let slot = g.consumer_or_create(consumer, now);
1297 let Some(from) = g.last_id().next() else {
1298 g.touch(slot, now, false);
1301 return Some(0);
1302 };
1303 let resume = g.resume(epoch, from);
1308 let (seen, mark) = walk_nodes(nodes, from, Id::MAX, count, resume, &mut |id, fields| {
1309 let read = edges.on_deliver(g, id);
1312 if noack {
1313 g.skip(id);
1314 } else {
1315 g.deliver(slot, id, now);
1316 }
1317 g.set_read(read);
1318 f(id, fields)
1319 });
1320 let next = g.last_id().next();
1321 g.set_resume(match (mark, next) {
1322 (Some((master, byte)), Some(next)) => Some(Cursor {
1323 epoch,
1324 next,
1325 master,
1326 byte,
1327 }),
1328 _ => None,
1329 });
1330 g.touch(slot, now, seen > 0);
1331 Some(seen)
1332 }
1333
1334 pub fn read_group_pending<F>(
1349 &mut self,
1350 group: &[u8],
1351 consumer: &[u8],
1352 after: Id,
1353 count: Option<usize>,
1354 now: u64,
1355 mut f: F,
1356 ) -> Option<usize>
1357 where
1358 F: FnMut(Id, Option<Fields<'_>>) -> bool,
1359 {
1360 let g = self.group_mut(group)?;
1370 let slot = g.consumer_or_create(consumer, now);
1371 let ids: Vec<Id> = g
1372 .consumer(slot)
1373 .expect("the slot that was just made")
1374 .pending()
1375 .filter(|&id| id > after)
1376 .take(count.unwrap_or(usize::MAX))
1377 .collect();
1378 for &id in &ids {
1379 g.redeliver(id, now);
1380 }
1381 g.touch(slot, now, !ids.is_empty());
1382
1383 let mut seen = 0;
1384 for &id in &ids {
1385 seen += 1;
1386 let mut go = true;
1387 let mut found = false;
1388 self.walk(id, id, Some(1), &mut |got, fields| {
1389 found = true;
1390 go = f(got, Some(fields));
1391 false
1392 });
1393 if !found {
1394 go = f(id, None);
1395 }
1396 if !go {
1397 break;
1398 }
1399 }
1400 Some(seen)
1401 }
1402
1403 #[allow(clippy::too_many_arguments)]
1416 pub fn claim(
1417 &mut self,
1418 group: &[u8],
1419 consumer: &[u8],
1420 ids: &[Id],
1421 min_idle: u64,
1422 time: u64,
1423 retry: Option<u64>,
1424 bump: bool,
1425 force: bool,
1426 now: u64,
1427 gone: &mut Vec<Id>,
1428 ) -> Option<Vec<Id>> {
1429 self.group_mut(group)?.consumer_or_create(consumer, now);
1435 let mut took = Vec::new();
1436 for &id in ids {
1437 let here = self.contains(id);
1438 let g = self.group_mut(group)?;
1439 let slot = g.consumer_or_create(consumer, now);
1440 match g.nack(id) {
1441 Some(nack) => {
1442 if !here {
1443 g.forget(id);
1444 gone.push(id);
1445 continue;
1446 }
1447 if nack.idle(now) < min_idle {
1448 continue;
1449 }
1450 if g.claim(id, slot, time, retry, bump) {
1451 took.push(id);
1452 }
1453 }
1454 None => {
1455 if force && here && g.force(id, slot, time, retry.unwrap_or(1)) {
1458 took.push(id);
1459 }
1460 }
1461 }
1462 }
1463 if !took.is_empty() {
1467 let g = self.group_mut(group).expect("the group found a moment ago");
1468 let slot = g.consumer_or_create(consumer, now);
1469 g.touch(slot, now, true);
1470 }
1471 Some(took)
1472 }
1473
1474 #[allow(clippy::too_many_arguments)]
1482 pub fn autoclaim(
1483 &mut self,
1484 group: &[u8],
1485 consumer: &[u8],
1486 start: Id,
1487 min_idle: u64,
1488 count: usize,
1489 bump: bool,
1490 now: u64,
1491 gone: &mut Vec<Id>,
1492 ) -> Option<(Option<Id>, Vec<Id>)> {
1493 let mut ids = Vec::new();
1494 let cursor = self
1495 .group(group)?
1496 .claimable(start, min_idle, now, count, &mut ids);
1497 let took = self.claim(
1498 group, consumer, &ids, min_idle, now, None, bump, false, now, gone,
1499 )?;
1500 Some((cursor, took))
1501 }
1502
1503 #[must_use]
1509 pub fn memory_bytes(&self) -> usize {
1510 let nodes: usize = self
1511 .nodes
1512 .iter()
1513 .map(|node| node.lp.byte_len() + std::mem::size_of::<Node>())
1514 .sum();
1515 let groups: usize = self
1516 .groups
1517 .iter()
1518 .map(|(name, g)| {
1519 name.capacity() + std::mem::size_of::<(Vec<u8>, Group)>() + g.memory_bytes()
1520 })
1521 .sum();
1522 nodes + groups
1523 }
1524
1525 #[must_use]
1528 pub fn nodes(&self) -> usize {
1529 self.nodes.len()
1530 }
1531
1532 fn walk<F>(&self, start: Id, end: Id, count: Option<usize>, f: &mut F) -> usize
1534 where
1535 F: FnMut(Id, Fields<'_>) -> bool,
1536 {
1537 walk_nodes(&self.nodes, start, end, count, None, f).0
1538 }
1539
1540 fn node_from(&self, id: Id) -> usize {
1542 node_from(&self.nodes, id)
1543 }
1544
1545 fn node_of(&self, id: Id) -> Option<usize> {
1547 let at = self.node_from(id);
1548 let node = self.nodes.get(at)?;
1549 (node.master <= id && id <= last_of(node)).then_some(at)
1550 }
1551}
1552
1553fn node_from(nodes: &VecDeque<Node>, id: Id) -> usize {
1564 let after = nodes.partition_point(|node| node.master <= id);
1565 after.saturating_sub(1)
1566}
1567
1568fn walk_nodes<F>(
1576 nodes: &VecDeque<Node>,
1577 start: Id,
1578 end: Id,
1579 count: Option<usize>,
1580 resume: Option<(Id, usize)>,
1581 f: &mut F,
1582) -> (usize, Option<(Id, usize)>)
1583where
1584 F: FnMut(Id, Fields<'_>) -> bool,
1585{
1586 let mut seen = 0;
1587 let mut stop = false;
1588 let first = node_from(nodes, start);
1589 let mut from =
1593 resume.and_then(|(master, byte)| (nodes.get(first)?.master == master).then_some(byte));
1594 let mut mark = None;
1595 for node in nodes.iter().skip(first) {
1596 if node.master > end {
1597 break;
1598 }
1599 let at = each(&node.lp, node.master, from.take(), &mut |id, _, fields| {
1600 if id > end {
1601 stop = true;
1602 return false;
1603 }
1604 if id < start {
1605 return true;
1606 }
1607 if count.is_some_and(|want| seen >= want) {
1608 stop = true;
1609 return false;
1610 }
1611 seen += 1;
1612 if !f(id, fields) {
1613 stop = true;
1614 return false;
1615 }
1616 true
1617 });
1618 mark = Some((node.master, at));
1619 if stop {
1620 break;
1621 }
1622 }
1623 (seen, mark)
1624}
1625
1626#[derive(Debug, Clone)]
1633pub struct Fields<'a> {
1634 names: Option<listpack::Iter<'a>>,
1636 body: listpack::Iter<'a>,
1637 left: usize,
1638}
1639
1640impl<'a> Iterator for Fields<'a> {
1641 type Item = (Entry<'a>, Entry<'a>);
1642
1643 fn next(&mut self) -> Option<(Entry<'a>, Entry<'a>)> {
1644 if self.left == 0 {
1645 return None;
1646 }
1647 self.left -= 1;
1648 let name = match &mut self.names {
1649 Some(names) => names.next()?,
1650 None => self.body.next()?,
1651 };
1652 Some((name, self.body.next()?))
1653 }
1654
1655 fn size_hint(&self) -> (usize, Option<usize>) {
1656 (self.left, Some(self.left))
1657 }
1658}
1659
1660impl ExactSizeIterator for Fields<'_> {}
1661
1662impl Fields<'_> {
1663 #[must_use]
1665 #[inline]
1666 pub fn is_empty(&self) -> bool {
1667 self.left == 0
1668 }
1669}
1670
1671fn master_of(fields: &[(&[u8], &[u8])]) -> Listpack {
1673 let mut lp = Listpack::new();
1674 push_int(&mut lp, 0);
1675 push_int(&mut lp, 0);
1676 push_int(&mut lp, fields.len() as i64);
1677 for (name, _) in fields {
1678 lp.push(name);
1679 }
1680 push_int(&mut lp, 0);
1681 lp
1682}
1683
1684fn same_fields(lp: &Listpack, fields: &[(&[u8], &[u8])]) -> bool {
1686 let mut it = lp.iter();
1687 let (_, _, want) = match (it.next(), it.next(), it.next()) {
1688 (Some(_), Some(_), Some(Entry::Int(n))) => ((), (), n),
1689 _ => return false,
1690 };
1691 if want != fields.len() as i64 {
1692 return false;
1693 }
1694 fields.iter().all(|(name, _)| match it.next() {
1695 Some(Entry::Str(s)) => s == *name,
1696 Some(Entry::Int(n)) => {
1697 let mut buf = [0u8; DIGITS_MAX];
1698 i64_digits(&mut buf, n) == *name
1699 }
1700 None => false,
1701 })
1702}
1703
1704fn counts(lp: &Listpack) -> (u64, u64) {
1706 let mut it = lp.iter();
1707 let count = int_or_zero(it.next());
1708 let deleted = int_or_zero(it.next());
1709 (count.max(0) as u64, deleted.max(0) as u64)
1710}
1711
1712fn bump(lp: &mut Listpack, live: i64, dead: i64) -> bool {
1722 let was = lp.byte_len();
1723 let (count, deleted) = counts(lp);
1724 let mut buf = [0u8; DIGITS_MAX];
1725 let at = count as i64 + live;
1726 lp.replace(0, u64_digits(&mut buf, at.max(0) as u64));
1727 let at = deleted as i64 + dead;
1728 lp.replace(1, u64_digits(&mut buf, at.max(0) as u64));
1729 lp.byte_len() != was
1730}
1731
1732fn int_or_zero(entry: Option<Entry<'_>>) -> i64 {
1734 match entry {
1735 Some(Entry::Int(n)) => n,
1736 _ => 0,
1737 }
1738}
1739
1740fn push_int(lp: &mut Listpack, n: i64) {
1742 let mut buf = [0u8; DIGITS_MAX];
1743 lp.push(i64_digits(&mut buf, n));
1744}
1745
1746fn set_int(lp: &mut Listpack, index: usize, n: i64) {
1748 let mut buf = [0u8; DIGITS_MAX];
1749 lp.replace(index, i64_digits(&mut buf, n));
1750}
1751
1752fn write_entry(lp: &mut Listpack, master: Id, id: Id, fields: &[(&[u8], &[u8])], same: bool) {
1754 let flags = if same { LIVE | SAME_FIELDS } else { LIVE };
1755 push_int(lp, flags);
1756 push_int(lp, id.ms.wrapping_sub(master.ms) as i64);
1761 push_int(lp, id.seq.wrapping_sub(master.seq) as i64);
1762 if same {
1763 for (_, value) in fields {
1764 lp.push(value);
1765 }
1766 push_int(lp, fields.len() as i64 + 3);
1767 } else {
1768 push_int(lp, fields.len() as i64);
1769 for (name, value) in fields {
1770 lp.push(name);
1771 lp.push(value);
1772 }
1773 push_int(lp, fields.len() as i64 * 2 + 4);
1774 }
1775}
1776
1777fn last_of(node: &Node) -> Id {
1779 let mut last = node.master;
1780 each(&node.lp, node.master, None, &mut |id, _, _| {
1781 last = id;
1782 true
1783 });
1784 last
1785}
1786
1787fn find(lp: &Listpack, master: Id, id: Id) -> Option<(usize, i64)> {
1792 let mut at = None;
1793 walk_node(lp, master, None, &mut |mark, flags, _| {
1794 if mark.id == id {
1795 if let Some(index) = mark.index {
1799 at = Some((index, flags));
1800 }
1801 return false;
1802 }
1803 mark.id < id
1804 });
1805 at
1806}
1807
1808fn each<'a, F>(lp: &'a Listpack, master: Id, from: Option<usize>, f: &mut F) -> usize
1811where
1812 F: FnMut(Id, usize, Fields<'a>) -> bool,
1813{
1814 walk_node(lp, master, from, &mut |mark, flags, fields| {
1815 if flags & DELETED != 0 {
1816 return true;
1817 }
1818 f(mark.id, mark.byte, fields)
1819 })
1820}
1821
1822fn walk_node<'a, F>(lp: &'a Listpack, master: Id, from: Option<usize>, f: &mut F) -> usize
1828where
1829 F: FnMut(Mark, i64, Fields<'a>) -> bool,
1830{
1831 let mut it = lp.iter();
1832 let (Some(_), Some(_), Some(Entry::Int(masters))) = (it.next(), it.next(), it.next()) else {
1833 return it.offset();
1834 };
1835 let masters = masters.max(0) as usize;
1836 let names = it.clone();
1839 let mut index = MASTER_FIELDS;
1840 for _ in 0..=masters {
1841 if it.next().is_none() {
1842 return it.offset();
1843 }
1844 index += 1;
1845 }
1846 let counting = from.is_none();
1851 if let Some(byte) = from.filter(|byte| *byte > it.offset()) {
1852 it = lp.iter_at(byte);
1853 }
1854
1855 loop {
1856 let at = it.offset();
1857 let element = index;
1858 let (Some(Entry::Int(flags)), Some(Entry::Int(ms)), Some(Entry::Int(seq))) =
1859 (it.next(), it.next(), it.next())
1860 else {
1861 return at;
1862 };
1863 index += 3;
1864 let id = Id {
1865 ms: master.ms.wrapping_add(ms as u64),
1866 seq: master.seq.wrapping_add(seq as u64),
1867 };
1868
1869 let same = flags & SAME_FIELDS != 0;
1870 let (fields, skip) = if same {
1871 let fields = Fields {
1872 names: Some(names.clone()),
1873 body: it.clone(),
1874 left: masters,
1875 };
1876 (fields, masters + 1)
1877 } else {
1878 let Some(Entry::Int(n)) = it.next() else {
1879 return at;
1880 };
1881 index += 1;
1882 let fields = Fields {
1883 names: None,
1884 body: it.clone(),
1885 left: n.max(0) as usize,
1886 };
1887 (fields, n.max(0) as usize * 2 + 1)
1888 };
1889
1890 let mark = Mark {
1891 id,
1892 index: counting.then_some(element),
1893 byte: at,
1894 };
1895 if !f(mark, flags, fields) {
1896 return at;
1897 }
1898 for _ in 0..skip {
1899 if it.next().is_none() {
1900 return it.offset();
1901 }
1902 index += 1;
1903 }
1904 }
1905}
1906
1907#[derive(Debug, Clone, Copy)]
1909struct Mark {
1910 id: Id,
1913 index: Option<usize>,
1918 byte: usize,
1921}
1922
1923#[cfg(test)]
1924mod tests {
1925 use super::*;
1926
1927 fn pairs<'a>(of: &'a [(&'a str, &'a str)]) -> Vec<(&'a [u8], &'a [u8])> {
1928 of.iter()
1929 .map(|(f, v)| (f.as_bytes(), v.as_bytes()))
1930 .collect()
1931 }
1932
1933 type Flat = (Id, Vec<(Vec<u8>, Vec<u8>)>);
1935
1936 fn dump(s: &Stream) -> Vec<Flat> {
1938 let mut out = Vec::new();
1939 s.range(Id::MIN, Id::MAX, None, |id, fields| {
1940 out.push((id, fields.map(|(f, v)| (f.to_vec(), v.to_vec())).collect()));
1941 true
1942 });
1943 out
1944 }
1945
1946 fn add(s: &mut Stream, ms: u64, seq: u64, fields: &[(&str, &str)]) {
1947 s.append(Id::new(ms, seq), &pairs(fields), Limits::default())
1948 .expect("an append");
1949 }
1950
1951 #[test]
1952 fn an_entry_comes_back_as_it_went_in() {
1953 let mut s = Stream::new();
1954 add(&mut s, 5, 0, &[("sensor", "1"), ("reading", "23.4")]);
1955 let got = dump(&s);
1956 assert_eq!(got.len(), 1);
1957 assert_eq!(got[0].0, Id::new(5, 0));
1958 assert_eq!(
1959 got[0].1,
1960 vec![
1961 (b"sensor".to_vec(), b"1".to_vec()),
1962 (b"reading".to_vec(), b"23.4".to_vec())
1963 ]
1964 );
1965 }
1966
1967 #[test]
1968 fn entries_come_back_in_order() {
1969 let mut s = Stream::new();
1970 for ms in 1..200u64 {
1971 add(&mut s, ms, 0, &[("n", "x")]);
1972 }
1973 let got = dump(&s);
1974 assert_eq!(got.len(), 199);
1975 for (at, (id, _)) in got.iter().enumerate() {
1976 assert_eq!(*id, Id::new(at as u64 + 1, 0));
1977 }
1978 assert_eq!(s.len(), 199);
1979 assert_eq!(s.added(), 199);
1980 assert_eq!(s.last_id(), Id::new(199, 0));
1981 }
1982
1983 #[test]
1985 fn a_long_stream_is_many_nodes() {
1986 let mut s = Stream::new();
1987 for ms in 1..=1000u64 {
1988 add(&mut s, ms, 0, &[("n", "x")]);
1989 }
1990 assert_eq!(s.nodes(), 10, "a hundred entries a node");
1991 assert_eq!(dump(&s).len(), 1000);
1992 }
1993
1994 #[test]
1996 fn the_field_names_are_not_stored_twice() {
1997 let mut shared = Stream::new();
1998 let mut apart = Stream::new();
1999 for ms in 1..=100u64 {
2000 add(
2001 &mut shared,
2002 ms,
2003 0,
2004 &[("temperature_celsius", "21"), ("relative_humidity", "44")],
2005 );
2006 let a = format!("temperature_celsius{ms}");
2007 let b = format!("relative_humidity{ms}");
2008 apart
2009 .append(
2010 Id::new(ms, 0),
2011 &[(a.as_bytes(), b"21"), (b.as_bytes(), b"44")],
2012 Limits::default(),
2013 )
2014 .expect("an append");
2015 }
2016 assert!(
2017 shared.memory_bytes() * 3 < apart.memory_bytes(),
2018 "{} against {}",
2019 shared.memory_bytes(),
2020 apart.memory_bytes()
2021 );
2022 }
2023
2024 #[test]
2034 fn an_entry_costs_about_two_dozen_bytes() {
2035 let mut s = Stream::new();
2036 for ms in 1..=10_000u64 {
2037 let reading = format!("{:.3}", ms as f64 / 7.0);
2038 s.append(
2039 Id::new(ms, 0),
2040 &[(b"sensor", b"a4"), (b"reading", reading.as_bytes())],
2041 Limits::default(),
2042 )
2043 .expect("an append");
2044 }
2045 let each = s.memory_bytes() as f64 / 10_000.0;
2046 assert!(each < 32.0, "{each:.2} bytes an entry");
2047 }
2048
2049 #[test]
2050 fn an_entry_with_its_own_fields_still_reads_back() {
2051 let mut s = Stream::new();
2052 add(&mut s, 1, 0, &[("a", "1"), ("b", "2")]);
2053 add(&mut s, 2, 0, &[("c", "3")]);
2054 add(&mut s, 3, 0, &[("a", "4"), ("b", "5")]);
2055 let got = dump(&s);
2056 assert_eq!(got[1].1, vec![(b"c".to_vec(), b"3".to_vec())]);
2057 assert_eq!(
2058 got[2].1,
2059 vec![
2060 (b"a".to_vec(), b"4".to_vec()),
2061 (b"b".to_vec(), b"5".to_vec())
2062 ]
2063 );
2064 }
2065
2066 #[test]
2068 fn the_order_of_the_names_matters() {
2069 let mut s = Stream::new();
2070 add(&mut s, 1, 0, &[("a", "1"), ("b", "2")]);
2071 add(&mut s, 2, 0, &[("b", "3"), ("a", "4")]);
2072 let got = dump(&s);
2073 assert_eq!(
2074 got[1].1,
2075 vec![
2076 (b"b".to_vec(), b"3".to_vec()),
2077 (b"a".to_vec(), b"4".to_vec())
2078 ]
2079 );
2080 }
2081
2082 #[test]
2083 fn an_id_must_beat_the_last_one() {
2084 let mut s = Stream::new();
2085 add(&mut s, 5, 5, &[("n", "x")]);
2086 let f = pairs(&[("n", "x")]);
2087 for id in [Id::new(5, 5), Id::new(5, 4), Id::new(1, 0)] {
2088 assert_eq!(
2089 s.append(id, &f, Limits::default()),
2090 Err(Refused::NotGreater),
2091 "{id:?}"
2092 );
2093 }
2094 assert_eq!(s.append(Id::new(5, 6), &f, Limits::default()), Ok(()));
2095 }
2096
2097 #[test]
2098 fn nothing_can_be_added_at_zero() {
2099 let mut s = Stream::new();
2100 assert_eq!(
2101 s.append(Id::MIN, &pairs(&[("n", "x")]), Limits::default()),
2102 Err(Refused::Zero)
2103 );
2104 }
2105
2106 #[test]
2107 fn a_range_takes_both_ends() {
2108 let mut s = Stream::new();
2109 for ms in 1..=10u64 {
2110 add(&mut s, ms, 0, &[("n", "x")]);
2111 }
2112 let mut seen = Vec::new();
2113 s.range(Id::new(3, 0), Id::new(6, 0), None, |id, _| {
2114 seen.push(id.ms);
2115 true
2116 });
2117 assert_eq!(seen, vec![3, 4, 5, 6]);
2118 }
2119
2120 #[test]
2122 fn a_range_that_lands_between_entries() {
2123 let mut s = Stream::new();
2124 for ms in [10u64, 20, 30] {
2125 add(&mut s, ms, 0, &[("n", "x")]);
2126 }
2127 let mut seen = Vec::new();
2128 s.range(Id::new(11, 0), Id::new(29, 0), None, |id, _| {
2129 seen.push(id.ms);
2130 true
2131 });
2132 assert_eq!(seen, vec![20]);
2133
2134 let mut none = 0;
2135 s.range(Id::new(31, 0), Id::MAX, None, |_, _| {
2136 none += 1;
2137 true
2138 });
2139 assert_eq!(none, 0);
2140 }
2141
2142 #[test]
2143 fn a_count_stops_the_walk() {
2144 let mut s = Stream::new();
2145 for ms in 1..=500u64 {
2146 add(&mut s, ms, 0, &[("n", "x")]);
2147 }
2148 let mut seen = 0;
2149 let answered = s.range(Id::MIN, Id::MAX, Some(7), |_, _| {
2150 seen += 1;
2151 true
2152 });
2153 assert_eq!((seen, answered), (7, 7));
2154 }
2155
2156 #[test]
2157 fn the_callback_can_stop_the_walk() {
2158 let mut s = Stream::new();
2159 for ms in 1..=500u64 {
2160 add(&mut s, ms, 0, &[("n", "x")]);
2161 }
2162 let mut seen = 0;
2163 s.range(Id::MIN, Id::MAX, None, |_, _| {
2164 seen += 1;
2165 seen < 3
2166 });
2167 assert_eq!(seen, 3);
2168 }
2169
2170 #[test]
2171 fn a_reverse_range_is_the_forward_one_backwards() {
2172 let mut s = Stream::new();
2173 for ms in 1..=350u64 {
2174 add(&mut s, ms, 0, &[("n", "x")]);
2175 }
2176 let mut forward = Vec::new();
2177 s.range(Id::new(50, 0), Id::new(300, 0), None, |id, _| {
2178 forward.push(id);
2179 true
2180 });
2181 let mut back = Vec::new();
2182 s.rev_range(Id::new(50, 0), Id::new(300, 0), None, |id, _| {
2183 back.push(id);
2184 true
2185 });
2186 back.reverse();
2187 assert_eq!(forward, back);
2188 assert_eq!(forward.len(), 251);
2189 }
2190
2191 #[test]
2192 fn a_reverse_range_takes_a_count_from_the_new_end() {
2193 let mut s = Stream::new();
2194 for ms in 1..=350u64 {
2195 add(&mut s, ms, 0, &[("n", "x")]);
2196 }
2197 let mut seen = Vec::new();
2198 s.rev_range(Id::MIN, Id::MAX, Some(3), |id, _| {
2199 seen.push(id.ms);
2200 true
2201 });
2202 assert_eq!(seen, vec![350, 349, 348]);
2203 }
2204
2205 #[test]
2206 fn deleting_leaves_the_rest_readable() {
2207 let mut s = Stream::new();
2208 for ms in 1..=10u64 {
2209 add(&mut s, ms, 0, &[("n", "x")]);
2210 }
2211 assert!(s.delete(Id::new(4, 0)));
2212 assert!(!s.delete(Id::new(4, 0)), "twice is not twice");
2213 assert!(!s.delete(Id::new(99, 0)));
2214 assert_eq!(s.len(), 9);
2215 assert_eq!(s.max_deleted_id(), Id::new(4, 0));
2216 let seen: Vec<u64> = dump(&s).iter().map(|(id, _)| id.ms).collect();
2217 assert_eq!(seen, vec![1, 2, 3, 5, 6, 7, 8, 9, 10]);
2218 }
2219
2220 #[test]
2221 fn deleting_the_first_entry_moves_the_first_id() {
2222 let mut s = Stream::new();
2223 for ms in 1..=5u64 {
2224 add(&mut s, ms, 0, &[("n", "x")]);
2225 }
2226 assert_eq!(s.first_id(), Some(Id::new(1, 0)));
2227 s.delete(Id::new(1, 0));
2228 assert_eq!(s.first_id(), Some(Id::new(2, 0)));
2229 }
2230
2231 #[test]
2233 fn emptying_a_node_drops_it() {
2234 let mut s = Stream::new();
2235 for ms in 1..=250u64 {
2236 add(&mut s, ms, 0, &[("n", "x")]);
2237 }
2238 assert_eq!(s.nodes(), 3);
2239 for ms in 1..=100u64 {
2240 assert!(s.delete(Id::new(ms, 0)), "{ms}");
2241 }
2242 assert_eq!(s.nodes(), 2);
2243 assert_eq!(s.len(), 150);
2244 assert_eq!(dump(&s).len(), 150);
2245 }
2246
2247 #[test]
2249 fn an_emptied_stream_still_remembers_its_last_id() {
2250 let mut s = Stream::new();
2251 add(&mut s, 7, 0, &[("n", "x")]);
2252 s.delete(Id::new(7, 0));
2253 assert!(s.is_empty());
2254 assert_eq!(s.last_id(), Id::new(7, 0));
2255 assert_eq!(s.added(), 1);
2256 assert_eq!(s.first_id(), None);
2257 assert_eq!(
2258 s.append(Id::new(7, 0), &pairs(&[("n", "x")]), Limits::default()),
2259 Err(Refused::NotGreater),
2260 "a deleted id is still used up"
2261 );
2262 }
2263
2264 #[test]
2265 fn trimming_to_a_length_takes_the_oldest() {
2266 let mut s = Stream::new();
2267 for ms in 1..=1000u64 {
2268 add(&mut s, ms, 0, &[("n", "x")]);
2269 }
2270 assert_eq!(s.trim_maxlen(150, true, None), 850);
2271 assert_eq!(s.len(), 150);
2272 assert_eq!(s.first_id(), Some(Id::new(851, 0)));
2273 assert_eq!(s.last_id(), Id::new(1000, 0));
2274 }
2275
2276 #[test]
2278 fn an_approximate_trim_stops_at_a_node() {
2279 let mut s = Stream::new();
2280 for ms in 1..=1000u64 {
2281 add(&mut s, ms, 0, &[("n", "x")]);
2282 }
2283 assert_eq!(s.trim_maxlen(150, false, None), 800);
2284 assert_eq!(s.len(), 200, "left at the node boundary above 150");
2285 assert_eq!(s.nodes(), 2);
2286 }
2287
2288 #[test]
2289 fn trimming_to_a_length_that_is_already_met_does_nothing() {
2290 let mut s = Stream::new();
2291 for ms in 1..=10u64 {
2292 add(&mut s, ms, 0, &[("n", "x")]);
2293 }
2294 assert_eq!(s.trim_maxlen(50, true, None), 0);
2295 assert_eq!(s.len(), 10);
2296 }
2297
2298 #[test]
2299 fn trimming_to_zero_empties_it() {
2300 let mut s = Stream::new();
2301 for ms in 1..=250u64 {
2302 add(&mut s, ms, 0, &[("n", "x")]);
2303 }
2304 assert_eq!(s.trim_maxlen(0, true, None), 250);
2305 assert!(s.is_empty());
2306 assert_eq!(s.nodes(), 0);
2307 assert_eq!(s.last_id(), Id::new(250, 0));
2308 }
2309
2310 #[test]
2311 fn trimming_below_an_id_takes_everything_under_it() {
2312 let mut s = Stream::new();
2313 for ms in 1..=1000u64 {
2314 add(&mut s, ms, 0, &[("n", "x")]);
2315 }
2316 assert_eq!(s.trim_minid(Id::new(400, 0), true, None), 399);
2317 assert_eq!(s.first_id(), Some(Id::new(400, 0)));
2318 assert_eq!(s.len(), 601);
2319 }
2320
2321 #[test]
2322 fn an_approximate_minid_trim_stops_at_a_node() {
2323 let mut s = Stream::new();
2324 for ms in 1..=1000u64 {
2325 add(&mut s, ms, 0, &[("n", "x")]);
2326 }
2327 assert_eq!(s.trim_minid(Id::new(450, 0), false, None), 400);
2328 assert_eq!(s.first_id(), Some(Id::new(401, 0)));
2329 }
2330
2331 #[test]
2332 fn several_entries_share_a_millisecond() {
2333 let mut s = Stream::new();
2334 for seq in 0..250u64 {
2335 add(&mut s, 5, seq, &[("n", "x")]);
2336 }
2337 let got = dump(&s);
2338 assert_eq!(got.len(), 250);
2339 for (at, (id, _)) in got.iter().enumerate() {
2340 assert_eq!(*id, Id::new(5, at as u64), "at {at}");
2341 }
2342 let mut seen = Vec::new();
2343 s.range(Id::new(5, 100), Id::new(5, 102), None, |id, _| {
2344 seen.push(id.seq);
2345 true
2346 });
2347 assert_eq!(seen, vec![100, 101, 102]);
2348 }
2349
2350 #[test]
2353 fn a_sequence_that_crosses_a_node() {
2354 let mut s = Stream::new();
2355 for seq in 0..300u64 {
2356 add(&mut s, 1, seq, &[("n", "x")]);
2357 }
2358 assert!(s.nodes() > 1);
2359 let got = dump(&s);
2360 assert_eq!(got.len(), 300);
2361 assert_eq!(got[299].0, Id::new(1, 299));
2362 assert!(s.delete(Id::new(1, 250)));
2363 assert_eq!(dump(&s).len(), 299);
2364 }
2365
2366 #[test]
2367 fn an_entry_with_no_fields_is_still_an_entry() {
2368 let mut s = Stream::new();
2369 s.append(Id::new(1, 0), &[], Limits::default())
2370 .expect("an append");
2371 add(&mut s, 2, 0, &[("n", "x")]);
2372 let got = dump(&s);
2373 assert_eq!(got.len(), 2);
2374 assert!(got[0].1.is_empty());
2375 }
2376
2377 #[test]
2378 fn a_value_that_looks_like_a_number_comes_back_as_it_went_in() {
2379 let mut s = Stream::new();
2380 add(&mut s, 1, 0, &[("n", "007"), ("m", "7")]);
2381 let got = dump(&s);
2382 assert_eq!(got[0].1[0].1, b"007".to_vec());
2383 assert_eq!(got[0].1[1].1, b"7".to_vec());
2384 }
2385
2386 #[test]
2387 fn the_auto_id_follows_the_clock_and_never_goes_back() {
2388 let mut s = Stream::new();
2389 assert_eq!(s.auto_id(1000), Some(Id::new(1000, 0)));
2390 add(&mut s, 1000, 0, &[("n", "x")]);
2391 assert_eq!(s.auto_id(1000), Some(Id::new(1000, 1)), "same millisecond");
2392 assert_eq!(s.auto_id(900), Some(Id::new(1000, 1)), "clock went back");
2393 assert_eq!(s.auto_id(1001), Some(Id::new(1001, 0)));
2394 }
2395
2396 #[test]
2397 fn an_explicit_millisecond_takes_the_next_sequence() {
2398 let mut s = Stream::new();
2399 add(&mut s, 5, 0, &[("n", "x")]);
2400 assert_eq!(s.auto_seq(5), Some(Id::new(5, 1)));
2401 assert_eq!(s.auto_seq(6), Some(Id::new(6, 0)));
2402 assert_eq!(s.auto_seq(4), None, "below the last one");
2403 }
2404
2405 #[test]
2406 fn an_id_reads_and_writes() {
2407 for (text, default, want) in [
2408 (&b"5"[..], 0, Some(Id::new(5, 0))),
2409 (b"5", u64::MAX, Some(Id::new(5, u64::MAX))),
2410 (b"5-3", 0, Some(Id::new(5, 3))),
2411 (b"0-0", 0, Some(Id::MIN)),
2412 (b"", 0, None),
2413 (b"-1", 0, None),
2414 (b"5-", 0, None),
2415 (b"a", 0, None),
2416 (b"5-a", 0, None),
2417 (b"18446744073709551616", 0, None),
2418 ] {
2419 assert_eq!(
2420 Id::parse(text, default),
2421 want,
2422 "{:?}",
2423 String::from_utf8_lossy(text)
2424 );
2425 }
2426 assert_eq!(Id::new(5, 3).to_vec(), b"5-3".to_vec());
2427 }
2428
2429 #[test]
2430 fn an_id_round_trips_through_its_bytes() {
2431 for id in [Id::MIN, Id::MAX, Id::new(1, 2), Id::new(u64::MAX, 0)] {
2432 assert_eq!(Id::from_bytes(id.to_bytes()), id);
2433 }
2434 assert!(Id::new(1, 2).to_bytes() < Id::new(1, 3).to_bytes());
2436 assert!(Id::new(1, u64::MAX).to_bytes() < Id::new(2, 0).to_bytes());
2437 }
2438
2439 #[test]
2440 fn stepping_an_id_carries_and_stops() {
2441 assert_eq!(Id::new(1, 2).next(), Some(Id::new(1, 3)));
2442 assert_eq!(Id::new(1, u64::MAX).next(), Some(Id::new(2, 0)));
2443 assert_eq!(Id::MAX.next(), None);
2444 assert_eq!(Id::new(1, 3).prev(), Some(Id::new(1, 2)));
2445 assert_eq!(Id::new(2, 0).prev(), Some(Id::new(1, u64::MAX)));
2446 assert_eq!(Id::MIN.prev(), None);
2447 }
2448
2449 #[test]
2451 fn the_node_size_changes_nothing_but_the_node_count() {
2452 let mut want = None;
2453 for entries in [1usize, 2, 7, 100, 4096] {
2454 let mut s = Stream::new();
2455 let limits = Limits {
2456 max_node_bytes: NODE_BYTES,
2457 max_node_entries: entries,
2458 };
2459 for ms in 1..=400u64 {
2460 let value = format!("v{ms}");
2461 s.append(Id::new(ms, 0), &[(b"n", value.as_bytes())], limits)
2462 .expect("an append");
2463 }
2464 for ms in (1..=400u64).step_by(7) {
2465 s.delete(Id::new(ms, 0));
2466 }
2467 let got = dump(&s);
2468 match &want {
2469 None => want = Some(got),
2470 Some(want) => assert_eq!(&got, want, "at {entries} entries a node"),
2471 }
2472 }
2473 }
2474
2475 #[test]
2477 fn a_tiny_byte_limit_still_works() {
2478 let limits = Limits {
2479 max_node_bytes: 1,
2480 max_node_entries: NODE_ENTRIES,
2481 };
2482 let mut s = Stream::new();
2483 for ms in 1..=20u64 {
2484 s.append(Id::new(ms, 0), &[(b"n", b"x")], limits)
2485 .expect("an append");
2486 }
2487 assert_eq!(s.nodes(), 20);
2488 assert_eq!(dump(&s).len(), 20);
2489 }
2490
2491 const REDIS_DUMP: &str = "1b0110000000000000000100000000000000014070\
2508 700000001f000301010102019374656d70657261747572655f63656c736975731491\
2509 72656c61746976655f68756d69646974791200010201000100011501370105010301\
2510 0001010116013801050102010101000117013901050100010201000101018673656e\
2511 736f72078161020601ff030301010101020400406440640000000f00239a5c2c7208\
2512 ea0a";
2513
2514 fn unhex(s: &str) -> Vec<u8> {
2515 let digits: Vec<u8> = s.bytes().filter(|b| !b.is_ascii_whitespace()).collect();
2516 digits
2517 .chunks(2)
2518 .map(|pair| {
2519 let of = |b: u8| (b as char).to_digit(16).expect("a hex digit") as u8;
2520 of(pair[0]) << 4 | of(pair[1])
2521 })
2522 .collect()
2523 }
2524
2525 fn rdb_len(bytes: &[u8], at: usize) -> (usize, usize) {
2531 match bytes[at] >> 6 {
2532 0 => (usize::from(bytes[at] & 0x3F), 1),
2533 1 => (
2534 usize::from(bytes[at] & 0x3F) << 8 | usize::from(bytes[at + 1]),
2535 2,
2536 ),
2537 other => panic!("the fixture used length form {other}"),
2538 }
2539 }
2540
2541 fn redis_node() -> (Id, Vec<u8>, Vec<u8>) {
2543 let dump = unhex(REDIS_DUMP);
2544 assert_eq!(dump[0], 0x1B, "RDB_TYPE_STREAM_LISTPACKS_3");
2545 let (nodes, n) = rdb_len(&dump, 1);
2546 assert_eq!(nodes, 1, "the fixture is one node");
2547 let mut at = 1 + n;
2548 let (key, n) = rdb_len(&dump, at);
2549 assert_eq!(key, 16, "a node key is an id in sixteen bytes");
2550 at += n;
2551 let master = Id::from_bytes(dump[at..at + 16].try_into().expect("sixteen bytes"));
2552 at += 16;
2553 let (len, n) = rdb_len(&dump, at);
2554 at += n;
2555 let lp = dump[at..at + len].to_vec();
2556 let rest = dump[at + len..dump.len() - 10].to_vec();
2559 (master, lp, rest)
2560 }
2561
2562 #[test]
2563 fn a_node_is_written_the_way_redis_writes_one() {
2564 let mut s = Stream::new();
2565 add(
2566 &mut s,
2567 1,
2568 1,
2569 &[("temperature_celsius", "21"), ("relative_humidity", "55")],
2570 );
2571 add(
2572 &mut s,
2573 1,
2574 2,
2575 &[("temperature_celsius", "22"), ("relative_humidity", "56")],
2576 );
2577 add(
2578 &mut s,
2579 2,
2580 1,
2581 &[("temperature_celsius", "23"), ("relative_humidity", "57")],
2582 );
2583 add(&mut s, 3, 1, &[("sensor", "a")]);
2584 assert!(s.delete(Id::new(1, 2)));
2585
2586 let (master, lp, _) = redis_node();
2587 assert_eq!(s.nodes(), 1, "all four fit in one node");
2588 assert_eq!(s.nodes[0].master, master);
2589 assert_eq!(s.nodes[0].lp.as_bytes(), &lp[..]);
2590 }
2591
2592 #[test]
2593 fn a_node_redis_wrote_reads_back() {
2594 let (master, lp, rest) = redis_node();
2595 let lp = Listpack::from_bytes(&lp).expect("a listpack Redis wrote");
2596 let s = Stream {
2597 nodes: VecDeque::from(vec![Node { master, lp }]),
2598 length: u64::from(rest[0]),
2599 last: Id::new(u64::from(rest[1]), u64::from(rest[2])),
2600 max_deleted: Id::new(u64::from(rest[5]), u64::from(rest[6])),
2601 added: u64::from(rest[7]),
2602 groups: Vec::new(),
2603 epoch: 0,
2604 };
2605
2606 assert_eq!(s.len(), 3);
2607 assert_eq!(s.last_id(), Id::new(3, 1));
2608 assert_eq!(s.max_deleted_id(), Id::new(1, 2));
2609 assert_eq!(s.added(), 4);
2610 assert_eq!(s.first_id(), Some(Id::new(1, 1)));
2611 assert_eq!(
2612 dump(&s),
2613 vec![
2614 (
2615 Id::new(1, 1),
2616 vec![
2617 (b"temperature_celsius".to_vec(), b"21".to_vec()),
2618 (b"relative_humidity".to_vec(), b"55".to_vec())
2619 ]
2620 ),
2621 (
2622 Id::new(2, 1),
2623 vec![
2624 (b"temperature_celsius".to_vec(), b"23".to_vec()),
2625 (b"relative_humidity".to_vec(), b"57".to_vec())
2626 ]
2627 ),
2628 (Id::new(3, 1), vec![(b"sensor".to_vec(), b"a".to_vec())]),
2629 ]
2630 );
2631 }
2632
2633 fn logged(n: u64) -> Stream {
2635 let mut s = Stream::new();
2636 for ms in 1..=n {
2637 add(&mut s, ms, 0, &[("job", "x")]);
2638 }
2639 s
2640 }
2641
2642 fn read(s: &mut Stream, group: &str, who: &str, count: Option<usize>, now: u64) -> Vec<Id> {
2644 let mut out = Vec::new();
2645 s.read_group(
2646 group.as_bytes(),
2647 who.as_bytes(),
2648 count,
2649 false,
2650 now,
2651 |id, _| {
2652 out.push(id);
2653 true
2654 },
2655 )
2656 .expect("the group");
2657 out
2658 }
2659
2660 #[test]
2665 fn a_group_draining_one_at_a_time_gets_every_entry_once() {
2666 let mut s = logged(500);
2667 s.create_group(b"workers", Id::MIN, Some(0));
2668 let mut got = Vec::new();
2669 for _ in 0..500 {
2670 got.extend(read(&mut s, "workers", "alice", Some(1), 100));
2671 }
2672 assert_eq!(got, (1..=500).map(|ms| Id::new(ms, 0)).collect::<Vec<_>>());
2673 assert!(read(&mut s, "workers", "alice", Some(1), 100).is_empty());
2674 }
2675
2676 #[test]
2679 fn a_group_that_has_caught_up_is_handed_the_next_append() {
2680 let mut s = logged(3);
2681 s.create_group(b"workers", Id::MIN, Some(0));
2682 assert_eq!(read(&mut s, "workers", "alice", None, 100).len(), 3);
2683 for ms in 4..=200u64 {
2684 add(&mut s, ms, 0, &[("job", "x")]);
2685 assert_eq!(
2686 read(&mut s, "workers", "alice", Some(1), 100),
2687 vec![Id::new(ms, 0)],
2688 "the entry appended a moment ago"
2689 );
2690 }
2691 }
2692
2693 #[test]
2696 fn a_delete_between_two_group_reads_does_not_lose_the_rest() {
2697 let mut s = logged(300);
2698 s.create_group(b"workers", Id::MIN, Some(0));
2699 let mut got = read(&mut s, "workers", "alice", Some(10), 100);
2700 assert!(s.delete(Id::new(150, 0)));
2701 while got.len() < 299 {
2702 let more = read(&mut s, "workers", "alice", Some(1), 100);
2703 assert_eq!(more.len(), 1, "at {}", got.len());
2704 got.extend(more);
2705 }
2706 let want: Vec<Id> = (1..=300)
2707 .filter(|ms| *ms != 150)
2708 .map(|ms| Id::new(ms, 0))
2709 .collect();
2710 assert_eq!(got, want);
2711 }
2712
2713 #[test]
2716 fn moving_the_bookmark_back_reads_it_all_again() {
2717 let mut s = logged(250);
2718 s.create_group(b"workers", Id::MIN, Some(0));
2719 for _ in 0..120 {
2720 read(&mut s, "workers", "alice", Some(1), 100);
2721 }
2722 s.group_mut(b"workers")
2723 .expect("the group")
2724 .set_id(Id::MIN, Some(0));
2725 let mut got = Vec::new();
2726 for _ in 0..250 {
2727 got.extend(read(&mut s, "workers", "alice", Some(1), 100));
2728 }
2729 assert_eq!(got, (1..=250).map(|ms| Id::new(ms, 0)).collect::<Vec<_>>());
2730 }
2731
2732 #[test]
2736 fn a_trim_under_a_group_does_not_hand_out_the_wrong_entries() {
2737 let mut s = logged(500);
2738 s.create_group(b"workers", Id::MIN, Some(0));
2739 for _ in 0..50 {
2740 read(&mut s, "workers", "alice", Some(1), 100);
2741 }
2742 assert_eq!(s.trim_maxlen(200, false, None), 300);
2743 let mut got = Vec::new();
2744 for _ in 0..250 {
2745 got.extend(read(&mut s, "workers", "alice", Some(1), 100));
2746 }
2747 assert_eq!(
2748 got,
2749 (301..=500).map(|ms| Id::new(ms, 0)).collect::<Vec<_>>(),
2750 "everything the trim left that the group had not read"
2751 );
2752 }
2753
2754 #[test]
2755 fn a_group_is_made_once() {
2756 let mut s = logged(3);
2757 assert!(s.create_group(b"workers", Id::MIN, Some(0)));
2758 assert!(!s.create_group(b"workers", Id::MIN, Some(0)));
2759 assert!(s.group(b"workers").is_some());
2760 assert!(s.destroy_group(b"workers"));
2761 assert!(!s.destroy_group(b"workers"));
2762 assert!(s.group(b"workers").is_none());
2763 }
2764
2765 #[test]
2766 fn a_group_read_hands_out_what_comes_after_the_bookmark() {
2767 let mut s = logged(5);
2768 s.create_group(b"workers", Id::MIN, Some(0));
2769
2770 assert_eq!(
2771 read(&mut s, "workers", "alice", Some(2), 100),
2772 vec![Id::new(1, 0), Id::new(2, 0)]
2773 );
2774 assert_eq!(
2776 read(&mut s, "workers", "bob", Some(2), 100),
2777 vec![Id::new(3, 0), Id::new(4, 0)]
2778 );
2779 assert_eq!(
2780 read(&mut s, "workers", "alice", None, 100),
2781 vec![Id::new(5, 0)]
2782 );
2783 assert_eq!(read(&mut s, "workers", "alice", None, 100), vec![]);
2784 }
2785
2786 #[test]
2787 fn a_group_read_fills_the_pending_list() {
2788 let mut s = logged(3);
2789 s.create_group(b"workers", Id::MIN, Some(0));
2790 read(&mut s, "workers", "alice", None, 500);
2791
2792 let g = s.group(b"workers").expect("the group");
2793 assert_eq!(g.pending_len(), 3);
2794 assert_eq!(g.last_id(), Id::new(3, 0));
2795 assert_eq!(g.entries_read(), Some(3));
2796 assert_eq!(s.lag(g), Some(0));
2797 let c = g.consumer_named(b"alice").expect("alice");
2798 assert_eq!(c.len(), 3);
2799 assert_eq!(c.active(), Some(500));
2800 }
2801
2802 #[test]
2803 fn a_read_that_finds_nothing_is_seen_but_not_active() {
2804 let mut s = logged(1);
2805 s.create_group(b"workers", Id::MIN, Some(0));
2806 read(&mut s, "workers", "alice", None, 100);
2807 read(&mut s, "workers", "alice", None, 900);
2808
2809 let c = s
2810 .group(b"workers")
2811 .expect("the group")
2812 .consumer_named(b"alice")
2813 .expect("alice");
2814 assert_eq!((c.seen(), c.active()), (900, Some(100)));
2815 }
2816
2817 #[test]
2818 fn a_group_starting_at_the_end_reads_only_what_comes_next() {
2819 let mut s = logged(3);
2820 s.create_group(b"workers", s.last_id(), Some(s.added()));
2821 assert_eq!(read(&mut s, "workers", "alice", None, 1), vec![]);
2822 add(&mut s, 4, 0, &[("job", "x")]);
2823 assert_eq!(
2824 read(&mut s, "workers", "alice", None, 1),
2825 vec![Id::new(4, 0)]
2826 );
2827 }
2828
2829 #[test]
2830 fn reading_a_group_that_is_not_there_says_so() {
2831 let mut s = logged(1);
2832 assert!(
2833 s.read_group(b"nope", b"alice", None, false, 1, |_, _| true)
2834 .is_none()
2835 );
2836 }
2837
2838 #[test]
2839 fn a_consumer_can_re_read_what_it_is_holding() {
2840 let mut s = logged(4);
2841 s.create_group(b"workers", Id::MIN, Some(0));
2842 read(&mut s, "workers", "alice", Some(2), 1);
2843 read(&mut s, "workers", "bob", Some(2), 1);
2844
2845 let mut out = Vec::new();
2846 s.read_group_pending(b"workers", b"alice", Id::MIN, None, 2, |id, fields| {
2847 out.push((id, fields.map(|f| f.len())));
2848 true
2849 })
2850 .expect("the group");
2851 assert_eq!(
2852 out,
2853 vec![(Id::new(1, 0), Some(1)), (Id::new(2, 0), Some(1))]
2854 );
2855
2856 let mut after = Vec::new();
2858 s.read_group_pending(b"workers", b"alice", Id::new(1, 0), None, 2, |id, _| {
2859 after.push(id);
2860 true
2861 });
2862 assert_eq!(after, vec![Id::new(2, 0)]);
2863 }
2864
2865 #[test]
2874 fn re_reading_counts_as_being_handed_it_again() {
2875 let mut s = logged(2);
2876 s.create_group(b"workers", Id::MIN, Some(0));
2877 read(&mut s, "workers", "alice", None, 100);
2878 s.read_group_pending(
2879 b"workers",
2880 b"alice",
2881 Id::MIN,
2882 None,
2883 700,
2884 |_: Id, _: Option<Fields<'_>>| true,
2885 );
2886
2887 let g = s.group(b"workers").expect("the group");
2888 let nack = g.nack(Id::new(1, 0)).expect("a nack");
2889 assert_eq!((nack.count(), nack.time()), (2, 700));
2890 assert_eq!(g.last_id(), Id::new(2, 0));
2892 assert_eq!(g.pending_len(), 2);
2893 }
2894
2895 #[test]
2902 fn a_hole_in_front_of_a_group_takes_its_lag() {
2903 let mut s = logged(5);
2904 s.create_group(b"workers", Id::MIN, Some(0));
2905 read(&mut s, "workers", "alice", Some(3), 1);
2906 assert_eq!(counters(&s), (Some(3), Some(2)));
2907
2908 assert!(s.delete(Id::new(1, 0)));
2912 assert_eq!(counters(&s), (Some(3), Some(2)));
2913
2914 assert!(s.delete(Id::new(5, 0)));
2917 assert_eq!(counters(&s), (Some(3), None));
2918
2919 read(&mut s, "workers", "alice", None, 1);
2923 assert_eq!(s.group(b"workers").expect("g").last_id(), Id::new(4, 0));
2924 assert_eq!(counters(&s), (None, None));
2925 }
2926
2927 #[test]
2933 fn trimming_past_a_group_leaves_it_the_length() {
2934 let mut s = logged(500);
2935 s.create_group(b"workers", Id::MIN, Some(0));
2936 read(&mut s, "workers", "alice", Some(400), 1);
2937 assert_eq!(counters(&s), (Some(400), Some(100)));
2938
2939 assert_eq!(s.trim_maxlen(200, true, None), 300);
2941 assert_eq!(counters(&s), (Some(400), Some(100)));
2942
2943 assert_eq!(s.trim_maxlen(10, true, None), 190);
2946 assert_eq!(counters(&s), (Some(400), Some(10)));
2947 }
2948
2949 fn counters(s: &Stream) -> (Option<u64>, Option<u64>) {
2951 let g = s.group(b"workers").expect("the group");
2952 (g.entries_read(), s.lag(g))
2953 }
2954
2955 #[test]
2956 fn an_entry_that_went_away_still_comes_back_as_a_hole() {
2957 let mut s = logged(3);
2958 s.create_group(b"workers", Id::MIN, Some(0));
2959 read(&mut s, "workers", "alice", None, 1);
2960 assert!(s.delete(Id::new(2, 0)));
2961
2962 let mut out = Vec::new();
2963 s.read_group_pending(b"workers", b"alice", Id::MIN, None, 2, |id, fields| {
2964 out.push((id, fields.is_some()));
2965 true
2966 });
2967 assert_eq!(
2968 out,
2969 vec![
2970 (Id::new(1, 0), true),
2971 (Id::new(2, 0), false),
2972 (Id::new(3, 0), true)
2973 ]
2974 );
2975 }
2976
2977 #[test]
2978 fn acking_clears_the_pending_list() {
2979 let mut s = logged(3);
2980 s.create_group(b"workers", Id::MIN, Some(0));
2981 read(&mut s, "workers", "alice", None, 1);
2982 let g = s.group_mut(b"workers").expect("the group");
2983 assert!(g.ack(Id::new(2, 0)));
2984 assert_eq!(g.pending_len(), 2);
2985 }
2986
2987 #[test]
2988 fn a_claim_moves_work_off_a_consumer_that_stopped() {
2989 let mut s = logged(2);
2990 s.create_group(b"workers", Id::MIN, Some(0));
2991 read(&mut s, "workers", "alice", None, 100);
2992
2993 let mut gone = Vec::new();
2994 let took = s
2995 .claim(
2996 b"workers",
2997 b"bob",
2998 &[Id::new(1, 0), Id::new(2, 0)],
2999 500,
3000 5_000,
3001 None,
3002 true,
3003 false,
3004 5_000,
3005 &mut gone,
3006 )
3007 .expect("the group");
3008 assert_eq!(took, vec![Id::new(1, 0), Id::new(2, 0)]);
3009 assert!(gone.is_empty());
3010
3011 let g = s.group(b"workers").expect("the group");
3012 assert!(g.consumer_named(b"alice").expect("alice").is_empty());
3013 assert_eq!(g.consumer_named(b"bob").expect("bob").len(), 2);
3014 assert_eq!(g.nack(Id::new(1, 0)).expect("a nack").count(), 2);
3015 }
3016
3017 #[test]
3018 fn a_claim_leaves_work_that_is_not_idle_enough_alone() {
3019 let mut s = logged(1);
3020 s.create_group(b"workers", Id::MIN, Some(0));
3021 read(&mut s, "workers", "alice", None, 100);
3022
3023 let mut gone = Vec::new();
3024 let took = s
3025 .claim(
3026 b"workers",
3027 b"bob",
3028 &[Id::new(1, 0)],
3029 5_000,
3030 200,
3031 None,
3032 true,
3033 false,
3034 200,
3035 &mut gone,
3036 )
3037 .expect("the group");
3038 assert!(took.is_empty());
3039 assert_eq!(
3040 s.group(b"workers")
3041 .expect("the group")
3042 .consumer_named(b"alice")
3043 .expect("alice")
3044 .len(),
3045 1
3046 );
3047 }
3048
3049 #[test]
3050 fn claiming_an_entry_that_went_away_drops_it_instead() {
3051 let mut s = logged(2);
3052 s.create_group(b"workers", Id::MIN, Some(0));
3053 read(&mut s, "workers", "alice", None, 100);
3054 assert!(s.delete(Id::new(1, 0)));
3055
3056 let mut gone = Vec::new();
3057 let took = s
3058 .claim(
3059 b"workers",
3060 b"bob",
3061 &[Id::new(1, 0), Id::new(2, 0)],
3062 0,
3063 5_000,
3064 None,
3065 true,
3066 false,
3067 5_000,
3068 &mut gone,
3069 )
3070 .expect("the group");
3071 assert_eq!(took, vec![Id::new(2, 0)]);
3072 assert_eq!(gone, vec![Id::new(1, 0)]);
3073 assert_eq!(s.group(b"workers").expect("the group").pending_len(), 1);
3074 }
3075
3076 #[test]
3077 fn force_only_works_on_an_entry_that_is_really_there() {
3078 let mut s = logged(2);
3079 s.create_group(b"workers", s.last_id(), Some(2));
3080
3081 let mut gone = Vec::new();
3082 let took = s
3083 .claim(
3084 b"workers",
3085 b"bob",
3086 &[Id::new(1, 0), Id::new(99, 0)],
3087 0,
3088 100,
3089 None,
3090 true,
3091 true,
3092 100,
3093 &mut gone,
3094 )
3095 .expect("the group");
3096 assert_eq!(took, vec![Id::new(1, 0)], "99-0 is not in the stream");
3097 assert_eq!(s.group(b"workers").expect("the group").pending_len(), 1);
3098 }
3099
3100 #[test]
3101 fn autoclaim_sweeps_the_stale_ones_and_says_where_it_stopped() {
3102 let mut s = logged(6);
3103 s.create_group(b"workers", Id::MIN, Some(0));
3104 read(&mut s, "workers", "alice", Some(3), 100);
3105 read(&mut s, "workers", "alice", None, 900);
3106
3107 let mut gone = Vec::new();
3108 let (cursor, took) = s
3109 .autoclaim(
3110 b"workers",
3111 b"bob",
3112 Id::MIN,
3113 500,
3114 100,
3115 true,
3116 1_000,
3117 &mut gone,
3118 )
3119 .expect("the group");
3120 assert_eq!(cursor, None, "the sweep reached the end");
3121 assert_eq!(took, vec![Id::new(1, 0), Id::new(2, 0), Id::new(3, 0)]);
3122 assert_eq!(
3123 s.group(b"workers")
3124 .expect("the group")
3125 .consumer_named(b"bob")
3126 .expect("bob")
3127 .len(),
3128 3
3129 );
3130 }
3131
3132 #[test]
3133 fn autoclaim_hands_back_a_cursor_when_it_hits_the_count() {
3134 let mut s = logged(10);
3135 s.create_group(b"workers", Id::MIN, Some(0));
3136 read(&mut s, "workers", "alice", None, 100);
3137
3138 let mut gone = Vec::new();
3139 let (cursor, took) = s
3140 .autoclaim(b"workers", b"bob", Id::MIN, 0, 4, true, 1_000, &mut gone)
3141 .expect("the group");
3142 assert_eq!(took.len(), 4);
3143 assert_eq!(cursor, Some(Id::new(5, 0)));
3144
3145 let (cursor, took) = s
3147 .autoclaim(
3148 b"workers",
3149 b"bob",
3150 cursor.expect("a cursor"),
3151 0,
3152 100,
3153 true,
3154 1_000,
3155 &mut gone,
3156 )
3157 .expect("the group");
3158 assert_eq!(took.len(), 6);
3159 assert_eq!(cursor, None);
3160 }
3161
3162 #[test]
3163 fn a_group_survives_the_stream_being_trimmed_under_it() {
3164 let mut s = logged(10);
3165 s.create_group(b"workers", Id::MIN, Some(0));
3166 read(&mut s, "workers", "alice", Some(5), 100);
3167 assert_eq!(s.trim_maxlen(3, true, None), 7);
3169
3170 assert_eq!(s.group(b"workers").expect("the group").pending_len(), 5);
3171 let mut gone = Vec::new();
3172 let took = s
3173 .claim(
3174 b"workers",
3175 b"bob",
3176 &(1..=5).map(|ms| Id::new(ms, 0)).collect::<Vec<_>>(),
3177 0,
3178 1_000,
3179 None,
3180 true,
3181 false,
3182 1_000,
3183 &mut gone,
3184 )
3185 .expect("the group");
3186 assert!(took.is_empty(), "none of them are there any more");
3187 assert_eq!(gone.len(), 5);
3188 assert_eq!(s.group(b"workers").expect("the group").pending_len(), 0);
3189 }
3190
3191 fn round_trip(s: &Stream) -> Stream {
3193 let mut bytes = Vec::new();
3194 s.freeze(&mut bytes);
3195 let back = Stream::thaw(&bytes).expect("our own bytes");
3196 assert_eq!(dump(&back), dump(s), "the entries");
3197 assert_eq!(back.len(), s.len(), "the length");
3198 assert_eq!(back.added(), s.added(), "the count added");
3199 assert_eq!(back.last_id(), s.last_id(), "the last ID");
3200 assert_eq!(back.max_deleted_id(), s.max_deleted_id(), "the max deleted");
3201 assert_eq!(back.nodes(), s.nodes(), "the node count");
3202 assert_eq!(back, *s, "the whole thing");
3203 back
3204 }
3205
3206 #[test]
3207 fn a_frozen_stream_comes_back_with_every_entry_it_held() {
3208 let mut s = Stream::new();
3209 for ms in 1..=500u64 {
3210 add(&mut s, ms, 0, &[("job", "x"), ("n", "1")]);
3211 }
3212 add(&mut s, 500, 1, &[("job", "y")]);
3213 assert!(s.nodes() > 1, "more than one node, so the walk is tested");
3214 round_trip(&s);
3215 }
3216
3217 #[test]
3218 fn a_frozen_stream_keeps_the_holes_and_the_counters() {
3219 let mut s = logged(200);
3220 for ms in [3u64, 4, 5, 100, 199] {
3221 assert!(s.delete(Id::new(ms, 0)));
3222 }
3223 s.trim_minid(Id::new(20, 0), true, None);
3224 let back = round_trip(&s);
3225 assert_eq!(back.first_id(), Some(Id::new(20, 0)));
3226 assert!(!back.contains(Id::new(100, 0)), "a hole is still a hole");
3227 assert_eq!(back.max_deleted_id(), Id::new(199, 0));
3228 }
3229
3230 #[test]
3231 fn a_frozen_stream_keeps_its_groups_and_who_is_holding_what() {
3232 let mut s = logged(20);
3233 s.create_group(b"workers", Id::MIN, Some(0));
3234 s.create_group(b"audit", Id::new(5, 0), None);
3235 read(&mut s, "workers", "alice", Some(6), 1_000);
3236 read(&mut s, "workers", "bob", Some(4), 2_000);
3237 assert_eq!(
3240 s.nack(b"workers", Id::new(2, 0), Retry::Keep, true),
3241 Some(true)
3242 );
3243 s.group_mut(b"workers")
3246 .expect("the group")
3247 .create_consumer(b"carol", 3_000);
3248 read(&mut s, "workers", "dave", Some(2), 4_000);
3249 s.group_mut(b"workers")
3250 .expect("the group")
3251 .delete_consumer(b"carol");
3252
3253 let back = round_trip(&s);
3254 let g = back.group(b"workers").expect("the group");
3255 assert_eq!(g.pending_len(), 12);
3256 assert_eq!(g.nacked_len(), 1);
3257 assert_eq!(g.entries_read(), Some(12));
3258 assert_eq!(g.consumer_named(b"alice").expect("alice").len(), 5);
3260 assert_eq!(g.consumer_named(b"bob").expect("bob").len(), 4);
3261 assert_eq!(g.consumer_named(b"dave").expect("dave").len(), 2);
3262 assert_eq!(g.consumer_named(b"carol"), None);
3263 assert_eq!(g.slot(b"dave"), Some(3));
3266 assert_eq!(g.nack(Id::new(1, 0)).expect("a nack").owner(), Some(0));
3267 assert_eq!(g.nack(Id::new(2, 0)).expect("a nack").owner(), None);
3268 assert_eq!(
3269 back.group(b"audit").expect("audit").last_id(),
3270 Id::new(5, 0)
3271 );
3272 assert_eq!(back.group(b"audit").expect("audit").entries_read(), None);
3273 }
3274
3275 #[test]
3276 fn a_stream_that_came_back_still_takes_entries_and_reads_them() {
3277 let mut s = logged(10);
3278 s.create_group(b"workers", Id::MIN, Some(0));
3279 read(&mut s, "workers", "alice", Some(4), 1_000);
3280
3281 let mut back = round_trip(&s);
3282 add(&mut back, 11, 0, &[("job", "new")]);
3283 assert_eq!(back.len(), 11);
3284 assert_eq!(
3285 read(&mut back, "workers", "alice", Some(3), 2_000),
3286 vec![Id::new(5, 0), Id::new(6, 0), Id::new(7, 0)]
3287 );
3288 assert!(
3289 back.group_mut(b"workers")
3290 .expect("the group")
3291 .ack(Id::new(1, 0))
3292 );
3293 assert_eq!(back.group(b"workers").expect("the group").pending_len(), 6);
3294 }
3295
3296 #[test]
3297 fn an_empty_stream_that_still_exists_comes_back() {
3298 let mut s = logged(3);
3299 for ms in 1..=3u64 {
3300 assert!(s.delete(Id::new(ms, 0)));
3301 }
3302 assert_eq!(s.nodes(), 0, "the last node went with the last entry");
3303 let back = round_trip(&s);
3304 assert!(back.is_empty());
3305 assert_eq!(back.last_id(), Id::new(3, 0));
3308 round_trip(&Stream::new());
3309 }
3310
3311 #[test]
3312 fn a_frozen_stream_that_arrives_damaged_is_an_error_and_not_a_panic() {
3313 let mut s = logged(8);
3314 s.create_group(b"workers", Id::MIN, Some(0));
3315 read(&mut s, "workers", "alice", Some(3), 1_000);
3316 let mut bytes = Vec::new();
3317 s.freeze(&mut bytes);
3318
3319 for cut in 0..bytes.len() {
3320 assert!(Stream::thaw(&bytes[..cut]).is_err(), "cut at {cut}");
3321 }
3322 for at in 0..bytes.len().min(40) {
3325 for bit in 0..8 {
3326 let mut bad = bytes.clone();
3327 bad[at] ^= 1 << bit;
3328 let _ = Stream::thaw(&bad);
3332 }
3333 }
3334 assert_eq!(Stream::thaw(&[]), Err(Broken::Short));
3335 assert_eq!(Stream::thaw(&[9]), Err(Broken::Form));
3336 }
3337
3338 #[test]
3339 fn nothing_at_all() {
3340 let mut s = Stream::new();
3341 assert!(s.is_empty());
3342 assert_eq!(s.len(), 0);
3343 assert_eq!(s.first_id(), None);
3344 assert_eq!(s.last_id(), Id::MIN);
3345 assert_eq!(s.trim_maxlen(0, true, None), 0);
3346 assert_eq!(s.trim_minid(Id::MAX, true, None), 0);
3347 assert_eq!(dump(&s), vec![]);
3348 }
3349}