Skip to main content

yo_kv/
streams.rs

1//! The stream commands.
2//!
3//! One method per Redis command on [`Keyspace`], the same arrangement the list
4//! and set commands use and for the same reason: a key belongs to the database
5//! and not to a type, so `XADD` against a string has to be able to see that it
6//! is a string. The log itself is [`crate::stream`] and the groups are
7//! [`crate::stream::groups`]. This file is what the wire and the embedded API
8//! both call.
9//!
10//! # An empty stream is still a stream
11//!
12//! Every other collection here disappears when its last element goes, so
13//! `EXISTS` answers zero after the last `LPOP`. A stream does not. `XDEL` of
14//! every entry leaves the key, and so does a `MAXLEN 0` trim, because the
15//! stream is still carrying the last ID it handed out and the groups reading
16//! it, and throwing those away because the entries have aged out would hand the
17//! same IDs out twice.
18//!
19//! That is why the methods here do not have the create on write and drop on
20//! empty pair the other four types have. There is a create, [`Keyspace::xadd`]
21//! and the `MKSTREAM` half of [`Keyspace::xgroup_create`], and there is no
22//! drop.
23//!
24//! # Reading is one method and not eight
25//!
26//! `XINFO` reads a dozen fields off a stream and none of them are decisions.
27//! Rather than a method here per field, [`Keyspace::stream`] hands the wire the
28//! stream and it reads what it needs, which is the same borrow it would have
29//! got and a great deal less to keep in step. Everything that changes the
30//! stream, or that has to say something about a key that is missing or is the
31//! wrong type, is a method.
32
33use 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
40/// What every stream command says about an ID it cannot read.
41pub const BAD_ID: &str = "Invalid stream ID specified as stream command argument";
42
43/// What `XADD` says about an ID that is not above the last one.
44pub const NOT_GREATER: &str =
45    "The ID specified in XADD is equal or smaller than the target stream top item";
46
47/// What it says about `0-0`, which no entry can have because nothing sorts below it.
48pub const ZERO_ID: &str = "The ID specified in XADD must be greater than 0-0";
49
50/// What it says when the stream is at the last ID there is.
51pub const EXHAUSTED: &str =
52    "The stream has exhausted the last possible ID, unable to add more items";
53
54/// What `XGROUP` says about a key that is not there.
55///
56/// Redis's wording, run on sentence and all, because it goes on the wire
57/// verbatim and a client's test suite compares it.
58pub 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
60/// What `XGROUP CREATE` says about a group that is already there.
61///
62/// This one goes out under a `BUSYGROUP` prefix rather than `ERR`, which the
63/// wire layer writes where it decides it, the same way it writes `NOPROTO` and
64/// `WRONGPASS`.
65pub const GROUP_EXISTS: &str = "Consumer Group name already exists";
66
67/// What `XSETID` says about an ID below an entry that is still there.
68pub const SETID_TOO_SMALL: &str =
69    "The ID specified in XSETID is smaller than the target stream top item";
70
71/// And what it says about an ID below the `MAXDELETEDID` it was handed.
72///
73/// A separate sentence because it is a separate mistake. The one above is about
74/// an entry the stream still holds and this is about one it says it deleted, and
75/// a stream whose last ID sat below its own high water mark for deletions would
76/// hand that ID out again.
77pub const SETID_BELOW_MAX_DELETED: &str =
78    "The ID specified in XSETID is smaller than the provided max_deleted_entry_id";
79
80/// What a command that needs the key says when it is not there.
81pub const NO_SUCH_KEY: &str = "no such key";
82
83/// What `XGROUP` and `XINFO CONSUMERS` say about a group that is not there.
84///
85/// A function rather than a constant because Redis puts the group and the key
86/// in it, and a client watching for a particular group in its logs is reading
87/// exactly that. It goes out under a `NOGROUP` prefix.
88///
89/// There are three of these and they are not interchangeable. This is the one
90/// the commands that only ever name one key use. Every wording here was read
91/// off Redis 8.10.1, since the difference between them is not something the
92/// documentation mentions and a client library matching on the text would break
93/// on a paraphrase.
94#[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/// What `XPENDING`, `XCLAIM` and `XAUTOCLAIM` say instead.
104///
105/// The key comes first here and the sentence allows for either half being the
106/// missing one, because these commands cannot tell a stream with no such group
107/// from a key that is not a stream at all without looking twice.
108#[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/// And what `XREADGROUP` says, which is the one above with its own tail.
118///
119/// The tail is there because `XREADGROUP` is the command people send by
120/// accident against a stream they never made a group on, so Redis spells out
121/// which option is at fault.
122#[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/// What `XADD` was told to use for the ID.
132#[derive(Debug, Clone, Copy, PartialEq, Eq)]
133pub enum Add {
134    /// `*`, which is the clock, or the last ID with one added if the clock has
135    /// not moved.
136    Auto,
137    /// `5-*`, which is that millisecond and the next free sequence inside it.
138    Seq(u64),
139    /// `5-3`, which is exactly that and fails if it is not above the last one.
140    At(Id),
141}
142
143/// What a trim was told to cut down to.
144///
145/// `exact` is Redis's `=` against `~`. Without it only whole nodes go, so the
146/// stream is left at the threshold or a little over and no node is ever
147/// rewritten, which is the form to use and is why `~` exists.
148///
149/// `limit` only ever arrives with `~`, because Redis refuses the two together
150/// with `=` and the wire layer refuses it here for the same reason: the limit
151/// is a brake on how long one command runs, and an exact trim that stopped
152/// early would not have done what it was asked.
153#[derive(Debug, Clone, Copy, PartialEq, Eq)]
154pub enum Trim {
155    /// No trim, which is `XADD` without a `MAXLEN` or a `MINID`.
156    None,
157    /// `MAXLEN n`, which keeps the newest `n` entries.
158    MaxLen {
159        /// How many to keep.
160        len: u64,
161        /// Redis's `=` rather than `~`.
162        exact: bool,
163        /// `LIMIT`, which stops the trim after that many entries have gone.
164        limit: Option<u64>,
165    },
166    /// `MINID id`, which drops everything below `id`.
167    MinId {
168        /// The lowest ID to keep.
169        id: Id,
170        /// Redis's `=` rather than `~`.
171        exact: bool,
172        /// `LIMIT`, as above.
173        limit: Option<u64>,
174    },
175}
176
177/// Where a group's bookmark is being put, which is `XGROUP CREATE` and `SETID`.
178#[derive(Debug, Clone, Copy, PartialEq, Eq)]
179pub enum Start {
180    /// `$`, the last ID the stream has handed out, so the group sees only what
181    /// arrives after it was made.
182    Last,
183    /// An ID, usually `0`, so the group reads the stream from there.
184    At(Id),
185}
186
187/// What `XREADGROUP` was asked for.
188#[derive(Debug, Clone, Copy, PartialEq, Eq)]
189pub enum From {
190    /// `>`, meaning entries no consumer in this group has been given yet.
191    New,
192    /// An ID, meaning what this consumer is already holding above that ID.
193    ///
194    /// Redis counts this as a real delivery, so the entries come back with
195    /// their delivery time reset and their count up by one.
196    Pending(Id),
197}
198
199/// Everything `XREADGROUP` takes past the key.
200///
201/// A struct for the same reason [`Claim`] is one, and because the wire parses
202/// `GROUP g c`, `COUNT n` and `NOACK` in any order and would otherwise be
203/// carrying five loose values from the parse to the call.
204#[derive(Debug, Clone, Copy)]
205pub struct Read<'a> {
206    /// Which group is reading.
207    pub group: &'a [u8],
208    /// Which consumer inside it, created by turning up if it is new.
209    pub consumer: &'a [u8],
210    /// `>` or an ID, which are two quite different commands wearing one name.
211    pub from: From,
212    /// `COUNT`, or everything there is.
213    pub count: Option<usize>,
214    /// `NOACK`, which hands the entries over without writing them down.
215    pub noack: bool,
216}
217
218/// Everything `XCLAIM` takes past the key and the IDs.
219///
220/// A struct rather than seven more arguments because the command parses them as
221/// one thing and they travel together from the wire to here, and because
222/// `XAUTOCLAIM` takes the same set with one extra of its own.
223#[derive(Debug, Clone, Copy)]
224pub struct Claim<'a> {
225    /// Which group's pending list is being moved around.
226    pub group: &'a [u8],
227    /// Which consumer ends up holding what is claimed.
228    pub consumer: &'a [u8],
229    /// Skip anything idle less than this many milliseconds.
230    pub min_idle: u64,
231    /// What to set the delivery time to, which `IDLE` and `TIME` both work out.
232    pub time: u64,
233    /// `RETRYCOUNT`, or leave the count where it is.
234    pub retry: Option<u64>,
235    /// Whether the delivery count goes up, which it does unless `JUSTID` was
236    /// asked for.
237    pub bump: bool,
238    /// `FORCE`, which makes a pending entry for one that is in the stream and
239    /// was never handed out.
240    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    /// The stream under `key`, for reading.
259    ///
260    /// `None` for a key that is not there or has expired, and an error for one
261    /// holding something else, which is the three way answer every type's entry
262    /// point here gives. `XINFO` and `XLEN` go through this rather than through
263    /// a method each, because reading a field off a stream is not a decision.
264    ///
265    /// # Errors
266    ///
267    /// [`Code::WrongType`] for a key holding anything but a stream.
268    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    /// The same, for a caller that is going to change it.
276    ///
277    /// # Errors
278    ///
279    /// [`Code::WrongType`] for a key holding anything but a stream.
280    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    /// `XADD key [NOMKSTREAM] [trim] id field value [field value ...]`.
288    ///
289    /// Answers the ID that was written, or `None` when `NOMKSTREAM` was asked
290    /// for and the key was not there.
291    ///
292    /// The trim runs after the append, which is Redis's order and matters when
293    /// the threshold is `MAXLEN 1`: the entry that was just written is the one
294    /// that survives.
295    ///
296    /// # Errors
297    ///
298    /// [`Code::WrongType`] for a key holding something else, and
299    /// [`Code::Invalid`] for an ID that is zero, is not above the last one, or
300    /// asks for a sequence inside a millisecond that has already filled up.
301    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        Ok(self
311            .xadd_trimmed(key, id, fields, trim, mkstream, now)?
312            .map(|(id, _)| id))
313    }
314
315    /// The same write, saying how many entries the trim behind it took.
316    ///
317    /// [`Keyspace::xadd`] is this with that count dropped, which is all a caller
318    /// wants when it is only writing. The wire layer wants it because a trim
319    /// that removed something is a second keyspace notification and a trim that
320    /// found nothing over the threshold is not, and the ID that comes back says
321    /// nothing either way.
322    ///
323    /// # Errors
324    ///
325    /// The same as [`Keyspace::xadd`].
326    pub fn xadd_trimmed(
327        &mut self,
328        key: &[u8],
329        id: Add,
330        fields: &[(&[u8], &[u8])],
331        trim: Trim,
332        mkstream: bool,
333        now: u64,
334    ) -> Result<Option<(Id, u64)>> {
335        let limits = self.stream_limits;
336        let at = match self.live_slot(key, Kind::Stream)? {
337            Some(at) => at,
338            None if mkstream => self.new_stream(key),
339            None => return Ok(None),
340        };
341        let s = self.stream_at(at);
342        // Worked out against the stream as it is, before anything is written,
343        // so that a `*` that has nowhere to go is an error and not a panic.
344        let want = match id {
345            Add::Auto => s.auto_id(now).ok_or_else(exhausted)?,
346            Add::Seq(ms) => s.auto_seq(ms).ok_or_else(|| {
347                if ms < s.last_id().ms {
348                    Error::new(Code::Invalid, NOT_GREATER)
349                } else {
350                    exhausted()
351                }
352            })?,
353            Add::At(id) => id,
354        };
355        s.append(want, fields, limits).map_err(refused)?;
356        let cut = cut(s, trim);
357        Ok(Some((want, cut)))
358    }
359
360    /// `XDEL key id [id ...]`. Answers how many were there to delete.
361    ///
362    /// # Errors
363    ///
364    /// [`Code::WrongType`] for a key holding something else.
365    pub fn xdel(&mut self, key: &[u8], ids: impl Iterator<Item = Id>) -> Result<u64> {
366        let Some(at) = self.live_slot(key, Kind::Stream)? else {
367            return Ok(0);
368        };
369        let s = self.stream_at(at);
370        Ok(ids.filter(|&id| s.delete(id)).count() as u64)
371    }
372
373    /// `XDELEX key [KEEPREF|DELREF|ACKED] IDS numids id [id ...]`.
374    ///
375    /// The callback gets what became of each ID, in the order they were given,
376    /// and the answer is how many entries left the log. A key that is not there
377    /// is not an error and not a short reply either: every ID gets
378    /// [`Fate::Missing`], which is what a real server answers and is why the ID
379    /// list is walked even when there is nothing to walk it against.
380    ///
381    /// # Errors
382    ///
383    /// [`Code::WrongType`] for a key holding something else.
384    pub fn xdelex<F>(
385        &mut self,
386        key: &[u8],
387        refs: Refs,
388        ids: impl Iterator<Item = Id>,
389        mut f: F,
390    ) -> Result<u64>
391    where
392        F: FnMut(Fate),
393    {
394        let Some(at) = self.live_slot(key, Kind::Stream)? else {
395            ids.for_each(|_| f(Fate::Missing));
396            return Ok(0);
397        };
398        let s = self.stream_at(at);
399        let mut gone = 0;
400        ids.for_each(|id| {
401            let fate = s.delete_ref(id, refs);
402            gone += u64::from(fate == Fate::Gone);
403            f(fate);
404        });
405        Ok(gone)
406    }
407
408    /// `XACKDEL key group [KEEPREF|DELREF|ACKED] IDS numids id [id ...]`.
409    ///
410    /// The same shape, and a group that is not there behaves like a key that is
411    /// not there rather than raising `NOGROUP`, because the answer this command
412    /// gives per ID is about the pending list and an absent group is holding
413    /// nothing.
414    ///
415    /// The count that comes back is how many entries left the log, which is not
416    /// how many IDs answered [`Fate::Gone`]. See [`Stream::ack_delete`].
417    ///
418    /// # Errors
419    ///
420    /// [`Code::WrongType`] for a key holding something else.
421    pub fn xackdel<F>(
422        &mut self,
423        key: &[u8],
424        group: &[u8],
425        refs: Refs,
426        ids: impl Iterator<Item = Id>,
427        mut f: F,
428    ) -> Result<u64>
429    where
430        F: FnMut(Fate),
431    {
432        let Some(at) = self.live_slot(key, Kind::Stream)? else {
433            ids.for_each(|_| f(Fate::Missing));
434            return Ok(0);
435        };
436        let s = self.stream_at(at);
437        let mut gone = 0;
438        ids.for_each(|id| {
439            let (fate, took) = s.ack_delete(group, id, refs);
440            gone += u64::from(took);
441            f(fate);
442        });
443        Ok(gone)
444    }
445
446    /// `XNACK key group <SILENT|FAIL|FATAL> IDS numids id [id ...] [RETRYCOUNT n] [FORCE]`.
447    ///
448    /// Answers how many entries were released, and `None` when there is no such
449    /// key or group, which this command does raise `NOGROUP` for.
450    ///
451    /// # Errors
452    ///
453    /// [`Code::WrongType`] for a key holding something else.
454    pub fn xnack(
455        &mut self,
456        key: &[u8],
457        group: &[u8],
458        retry: Retry,
459        force: bool,
460        ids: impl Iterator<Item = Id>,
461    ) -> Result<Option<u64>> {
462        let Some(at) = self.live_slot(key, Kind::Stream)? else {
463            return Ok(None);
464        };
465        let s = self.stream_at(at);
466        if s.group(group).is_none() {
467            return Ok(None);
468        }
469        let mut done = 0;
470        for id in ids {
471            done += u64::from(s.nack(group, id, retry, force).unwrap_or(false));
472        }
473        Ok(Some(done))
474    }
475
476    /// `XTRIM key strategy`. Answers how many entries went.
477    ///
478    /// # Errors
479    ///
480    /// [`Code::WrongType`] for a key holding something else.
481    pub fn xtrim(&mut self, key: &[u8], trim: Trim) -> Result<u64> {
482        let Some(at) = self.live_slot(key, Kind::Stream)? else {
483            return Ok(0);
484        };
485        Ok(cut(self.stream_at(at), trim))
486    }
487
488    /// `XSETID key id [ENTRIESADDED n] [MAXDELETEDID id]`.
489    ///
490    /// # Errors
491    ///
492    /// [`Code::WrongType`] for a key holding something else,
493    /// [`Code::NotFound`] for a key that is not there, and [`Code::Invalid`]
494    /// for an ID below an entry the stream still holds.
495    pub fn xsetid(
496        &mut self,
497        key: &[u8],
498        last: Id,
499        added: Option<u64>,
500        max_deleted: Option<Id>,
501    ) -> Result<()> {
502        let Some(at) = self.live_slot(key, Kind::Stream)? else {
503            return Err(Error::new(Code::NotFound, NO_SUCH_KEY));
504        };
505        // Checked here rather than in `Stream::set_id`, because it is a rule
506        // about the two arguments and not about the stream: the pair is
507        // contradictory whatever the stream currently holds.
508        if max_deleted.is_some_and(|id| last < id) {
509            return Err(Error::new(Code::Invalid, SETID_BELOW_MAX_DELETED));
510        }
511        self.stream_at(at)
512            .set_id(last, added, max_deleted)
513            .map_err(|_| Error::new(Code::Invalid, SETID_TOO_SMALL))
514    }
515
516    /// `XRANGE` and `XREVRANGE`, which differ only in the direction.
517    ///
518    /// `start` and `end` are the low and the high end either way, so the wire
519    /// layer swaps `XREVRANGE`'s arguments once rather than every reader here
520    /// working out which is which. Answers how many entries the callback saw.
521    ///
522    /// # Errors
523    ///
524    /// [`Code::WrongType`] for a key holding something else.
525    pub fn xrange_into<F>(
526        &mut self,
527        key: &[u8],
528        start: Id,
529        end: Id,
530        count: Option<usize>,
531        rev: bool,
532        f: F,
533    ) -> Result<usize>
534    where
535        F: FnMut(Id, Fields<'_>) -> bool,
536    {
537        let Some(s) = self.stream(key)? else {
538            return Ok(0);
539        };
540        Ok(if rev {
541            s.rev_range(start, end, count, f)
542        } else {
543            s.range(start, end, count, f)
544        })
545    }
546
547    /// `XREAD ... STREAMS key id`, which is a plain range with no group.
548    ///
549    /// Everything after `after`, up to `count`. Answers how many the callback
550    /// saw, which is zero for a key that is not there, because `XREAD` on a
551    /// missing key is nothing to report rather than an error.
552    ///
553    /// # Errors
554    ///
555    /// [`Code::WrongType`] for a key holding something else.
556    pub fn xread_into<F>(
557        &mut self,
558        key: &[u8],
559        after: Id,
560        count: Option<usize>,
561        f: F,
562    ) -> Result<usize>
563    where
564        F: FnMut(Id, Fields<'_>) -> bool,
565    {
566        let Some(from) = after.next() else {
567            return Ok(0);
568        };
569        self.xrange_into(key, from, Id::MAX, count, false, f)
570    }
571
572    /// `XGROUP CREATE key group id [MKSTREAM] [ENTRIESREAD n]`.
573    ///
574    /// Answers whether the group was made, which is `false` when one of that
575    /// name was already there and is the `BUSYGROUP` the wire reports.
576    ///
577    /// # Errors
578    ///
579    /// [`Code::WrongType`] for a key holding something else, and
580    /// [`Code::NotFound`] for a key that is not there without `MKSTREAM`.
581    pub fn xgroup_create(
582        &mut self,
583        key: &[u8],
584        group: &[u8],
585        at: Start,
586        mkstream: bool,
587        read: Option<u64>,
588    ) -> Result<bool> {
589        let slot = match self.live_slot(key, Kind::Stream)? {
590            Some(slot) => slot,
591            None if mkstream => self.new_stream(key),
592            None => return Err(Error::new(Code::NotFound, NO_KEY_FOR_GROUP)),
593        };
594        let s = self.stream_at(slot);
595        let last = position(s, at);
596        // `read` and not a zero for a group with no `ENTRIESREAD`. A fresh group
597        // does not know how many entries are behind it, and saying zero would be
598        // a claim rather than a default: `XINFO GROUPS` reports the counter as
599        // null on a real server until something sets it, and the lag is worked
600        // out from where the bookmark sits instead.
601        let read = capped(read, s);
602        Ok(s.create_group(group, last, read))
603    }
604
605    /// `XGROUP DESTROY key group`. Answers whether there was one.
606    ///
607    /// A group that is not there is a zero and a key that is not there is an
608    /// error, which is Redis's rule for every `XGROUP` subcommand and is worth
609    /// stating because the two look like the same kind of nothing from a client.
610    /// They are not: destroying a group nobody made is a no op, and destroying a
611    /// group on a key nobody made is a mistake about which key.
612    ///
613    /// # Errors
614    ///
615    /// [`Code::WrongType`] for a key holding something else, and
616    /// [`Code::NotFound`] for a key that is not there.
617    pub fn xgroup_destroy(&mut self, key: &[u8], group: &[u8]) -> Result<bool> {
618        let Some(at) = self.live_slot(key, Kind::Stream)? else {
619            return Err(Error::new(Code::NotFound, NO_KEY_FOR_GROUP));
620        };
621        Ok(self.stream_at(at).destroy_group(group))
622    }
623
624    /// `XGROUP SETID key group id [ENTRIESREAD n]`.
625    ///
626    /// `None` when there is no such group, which the wire reports as `NOGROUP`.
627    ///
628    /// # Errors
629    ///
630    /// [`Code::WrongType`] for a key holding something else, and
631    /// [`Code::NotFound`] for a key that is not there.
632    pub fn xgroup_setid(
633        &mut self,
634        key: &[u8],
635        group: &[u8],
636        at: Start,
637        read: Option<u64>,
638    ) -> Result<Option<()>> {
639        let Some(slot) = self.live_slot(key, Kind::Stream)? else {
640            return Err(Error::new(Code::NotFound, NO_KEY_FOR_GROUP));
641        };
642        let s = self.stream_at(slot);
643        let last = position(s, at);
644        let read = capped(read, s);
645        let Some(g) = s.group_mut(group) else {
646            return Ok(None);
647        };
648        // `read` and not the group's old counter when nothing was named, so
649        // `XGROUP SETID key group 0` gives the counter up rather than leaving
650        // one that was true of somewhere else. That is what a real server does
651        // and it is visible immediately: `XINFO GROUPS` reports both the counter
652        // and the lag as null afterwards, until a read or an `ENTRIESREAD` puts
653        // a number back.
654        g.set_id(last, read);
655        Ok(Some(()))
656    }
657
658    /// `XGROUP CREATECONSUMER key group consumer`.
659    ///
660    /// Answers whether the consumer was made, and `None` when there is no such
661    /// group.
662    ///
663    /// # Errors
664    ///
665    /// [`Code::WrongType`] for a key holding something else, and
666    /// [`Code::NotFound`] for a key that is not there.
667    pub fn xgroup_create_consumer(
668        &mut self,
669        key: &[u8],
670        group: &[u8],
671        consumer: &[u8],
672        now: u64,
673    ) -> Result<Option<bool>> {
674        let Some(g) = self.group_mut_of(key, group)? else {
675            return Ok(None);
676        };
677        Ok(Some(g.create_consumer(consumer, now)))
678    }
679
680    /// `XGROUP DELCONSUMER key group consumer`.
681    ///
682    /// Answers how many pending entries went with it, and `None` when there is
683    /// no such group.
684    ///
685    /// # Errors
686    ///
687    /// [`Code::WrongType`] for a key holding something else, and
688    /// [`Code::NotFound`] for a key that is not there.
689    pub fn xgroup_del_consumer(
690        &mut self,
691        key: &[u8],
692        group: &[u8],
693        consumer: &[u8],
694    ) -> Result<Option<u64>> {
695        let Some(g) = self.group_mut_of(key, group)? else {
696            return Ok(None);
697        };
698        Ok(Some(g.delete_consumer(consumer)))
699    }
700
701    /// `XREADGROUP GROUP group consumer [COUNT n] [NOACK] STREAMS key id`.
702    ///
703    /// Answers how many entries the callback saw, and `None` when there is no
704    /// such group. The callback takes an `Option` because a history read can
705    /// name an entry that has since been deleted, and Redis puts a null in the
706    /// reply for it rather than leaving it out.
707    ///
708    /// # Errors
709    ///
710    /// [`Code::WrongType`] for a key holding something else, and
711    /// [`Code::NotFound`] for a key that is not there, which is the `NOGROUP`
712    /// Redis answers because a missing key cannot have the group either.
713    pub fn xreadgroup_into<F>(
714        &mut self,
715        key: &[u8],
716        want: Read<'_>,
717        now: u64,
718        mut f: F,
719    ) -> Result<Option<usize>>
720    where
721        F: FnMut(Id, Option<Fields<'_>>) -> bool,
722    {
723        let Some(at) = self.live_slot(key, Kind::Stream)? else {
724            return Ok(None);
725        };
726        let s = self.stream_at(at);
727        Ok(match want.from {
728            From::New => s.read_group(
729                want.group,
730                want.consumer,
731                want.count,
732                want.noack,
733                now,
734                |id, fields| f(id, Some(fields)),
735            ),
736            From::Pending(after) => {
737                s.read_group_pending(want.group, want.consumer, after, want.count, now, &mut f)
738            }
739        })
740    }
741
742    /// `XACK key group id [id ...]`. Answers how many were pending.
743    ///
744    /// # Errors
745    ///
746    /// [`Code::WrongType`] for a key holding something else.
747    pub fn xack(&mut self, key: &[u8], group: &[u8], ids: impl Iterator<Item = Id>) -> Result<u64> {
748        let Some(at) = self.live_slot(key, Kind::Stream)? else {
749            return Ok(0);
750        };
751        let Some(g) = self.stream_at(at).group_mut(group) else {
752            return Ok(0);
753        };
754        Ok(ids.filter(|&id| g.ack(id)).count() as u64)
755    }
756
757    /// `XPENDING key group [[IDLE ms] start end count [consumer]]`, the long form.
758    ///
759    /// The callback gets each entry with its NACK and its owner. Answers how
760    /// many it saw, and `None` when there is no such group.
761    ///
762    /// # Errors
763    ///
764    /// [`Code::WrongType`] for a key holding something else.
765    pub fn xpending_into<F>(
766        &mut self,
767        key: &[u8],
768        group: &[u8],
769        want: Filter,
770        now: u64,
771        f: F,
772    ) -> Result<Option<usize>>
773    where
774        F: FnMut(Id, &crate::stream::Nack, Option<&crate::stream::Consumer>) -> bool,
775    {
776        let Some(s) = self.stream(key)? else {
777            return Ok(None);
778        };
779        let Some(g) = s.group(group) else {
780            return Ok(None);
781        };
782        Ok(Some(g.pending_range(want, now, f)))
783    }
784
785    /// `XCLAIM key group consumer min-idle-time id [id ...]`.
786    ///
787    /// Answers what was claimed, and fills `gone` with the IDs that were in the
788    /// pending list and are no longer in the stream, which the claim clears out
789    /// on the way past because nobody can ever finish them. `None` when there
790    /// is no such group.
791    ///
792    /// # Errors
793    ///
794    /// [`Code::WrongType`] for a key holding something else.
795    pub fn xclaim(
796        &mut self,
797        key: &[u8],
798        ids: &[Id],
799        how: Claim<'_>,
800        now: u64,
801        gone: &mut Vec<Id>,
802    ) -> Result<Option<Vec<Id>>> {
803        let Some(at) = self.live_slot(key, Kind::Stream)? else {
804            return Ok(None);
805        };
806        Ok(self.stream_at(at).claim(
807            how.group,
808            how.consumer,
809            ids,
810            how.min_idle,
811            how.time,
812            how.retry,
813            how.bump,
814            how.force,
815            now,
816            gone,
817        ))
818    }
819
820    /// `XAUTOCLAIM key group consumer min-idle-time start [COUNT n] [JUSTID]`.
821    ///
822    /// Answers where a following call should carry on from, which is `None` at
823    /// the end of the list and is the `0-0` Redis replies with, along with what
824    /// was claimed. `gone` is filled the same way [`Keyspace::xclaim`] fills it.
825    ///
826    /// # Errors
827    ///
828    /// [`Code::WrongType`] for a key holding something else.
829    pub fn xautoclaim(
830        &mut self,
831        key: &[u8],
832        start: Id,
833        how: Claim<'_>,
834        count: usize,
835        now: u64,
836        gone: &mut Vec<Id>,
837    ) -> Result<Option<(Option<Id>, Vec<Id>)>> {
838        let Some(at) = self.live_slot(key, Kind::Stream)? else {
839            return Ok(None);
840        };
841        Ok(self.stream_at(at).autoclaim(
842            how.group,
843            how.consumer,
844            start,
845            how.min_idle,
846            count,
847            how.bump,
848            now,
849            gone,
850        ))
851    }
852
853    /// The group under a key, for the three commands that only touch the group.
854    ///
855    /// # Errors
856    ///
857    /// [`Code::WrongType`] for a key holding something else, and
858    /// [`Code::NotFound`] for a key that is not there, which is what `XGROUP`
859    /// says about all of its subcommands.
860    fn group_mut_of(&mut self, key: &[u8], group: &[u8]) -> Result<Option<&mut Group>> {
861        let Some(at) = self.live_slot(key, Kind::Stream)? else {
862            return Err(Error::new(Code::NotFound, NO_KEY_FOR_GROUP));
863        };
864        Ok(self.stream_at(at).group_mut(group))
865    }
866
867    fn stream_at(&mut self, at: u32) -> &mut Stream {
868        self.streams
869            .get_mut(at)
870            .expect("the record points at its body")
871    }
872
873    fn new_stream(&mut self, key: &[u8]) -> u32 {
874        let at = self.streams.insert(Stream::new());
875        let len = value::slot_record_len(false);
876        self.write_rec(key, len, |out| {
877            value::write_slot_record(out, Kind::Stream, at, None);
878        });
879        self.bodies += 1;
880        at
881    }
882}
883
884/// Where a bookmark goes for a `$` or for an ID.
885fn position(s: &Stream, at: Start) -> Id {
886    match at {
887        Start::Last => s.last_id(),
888        Start::At(id) => id,
889    }
890}
891
892/// An `ENTRIESREAD` held down to what the stream has ever added.
893///
894/// A group cannot have read more entries than were ever written, so a client
895/// that says it has is corrected rather than believed. Redis does the same and
896/// it is visible: `XGROUP CREATE key g 0 ENTRIESREAD 99` on a stream of three
897/// reports three afterwards, not ninety nine. It matters because the number is
898/// subtracted from the entry count to get the lag, and an inflated one would
899/// make the lag come out at zero on a group that has read nothing.
900fn capped(read: Option<u64>, s: &Stream) -> Option<u64> {
901    read.map(|n| n.min(s.added()))
902}
903
904/// Run a trim, whichever kind it is. Answers how many entries went.
905fn cut(s: &mut Stream, trim: Trim) -> u64 {
906    match trim {
907        Trim::None => 0,
908        Trim::MaxLen { len, exact, limit } => s.trim_maxlen(len, exact, limit),
909        Trim::MinId { id, exact, limit } => s.trim_minid(id, exact, limit),
910    }
911}
912
913fn exhausted() -> Error {
914    Error::new(Code::Invalid, EXHAUSTED)
915}
916
917fn refused(why: Refused) -> Error {
918    match why {
919        Refused::Zero => Error::new(Code::Invalid, ZERO_ID),
920        Refused::NotGreater => Error::new(Code::Invalid, NOT_GREATER),
921        Refused::Full => exhausted(),
922    }
923}
924
925#[cfg(test)]
926mod tests {
927    use super::*;
928
929    fn db() -> Keyspace {
930        Keyspace::new()
931    }
932
933    /// One entry, with the two fields a reading has.
934    fn add(d: &mut Keyspace, key: &[u8], id: Add) -> Id {
935        d.xadd(
936            key,
937            id,
938            &[(b"sensor", b"a4"), (b"reading", b"21.5")],
939            Trim::None,
940            true,
941            1_000,
942        )
943        .expect("a stream")
944        .expect("an ID")
945    }
946
947    /// Every ID in the stream, oldest first.
948    fn ids(d: &mut Keyspace, key: &[u8]) -> Vec<Id> {
949        let mut out = Vec::new();
950        d.xrange_into(key, Id::MIN, Id::MAX, None, false, |id, _| {
951            out.push(id);
952            true
953        })
954        .expect("a stream");
955        out
956    }
957
958    #[test]
959    fn a_write_makes_the_key_and_a_read_finds_it() {
960        let mut d = db();
961        assert_eq!(d.kind_of(b"s"), None);
962        let id = add(&mut d, b"s", Add::At(Id::new(5, 0)));
963        assert_eq!(id, Id::new(5, 0));
964        assert_eq!(d.kind_of(b"s"), Some(Kind::Stream));
965        assert_eq!(d.type_name(b"s"), Some("stream"));
966        assert_eq!(d.encoding_name(b"s"), Some("stream"));
967        assert_eq!(ids(&mut d, b"s"), vec![Id::new(5, 0)]);
968    }
969
970    #[test]
971    fn nomkstream_leaves_a_missing_key_missing() {
972        let mut d = db();
973        let got = d
974            .xadd(b"s", Add::Auto, &[(b"f", b"v")], Trim::None, false, 1_000)
975            .expect("a stream");
976        assert_eq!(got, None);
977        assert_eq!(d.kind_of(b"s"), None);
978    }
979
980    #[test]
981    fn an_auto_id_follows_the_clock_and_then_the_last_id() {
982        let mut d = db();
983        assert_eq!(add(&mut d, b"s", Add::Auto), Id::new(1_000, 0));
984        // The clock has not moved, so the sequence does.
985        assert_eq!(add(&mut d, b"s", Add::Auto), Id::new(1_000, 1));
986        assert_eq!(add(&mut d, b"s", Add::Seq(1_000)), Id::new(1_000, 2));
987        assert_eq!(add(&mut d, b"s", Add::Seq(2_000)), Id::new(2_000, 0));
988    }
989
990    #[test]
991    fn an_id_that_is_not_above_the_last_one_is_refused() {
992        let mut d = db();
993        add(&mut d, b"s", Add::At(Id::new(5, 0)));
994        let e = d
995            .xadd(
996                b"s",
997                Add::At(Id::new(5, 0)),
998                &[(b"f", b"v")],
999                Trim::None,
1000                true,
1001                1_000,
1002            )
1003            .expect_err("not above the last one");
1004        assert_eq!(e.message(), NOT_GREATER);
1005        // And a sequence asked for inside a millisecond that has gone by.
1006        let e = d
1007            .xadd(b"s", Add::Seq(4), &[(b"f", b"v")], Trim::None, true, 1_000)
1008            .expect_err("a millisecond that has gone by");
1009        assert_eq!(e.message(), NOT_GREATER);
1010    }
1011
1012    #[test]
1013    fn zero_is_refused_and_says_so_in_its_own_words() {
1014        let mut d = db();
1015        let e = d
1016            .xadd(
1017                b"s",
1018                Add::At(Id::MIN),
1019                &[(b"f", b"v")],
1020                Trim::None,
1021                true,
1022                1_000,
1023            )
1024            .expect_err("nothing sorts below zero");
1025        assert_eq!(e.message(), ZERO_ID);
1026    }
1027
1028    #[test]
1029    fn a_stream_is_not_deleted_when_the_last_entry_goes() {
1030        let mut d = db();
1031        let id = add(&mut d, b"s", Add::At(Id::new(5, 0)));
1032        assert_eq!(d.xdel(b"s", [id].into_iter()).expect("a stream"), 1);
1033        assert_eq!(d.xdel(b"s", [id].into_iter()).expect("a stream"), 0);
1034        assert_eq!(d.kind_of(b"s"), Some(Kind::Stream), "the key is still here");
1035        // And the ID it handed out is still remembered, so the next one is above
1036        // it rather than the same one again.
1037        assert_eq!(add(&mut d, b"s", Add::Auto), Id::new(1_000, 0));
1038    }
1039
1040    #[test]
1041    fn a_trim_runs_after_the_append() {
1042        let mut d = db();
1043        for ms in 1..=10u64 {
1044            add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1045        }
1046        let trim = Trim::MaxLen {
1047            len: 1,
1048            exact: true,
1049            limit: None,
1050        };
1051        let id = d
1052            .xadd(
1053                b"s",
1054                Add::At(Id::new(11, 0)),
1055                &[(b"f", b"v")],
1056                trim,
1057                true,
1058                1,
1059            )
1060            .expect("a stream")
1061            .expect("an ID");
1062        // The entry that was just written is the one that survives, which is the
1063        // whole reason the order matters.
1064        assert_eq!(ids(&mut d, b"s"), vec![id]);
1065    }
1066
1067    #[test]
1068    fn a_limit_stops_a_trim_at_the_next_node_boundary() {
1069        let mut d = db();
1070        for ms in 1..=1_000u64 {
1071            add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1072        }
1073        let trim = Trim::MaxLen {
1074            len: 0,
1075            exact: false,
1076            limit: Some(10),
1077        };
1078        // A node is a hundred entries and a node is what goes, so asking to stop
1079        // after ten stops after the first node rather than in the middle of it.
1080        // That is what Redis does too, and it is why `LIMIT` is only allowed
1081        // with `~`: the limit is a brake on how long the command runs and not a
1082        // count of what it is allowed to remove.
1083        assert_eq!(d.xtrim(b"s", trim).expect("a stream"), 100);
1084        assert_eq!(
1085            d.stream(b"s").expect("a stream").expect("the key").len(),
1086            900
1087        );
1088    }
1089
1090    #[test]
1091    fn setid_moves_the_bookmark_and_refuses_to_go_below_an_entry() {
1092        let mut d = db();
1093        add(&mut d, b"s", Add::At(Id::new(5, 0)));
1094        let e = d
1095            .xsetid(b"s", Id::new(4, 0), None, None)
1096            .expect_err("below an entry that is still there");
1097        assert_eq!(e.message(), SETID_TOO_SMALL);
1098        d.xsetid(b"s", Id::new(9, 0), Some(41), None)
1099            .expect("above it");
1100        let s = d.stream(b"s").expect("a stream").expect("the key");
1101        assert_eq!((s.last_id(), s.added()), (Id::new(9, 0), 41));
1102    }
1103
1104    #[test]
1105    fn setid_on_a_key_that_is_not_there_says_so() {
1106        let mut d = db();
1107        let e = d
1108            .xsetid(b"s", Id::new(1, 0), None, None)
1109            .expect_err("no key");
1110        assert_eq!((e.code(), e.message()), (Code::NotFound, NO_SUCH_KEY));
1111    }
1112
1113    #[test]
1114    fn every_command_sees_the_wrong_type() {
1115        let mut d = db();
1116        d.set_plain(b"s", b"a string").expect("a string");
1117        for e in [
1118            d.xadd(b"s", Add::Auto, &[(b"f", b"v")], Trim::None, true, 1)
1119                .expect_err("a string"),
1120            d.xdel(b"s", [Id::new(1, 0)].into_iter())
1121                .expect_err("a string"),
1122            d.xtrim(b"s", Trim::None).expect_err("a string"),
1123            d.xrange_into(b"s", Id::MIN, Id::MAX, None, false, |_, _| true)
1124                .expect_err("a string"),
1125            d.xack(b"s", b"g", [Id::new(1, 0)].into_iter())
1126                .expect_err("a string"),
1127        ] {
1128            assert_eq!(e.code(), Code::WrongType);
1129        }
1130    }
1131
1132    /// A new group has no read counter and still has a lag, because the two are
1133    /// worked out separately.
1134    ///
1135    /// Both lines are Redis 8.10.1's: `XINFO GROUPS` on a group made with no
1136    /// `ENTRIESREAD` reports the counter as null whichever position it was made
1137    /// at, and reports a lag of the whole stream at `0` and of zero at `$`.
1138    #[test]
1139    fn a_new_group_has_no_read_counter() {
1140        let mut d = db();
1141        for ms in 1..=5 {
1142            add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1143        }
1144
1145        assert!(
1146            d.xgroup_create(b"s", b"early", Start::At(Id::MIN), false, None)
1147                .expect("a stream")
1148        );
1149        assert!(
1150            d.xgroup_create(b"s", b"late", Start::Last, false, None)
1151                .expect("a stream")
1152        );
1153        let s = d.stream(b"s").expect("a stream").expect("the key");
1154        let early = s.group(b"early").expect("the early group");
1155        assert_eq!(early.entries_read(), None);
1156        assert_eq!(s.lag(early), Some(5), "everything is still in front of it");
1157        let late = s.group(b"late").expect("the late group");
1158        assert_eq!(late.entries_read(), None);
1159        assert_eq!(s.lag(late), Some(0), "and nothing is in front of this one");
1160    }
1161
1162    /// A read counter a client hands in is held down to what was ever written,
1163    /// and giving none at all on a `SETID` gives the counter up.
1164    ///
1165    /// Both are Redis 8.10.1's. The first matters because the number is
1166    /// subtracted from the entry count to work the lag out, so believing a
1167    /// client that says ninety nine would report a lag of zero on a group that
1168    /// has read nothing.
1169    #[test]
1170    fn a_read_counter_that_is_too_big_is_brought_back_down() {
1171        let mut d = db();
1172        for ms in 1..=3 {
1173            add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1174        }
1175
1176        d.xgroup_create(b"s", b"g", Start::At(Id::MIN), false, Some(99))
1177            .expect("a stream");
1178        assert_eq!(
1179            counter(&mut d, b"g"),
1180            Some(3),
1181            "held down to what was added"
1182        );
1183
1184        d.xgroup_setid(b"s", b"g", Start::At(Id::MIN), Some(2))
1185            .expect("a stream")
1186            .expect("the group");
1187        assert_eq!(counter(&mut d, b"g"), Some(2), "and left alone below that");
1188
1189        d.xgroup_setid(b"s", b"g", Start::At(Id::MIN), None)
1190            .expect("a stream")
1191            .expect("the group");
1192        assert_eq!(
1193            counter(&mut d, b"g"),
1194            None,
1195            "and given up when none is named"
1196        );
1197    }
1198
1199    /// The one group's read counter, which the test above reads three times.
1200    fn counter(d: &mut Keyspace, group: &[u8]) -> Option<u64> {
1201        d.stream(b"s")
1202            .expect("a stream")
1203            .expect("the key")
1204            .group(group)
1205            .expect("the group")
1206            .entries_read()
1207    }
1208
1209    /// `XSETID` refuses a last ID below the deletion mark it was handed.
1210    ///
1211    /// Its own sentence and not the one about the top item, because it is its
1212    /// own mistake: the pair contradicts itself whatever the stream holds, and a
1213    /// stream whose last ID sat below its own deletion mark would hand an ID out
1214    /// twice.
1215    #[test]
1216    fn setid_refuses_a_last_id_under_its_own_deletion_mark() {
1217        let mut d = db();
1218        add(&mut d, b"s", Add::At(Id::new(5, 0)));
1219
1220        let e = d
1221            .xsetid(b"s", Id::new(9, 9), None, Some(Id::new(99, 99)))
1222            .expect_err("the pair contradicts itself");
1223        assert_eq!(e.message(), SETID_BELOW_MAX_DELETED);
1224
1225        d.xsetid(b"s", Id::new(99, 99), None, Some(Id::new(9, 9)))
1226            .expect("the other way round is fine");
1227        let s = d.stream(b"s").expect("a stream").expect("the key");
1228        assert_eq!(s.last_id(), Id::new(99, 99));
1229        assert_eq!(s.max_deleted_id(), Id::new(9, 9));
1230    }
1231
1232    /// A history read makes the consumer it was sent as, and answers nothing.
1233    ///
1234    /// The case is a worker that restarts under a new name and asks for its own
1235    /// backlog before it asks for new work. There is no backlog because the name
1236    /// is new, and that is an empty list rather than a missing group, which is
1237    /// the answer the wire has to be able to tell apart from `NOGROUP`.
1238    #[test]
1239    fn a_history_read_by_a_name_nobody_has_used_makes_the_consumer() {
1240        let mut d = db();
1241        add(&mut d, b"s", Add::At(Id::new(1, 0)));
1242        d.xgroup_create(b"s", b"g", Start::At(Id::MIN), false, None)
1243            .expect("a stream");
1244
1245        let want = Read {
1246            group: b"g",
1247            consumer: b"newbie",
1248            from: From::Pending(Id::MIN),
1249            count: None,
1250            noack: false,
1251        };
1252        let seen = d
1253            .xreadgroup_into(b"s", want, 500, |_, _| true)
1254            .expect("a stream")
1255            .expect("the group is there");
1256        assert_eq!(seen, 0, "nothing was ever handed to this name");
1257
1258        let s = d.stream(b"s").expect("a stream").expect("the key");
1259        let c = s
1260            .group(b"g")
1261            .expect("the group")
1262            .consumer_named(b"newbie")
1263            .expect("the read made it");
1264        assert_eq!(c.seen(), 500, "it was heard from");
1265        assert_eq!(c.active(), None, "and it has never had anything");
1266    }
1267
1268    #[test]
1269    fn a_group_reads_what_arrives_after_it_was_made() {
1270        let mut d = db();
1271        add(&mut d, b"s", Add::At(Id::new(1, 0)));
1272        assert!(
1273            d.xgroup_create(b"s", b"workers", Start::Last, false, None)
1274                .expect("a stream")
1275        );
1276        // Making it again is not an error here, it is a false, and the wire
1277        // turns that into BUSYGROUP.
1278        assert!(
1279            !d.xgroup_create(b"s", b"workers", Start::Last, false, None)
1280                .expect("a stream")
1281        );
1282        add(&mut d, b"s", Add::At(Id::new(2, 0)));
1283
1284        let mut got = Vec::new();
1285        let seen = d
1286            .xreadgroup_into(
1287                b"s",
1288                Read {
1289                    group: b"workers",
1290                    consumer: b"alice",
1291                    from: From::New,
1292                    count: None,
1293                    noack: false,
1294                },
1295                1_000,
1296                |id, fields| {
1297                    got.push((id, fields.is_some()));
1298                    true
1299                },
1300            )
1301            .expect("a stream")
1302            .expect("a group");
1303        assert_eq!(seen, 1, "only what arrived after the group was made");
1304        assert_eq!(got, vec![(Id::new(2, 0), true)]);
1305        assert_eq!(
1306            d.xack(b"s", b"workers", [Id::new(2, 0)].into_iter())
1307                .expect("a stream"),
1308            1
1309        );
1310    }
1311
1312    #[test]
1313    fn noack_hands_the_entry_over_without_writing_it_down() {
1314        let mut d = db();
1315        add(&mut d, b"s", Add::At(Id::new(1, 0)));
1316        d.xgroup_create(b"s", b"workers", Start::At(Id::MIN), false, None)
1317            .expect("a stream");
1318        let seen = d
1319            .xreadgroup_into(
1320                b"s",
1321                Read {
1322                    group: b"workers",
1323                    consumer: b"alice",
1324                    from: From::New,
1325                    count: None,
1326                    noack: true,
1327                },
1328                1_000,
1329                |_, _| true,
1330            )
1331            .expect("a stream")
1332            .expect("a group");
1333        assert_eq!(seen, 1);
1334        let s = d.stream(b"s").expect("a stream").expect("the key");
1335        let g = s.group(b"workers").expect("the group");
1336        assert_eq!(g.pending_len(), 0, "nothing was written down");
1337        assert_eq!(g.last_id(), Id::new(1, 0), "the bookmark still moved");
1338        assert_eq!(s.lag(g), Some(0), "and the group has caught up");
1339    }
1340
1341    #[test]
1342    fn a_missing_group_is_a_none_and_not_an_error() {
1343        let mut d = db();
1344        add(&mut d, b"s", Add::At(Id::new(1, 0)));
1345        let got = d
1346            .xreadgroup_into(
1347                b"s",
1348                Read {
1349                    group: b"nope",
1350                    consumer: b"alice",
1351                    from: From::New,
1352                    count: None,
1353                    noack: false,
1354                },
1355                1,
1356                |_, _| true,
1357            )
1358            .expect("a stream");
1359        assert_eq!(got, None);
1360        assert!(!d.xgroup_destroy(b"s", b"nope").expect("a stream"));
1361        assert_eq!(
1362            d.xgroup_create_consumer(b"s", b"nope", b"alice", 1)
1363                .expect("a stream"),
1364            None
1365        );
1366    }
1367
1368    #[test]
1369    fn xgroup_needs_the_key_and_says_which_option_makes_one() {
1370        let mut d = db();
1371        let e = d
1372            .xgroup_create(b"s", b"workers", Start::Last, false, None)
1373            .expect_err("no key");
1374        assert_eq!((e.code(), e.message()), (Code::NotFound, NO_KEY_FOR_GROUP));
1375        assert!(
1376            d.xgroup_create(b"s", b"workers", Start::Last, true, None)
1377                .expect("mkstream made one")
1378        );
1379        assert_eq!(d.kind_of(b"s"), Some(Kind::Stream));
1380    }
1381
1382    #[test]
1383    fn a_claim_moves_an_idle_entry_and_drops_one_that_has_gone() {
1384        let mut d = db();
1385        for ms in 1..=2u64 {
1386            add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1387        }
1388        d.xgroup_create(b"s", b"workers", Start::At(Id::MIN), false, None)
1389            .expect("a stream");
1390        d.xreadgroup_into(
1391            b"s",
1392            Read {
1393                group: b"workers",
1394                consumer: b"alice",
1395                from: From::New,
1396                count: None,
1397                noack: false,
1398            },
1399            100,
1400            |_, _| true,
1401        )
1402        .expect("a stream")
1403        .expect("a group");
1404        d.xdel(b"s", [Id::new(1, 0)].into_iter()).expect("a stream");
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 took = d
1415            .xclaim(b"s", &[Id::new(1, 0), Id::new(2, 0)], how, 1_000, &mut gone)
1416            .expect("a stream")
1417            .expect("a group");
1418        assert_eq!(took, vec![Id::new(2, 0)]);
1419        assert_eq!(gone, vec![Id::new(1, 0)], "no one can ever finish that one");
1420    }
1421
1422    #[test]
1423    fn an_autoclaim_sweeps_from_a_cursor() {
1424        let mut d = db();
1425        for ms in 1..=10u64 {
1426            add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1427        }
1428        d.xgroup_create(b"s", b"workers", Start::At(Id::MIN), false, None)
1429            .expect("a stream");
1430        d.xreadgroup_into(
1431            b"s",
1432            Read {
1433                group: b"workers",
1434                consumer: b"alice",
1435                from: From::New,
1436                count: None,
1437                noack: false,
1438            },
1439            100,
1440            |_, _| true,
1441        )
1442        .expect("a stream")
1443        .expect("a group");
1444
1445        let mut gone = Vec::new();
1446        let how = Claim {
1447            group: b"workers",
1448            consumer: b"bob",
1449            min_idle: 500,
1450            time: 1_000,
1451            ..Claim::default()
1452        };
1453        let (cursor, took) = d
1454            .xautoclaim(b"s", Id::MIN, how, 4, 1_000, &mut gone)
1455            .expect("a stream")
1456            .expect("a group");
1457        assert_eq!(took.len(), 4);
1458        assert_eq!(cursor, Some(Id::new(5, 0)), "where the next call starts");
1459        assert!(gone.is_empty());
1460    }
1461
1462    #[test]
1463    fn a_pending_window_carries_the_owner_and_the_counts() {
1464        let mut d = db();
1465        for ms in 1..=3u64 {
1466            add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1467        }
1468        d.xgroup_create(b"s", b"workers", Start::At(Id::MIN), false, None)
1469            .expect("a stream");
1470        d.xreadgroup_into(
1471            b"s",
1472            Read {
1473                group: b"workers",
1474                consumer: b"alice",
1475                from: From::New,
1476                count: None,
1477                noack: false,
1478            },
1479            100,
1480            |_, _| true,
1481        )
1482        .expect("a stream")
1483        .expect("a group");
1484
1485        let mut out = Vec::new();
1486        let seen = d
1487            .xpending_into(b"s", b"workers", Filter::default(), 600, |id, nack, c| {
1488                out.push((
1489                    id,
1490                    nack.count(),
1491                    nack.idle(600),
1492                    c.expect("an owner").name().to_vec(),
1493                ));
1494                true
1495            })
1496            .expect("a stream")
1497            .expect("a group");
1498        assert_eq!(seen, 3);
1499        assert_eq!(out[0], (Id::new(1, 0), 1, 500, b"alice".to_vec()));
1500    }
1501
1502    #[test]
1503    fn a_history_read_hands_back_a_null_for_an_entry_that_has_gone() {
1504        let mut d = db();
1505        for ms in 1..=2u64 {
1506            add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1507        }
1508        d.xgroup_create(b"s", b"workers", Start::At(Id::MIN), false, None)
1509            .expect("a stream");
1510        d.xreadgroup_into(
1511            b"s",
1512            Read {
1513                group: b"workers",
1514                consumer: b"alice",
1515                from: From::New,
1516                count: None,
1517                noack: false,
1518            },
1519            100,
1520            |_, _| true,
1521        )
1522        .expect("a stream")
1523        .expect("a group");
1524        d.xdel(b"s", [Id::new(1, 0)].into_iter()).expect("a stream");
1525
1526        let mut out = Vec::new();
1527        d.xreadgroup_into(
1528            b"s",
1529            Read {
1530                group: b"workers",
1531                consumer: b"alice",
1532                from: From::Pending(Id::MIN),
1533                count: None,
1534                noack: false,
1535            },
1536            2_000,
1537            |id, fields| {
1538                out.push((id, fields.is_some()));
1539                true
1540            },
1541        )
1542        .expect("a stream")
1543        .expect("a group");
1544        assert_eq!(out, vec![(Id::new(1, 0), false), (Id::new(2, 0), true)]);
1545    }
1546
1547    #[test]
1548    fn a_stream_counts_against_the_memory_total() {
1549        let mut d = db();
1550        let before = d.memory_bytes();
1551        for ms in 1..=1_000u64 {
1552            add(&mut d, b"s", Add::At(Id::new(ms, 0)));
1553        }
1554        let after = d.memory_bytes();
1555        assert!(after > before + 1_000, "{before} then {after}");
1556        assert!(d.del(b"s"), "the key was there");
1557        assert_eq!(d.kind_of(b"s"), None);
1558    }
1559
1560    #[test]
1561    fn a_deadline_goes_on_a_stream_the_same_as_on_anything_else() {
1562        let mut d = db();
1563        add(&mut d, b"s", Add::At(Id::new(1, 0)));
1564        assert!(d.set_expiry(b"s", Some(1 << 45)));
1565        assert_eq!(d.kind_of(b"s"), Some(Kind::Stream));
1566        assert_eq!(ids(&mut d, b"s"), vec![Id::new(1, 0)], "the body is intact");
1567    }
1568}