Skip to main content

yo/
keys.rs

1//! The keyspace itself: what is there, what type it is, and when it goes away.
2//!
3//! [`Strings`](crate::Strings) and [`Sets`](crate::Sets) each hold the commands
4//! for one type. The commands here hold for all of them, because a deadline is
5//! not a string thing or a set thing. `EXPIRE` puts the moment in the key's own
6//! record, so the same call works on a key holding a string, a set or a hash and
7//! costs the same on each.
8//!
9//! ```
10//! use std::time::Duration;
11//!
12//! let db = yo::open(yo::MEMORY)?;
13//! let keys = db.keys();
14//!
15//! db.set("online").add("alice")?;
16//! keys.expire_in("online", Duration::from_secs(60))?;
17//!
18//! assert_eq!(keys.kind("online")?, Some(yo::Kind::Set));
19//! assert!(keys.ttl("online")?.left().is_some());
20//! # Ok::<(), yo::Error>(())
21//! ```
22//!
23//! # What a deadline is not
24//!
25//! It is not a property of the value. Giving a set a deadline rewrites five
26//! bytes of the key's record and does not touch a single member, which is why
27//! [`Keys::expire_in`] on a set of a million members is the same call as on a
28//! set of one.
29//!
30//! It is also not the same as the per field deadlines a hash can carry. Those
31//! are `HEXPIRE` and they live in the hash. A key can have a deadline while its
32//! fields have their own, and neither one knows about the other.
33//!
34//! # The clock is only read when it matters
35//!
36//! A database that has never been asked for a deadline never reads the clock on
37//! the data path, which [`Db::reads_the_clock`](crate::Db::reads_the_clock)
38//! reports. The first call here that creates one turns that on for good, so it
39//! is worth knowing that this is where the tens of nanoseconds come from.
40//!
41//! # There is no touch
42//!
43//! Redis has a `TOUCH`, and on a real server it counts the keys that are there
44//! and moves each of them up the eviction order. There is no eviction here, so
45//! all it could do is count, and [`Keys::count`] already does that. A second
46//! name for one call is worse than no second name, so the wire has `TOUCH` for
47//! the clients that send it and this does not.
48//!
49//! # There is no cursor
50//!
51//! The wire has `SCAN` because a server cannot stop and walk a keyspace for one
52//! client while every other client waits, so it hands out a number and does the
53//! walk in pieces. Nothing here is in that position. [`Keys::each`] holds the
54//! database for as long as it runs and nothing else can write to it in the
55//! meantime, so it is one walk, it sees one version of the keyspace, and there
56//! is no cursor to hold and no duplicate to filter out.
57//!
58//! What that costs is that a walk of ten million keys is ten million calls
59//! before the next line of your program runs. That is the same trade `KEYS`
60//! makes and it is the right one here, because the thing on the other side of
61//! the call is your own code rather than a socket.
62//!
63//! # The typed collections are somewhere else
64//!
65//! A [`Map`](crate::Map) is a named collection and not a key in the keyspace, so
66//! it does not show up here and cannot be given a deadline. That is `15`
67//! section 3's split and not an oversight: a map's name is checked when it is
68//! opened, and a key's name is whatever you pass.
69
70use std::time::{Duration, SystemTime, UNIX_EPOCH};
71
72use yo_common::{Code, Error, Result};
73use yo_kv::{Applied, Ask, Cond, Kind, MAX_AT, Moved};
74
75use crate::db::Handle;
76
77/// What a key says about when it goes away.
78///
79/// Three answers rather than two, because a key that is not there and a key
80/// that is never going away are different things and code that confuses them
81/// deletes the wrong data. Redis says this with `-2` and `-1` and hopes you
82/// read the manual.
83#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84pub enum Ttl {
85    /// There is no such key.
86    Missing,
87    /// The key is there and nothing is going to take it away.
88    Forever,
89    /// The key is there and this much of it is left.
90    In(Duration),
91}
92
93impl Ttl {
94    /// How long is left, or `None` for a key that is missing or has no
95    /// deadline.
96    ///
97    /// The short answer for code that only wants to know whether it should
98    /// refresh something. When the difference matters, match on the variants.
99    #[must_use]
100    pub fn left(self) -> Option<Duration> {
101        match self {
102            Ttl::In(left) => Some(left),
103            _ => None,
104        }
105    }
106
107    /// Whether the key is there at all.
108    #[must_use]
109    pub fn found(self) -> bool {
110        !matches!(self, Ttl::Missing)
111    }
112}
113
114/// Whether a deadline is allowed to move, which is `EXPIRE`'s `NX`, `XX`, `GT`
115/// and `LT`.
116///
117/// The condition is checked before the moment is, so a deadline that has
118/// already gone and a condition that says no leaves the key alone rather than
119/// deleting it.
120#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
121pub enum When {
122    /// Whatever is there now. Plain `EXPIRE`.
123    #[default]
124    Always,
125    /// Only if the key has no deadline yet. `NX`.
126    Unset,
127    /// Only if it already has one. `XX`.
128    AlreadySet,
129    /// Only if this pushes the deadline further out. `GT`.
130    ///
131    /// A key with no deadline is refused, because no deadline reads as
132    /// infinitely far away and nothing is further out than that.
133    Later,
134    /// Only if this brings the deadline in. `LT`.
135    ///
136    /// A key with no deadline is accepted, by the same reading.
137    Earlier,
138    /// Only if there is one now and this brings it in. `XX LT`.
139    ///
140    /// The one combination the other five cannot say. `XX GT` is just `GT`,
141    /// since `GT` already refuses a key with no deadline.
142    EarlierAndAlreadySet,
143}
144
145impl From<When> for Cond {
146    fn from(when: When) -> Cond {
147        match when {
148            When::Always => Cond::Always,
149            When::Unset => Cond::NotSet,
150            When::AlreadySet => Cond::AlreadySet,
151            When::Later => Cond::Greater,
152            When::Earlier => Cond::Less,
153            When::EarlierAndAlreadySet => Cond::LessAndSet,
154        }
155    }
156}
157
158/// Every command that works on a key whatever the key holds.
159///
160/// `DEL`, `EXISTS` and `TYPE`, plus the whole expiry family. Keys are byte
161/// strings the way Redis's are, so anything that is bytes will do.
162///
163/// ```
164/// let db = yo::open(yo::MEMORY)?;
165/// let keys = db.keys();
166///
167/// db.strings().set("greeting", "hello")?;
168/// assert!(keys.exists("greeting")?);
169/// assert!(keys.del("greeting")?);
170/// assert!(!keys.exists("greeting")?);
171/// # Ok::<(), yo::Error>(())
172/// ```
173#[derive(Clone)]
174pub struct Keys {
175    pub(crate) db: Handle,
176}
177
178impl core::fmt::Debug for Keys {
179    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
180        f.debug_struct("Keys").finish_non_exhaustive()
181    }
182}
183
184impl Keys {
185    /// Whether a key is there. `EXISTS`.
186    ///
187    /// A key whose deadline has gone is not there, whether or not anything has
188    /// got around to removing it yet.
189    ///
190    /// # Errors
191    ///
192    /// [`Code::Invalid`] if called from inside a callback that is already
193    /// holding this database.
194    pub fn exists(&self, key: impl AsRef<[u8]>) -> Result<bool> {
195        self.db.run(|inner| Ok(inner.strings.exists(key.as_ref())))
196    }
197
198    /// How many of these keys are there. `EXISTS` with several.
199    ///
200    /// The same key twice counts twice, which is Redis's rule and is worth
201    /// knowing before you use this to count distinct things.
202    ///
203    /// # Errors
204    ///
205    /// As [`Keys::exists`].
206    pub fn count<K: AsRef<[u8]>>(&self, keys: &[K]) -> Result<usize> {
207        self.db.run(|inner| {
208            Ok(keys
209                .iter()
210                .filter(|key| inner.strings.exists(key.as_ref()))
211                .count())
212        })
213    }
214
215    /// What a key holds, or `None` if it holds nothing. `TYPE`.
216    ///
217    /// # Errors
218    ///
219    /// As [`Keys::exists`].
220    pub fn kind(&self, key: impl AsRef<[u8]>) -> Result<Option<Kind>> {
221        self.db.run(|inner| Ok(inner.strings.kind_of(key.as_ref())))
222    }
223
224    /// Remove a key, and say whether it was there. `DEL`.
225    ///
226    /// # Errors
227    ///
228    /// As [`Keys::exists`].
229    pub fn del(&self, key: impl AsRef<[u8]>) -> Result<bool> {
230        self.db.run(|inner| Ok(inner.strings.del(key.as_ref())))
231    }
232
233    /// Remove several keys, and say how many were there. `DEL` with a list.
234    ///
235    /// # Errors
236    ///
237    /// As [`Keys::exists`].
238    pub fn del_many<K: AsRef<[u8]>>(&self, keys: &[K]) -> Result<usize> {
239        self.db.run(|inner| {
240            Ok(keys
241                .iter()
242                .filter(|key| inner.strings.del(key.as_ref()))
243                .count())
244        })
245    }
246
247    /// Give a key this long to live, and say whether the deadline was set.
248    /// `PEXPIRE`.
249    ///
250    /// A duration that has already gone, meaning zero, removes the key and
251    /// answers true, because the deadline was applied and applying it is what
252    /// took the key away. False means the key is not there.
253    ///
254    /// # Errors
255    ///
256    /// [`Code::Invalid`] for a duration that lands past what a millisecond
257    /// timestamp reaches, which is the year 4199, or if called from inside a
258    /// callback that is already holding this database.
259    pub fn expire_in(&self, key: impl AsRef<[u8]>, after: Duration) -> Result<bool> {
260        self.expire_in_when(key, after, When::Always)
261    }
262
263    /// The same with a condition on it. `PEXPIRE` with `NX`, `XX`, `GT` or
264    /// `LT`.
265    ///
266    /// False now means either that the key is not there or that the condition
267    /// said no, which is the one place Redis's reply is genuinely ambiguous.
268    /// Ask [`Keys::ttl`] first if you need to tell them apart.
269    ///
270    /// # Errors
271    ///
272    /// As [`Keys::expire_in`].
273    pub fn expire_in_when(
274        &self,
275        key: impl AsRef<[u8]>,
276        after: Duration,
277        when: When,
278    ) -> Result<bool> {
279        let ms = u64::try_from(after.as_millis()).map_err(|_| too_far())?;
280        self.db.deadlines(|inner| {
281            let at = inner
282                .strings
283                .clock()
284                .now_ms()
285                .checked_add(ms)
286                .ok_or_else(too_far)?;
287            apply(
288                inner
289                    .strings
290                    .expire(key.as_ref(), reachable(at)?, when.into()),
291            )
292        })
293    }
294
295    /// Set the moment a key goes away, and say whether it was set.
296    /// `PEXPIREAT`.
297    ///
298    /// A moment that has already gone removes the key, the same as
299    /// [`Keys::expire_in`] with nothing left on it.
300    ///
301    /// # Errors
302    ///
303    /// As [`Keys::expire_in`].
304    pub fn expire_at(&self, key: impl AsRef<[u8]>, at: SystemTime) -> Result<bool> {
305        self.expire_at_when(key, at, When::Always)
306    }
307
308    /// The same with a condition on it. `PEXPIREAT` with `NX`, `XX`, `GT` or
309    /// `LT`.
310    ///
311    /// # Errors
312    ///
313    /// As [`Keys::expire_in`].
314    pub fn expire_at_when(
315        &self,
316        key: impl AsRef<[u8]>,
317        at: SystemTime,
318        when: When,
319    ) -> Result<bool> {
320        let ms = moment(at)?;
321        self.db
322            .deadlines(|inner| apply(inner.strings.expire(key.as_ref(), ms, when.into())))
323    }
324
325    /// How long a key has left. `PTTL`.
326    ///
327    /// # Errors
328    ///
329    /// As [`Keys::exists`].
330    pub fn ttl(&self, key: impl AsRef<[u8]>) -> Result<Ttl> {
331        self.db.run(|inner| {
332            let now = inner.strings.clock().now_ms();
333            Ok(match inner.strings.deadline_of(key.as_ref()) {
334                Ask::Missing => Ttl::Missing,
335                Ask::NoDeadline => Ttl::Forever,
336                Ask::At(at) => Ttl::In(Duration::from_millis(at.saturating_sub(now))),
337            })
338        })
339    }
340
341    /// The moment a key goes away, or `None` if it is missing or has no
342    /// deadline. `PEXPIRETIME`.
343    ///
344    /// [`Keys::ttl`] is the one that tells those two apart. This one is for
345    /// when the answer needs to survive being written down, since a moment
346    /// stays true and a duration goes stale as soon as it is read.
347    ///
348    /// # Errors
349    ///
350    /// As [`Keys::exists`].
351    pub fn deadline(&self, key: impl AsRef<[u8]>) -> Result<Option<SystemTime>> {
352        self.db.run(|inner| {
353            Ok(match inner.strings.deadline_of(key.as_ref()) {
354                Ask::At(at) => Some(UNIX_EPOCH + Duration::from_millis(at)),
355                Ask::Missing | Ask::NoDeadline => None,
356            })
357        })
358    }
359
360    /// Take a key's deadline away and let it live, and say whether there was
361    /// one. `PERSIST`.
362    ///
363    /// # Errors
364    ///
365    /// As [`Keys::exists`].
366    pub fn persist(&self, key: impl AsRef<[u8]>) -> Result<bool> {
367        self.db.run(|inner| Ok(inner.strings.persist(key.as_ref())))
368    }
369
370    /// Move a key to another name, over whatever was there. `RENAME`.
371    ///
372    /// The value does not move and is not copied. A set or a hash is a slot
373    /// number sitting in a record, and the same slot number under a different
374    /// key is the same set, so this writes a new record and deletes the old one
375    /// however large the value is. Renaming a set of a million members writes
376    /// thirteen bytes.
377    ///
378    /// The deadline travels with the source, and whatever the destination had
379    /// goes away with the value it belonged to. A key renamed onto itself is
380    /// [`Moved::Ok`] and keeps its deadline.
381    ///
382    /// [`Moved::Taken`] cannot happen here, which is what
383    /// [`Keys::rename_if_new`] is for.
384    ///
385    /// # Errors
386    ///
387    /// As [`Keys::exists`].
388    pub fn rename(&self, src: impl AsRef<[u8]>, dst: impl AsRef<[u8]>) -> Result<Moved> {
389        self.db
390            .run(|inner| Ok(inner.strings.rename(src.as_ref(), dst.as_ref(), false)))
391    }
392
393    /// Move a key to another name, but only if that name is free. `RENAMENX`.
394    ///
395    /// A key renamed onto itself is [`Moved::Taken`], because the destination
396    /// does exist and a key is not new because it is the one you already had.
397    /// That is the one place this and [`Keys::rename`] disagree about a call
398    /// neither of them has to do any work for.
399    ///
400    /// # Errors
401    ///
402    /// As [`Keys::exists`].
403    pub fn rename_if_new(&self, src: impl AsRef<[u8]>, dst: impl AsRef<[u8]>) -> Result<Moved> {
404        self.db
405            .run(|inner| Ok(inner.strings.rename(src.as_ref(), dst.as_ref(), true)))
406    }
407
408    /// Copy a value to another key, leaving the destination alone if it is
409    /// already there. `COPY`.
410    ///
411    /// This is the one call here that costs what the value is worth. Two keys
412    /// cannot share a body, because then adding a member to one would show up in
413    /// the other, so the body is cloned. [`Keys::rename`] is the call that moves
414    /// a large value for nothing, and it is the one to reach for when the old
415    /// name is not wanted afterwards.
416    ///
417    /// The deadline is copied too, so a copy of a key with ten seconds left has
418    /// ten seconds left. A destination whose deadline has already gone counts as
419    /// free.
420    ///
421    /// # Errors
422    ///
423    /// As [`Keys::exists`].
424    pub fn copy(&self, src: impl AsRef<[u8]>, dst: impl AsRef<[u8]>) -> Result<Moved> {
425        self.db
426            .run(|inner| Ok(inner.strings.copy(src.as_ref(), dst.as_ref(), false)))
427    }
428
429    /// Copy a value to another key, over whatever was there. `COPY REPLACE`.
430    ///
431    /// [`Moved::Taken`] cannot happen here, the same way it cannot happen for
432    /// [`Keys::rename`].
433    ///
434    /// # Errors
435    ///
436    /// As [`Keys::exists`].
437    pub fn copy_over(&self, src: impl AsRef<[u8]>, dst: impl AsRef<[u8]>) -> Result<Moved> {
438        self.db
439            .run(|inner| Ok(inner.strings.copy(src.as_ref(), dst.as_ref(), true)))
440    }
441
442    /// Every key in the database, one call each. `KEYS *` without the reply.
443    ///
444    /// The key is handed over where it lies, so a walk of a million keys
445    /// allocates nothing at all. It is only borrowed for the length of the
446    /// call, which is what stops it from outliving the record it points into,
447    /// so anything you want to keep has to be copied out inside the closure.
448    ///
449    /// A key whose deadline has passed is not handed over, and it is deleted
450    /// once the walk has finished, so a walk is also the cheapest way to clear
451    /// out a database that has had a lot of things expire in it.
452    ///
453    /// ```
454    /// let db = yo::open(yo::MEMORY)?;
455    /// db.strings().set("a", "1")?;
456    /// db.strings().set("b", "2")?;
457    ///
458    /// let mut n = 0;
459    /// db.keys().each(|_| n += 1)?;
460    /// assert_eq!(n, 2);
461    /// # Ok::<(), yo::Error>(())
462    /// ```
463    ///
464    /// # Errors
465    ///
466    /// As [`Keys::exists`], which includes calling any method on this database
467    /// from inside the closure.
468    pub fn each(&self, mut f: impl FnMut(&[u8])) -> Result<()> {
469        self.db.run(|inner| {
470            inner.strings.keys(&mut f);
471            Ok(())
472        })
473    }
474
475    /// Every key, copied out into a vector. `KEYS *`.
476    ///
477    /// The convenient one, and the one that costs a key's worth of memory per
478    /// key. [`Keys::each`] is the same walk without that.
479    ///
480    /// # Errors
481    ///
482    /// As [`Keys::exists`].
483    pub fn all(&self) -> Result<Vec<Vec<u8>>> {
484        let mut out = Vec::new();
485        self.each(|key| out.push(key.to_vec()))?;
486        Ok(out)
487    }
488
489    /// Every key matching a glob pattern. `KEYS pattern`.
490    ///
491    /// The same `*`, `?`, `[abc]` and `\` that Redis matches with, so a pattern
492    /// that works against a Redis client works here.
493    ///
494    /// ```
495    /// let db = yo::open(yo::MEMORY)?;
496    /// db.strings().set("user:1", "alice")?;
497    /// db.strings().set("user:2", "bob")?;
498    /// db.strings().set("session:1", "x")?;
499    ///
500    /// assert_eq!(db.keys().matching("user:*")?.len(), 2);
501    /// # Ok::<(), yo::Error>(())
502    /// ```
503    ///
504    /// # Errors
505    ///
506    /// As [`Keys::exists`].
507    pub fn matching(&self, pattern: impl AsRef<[u8]>) -> Result<Vec<Vec<u8>>> {
508        let pattern = pattern.as_ref();
509        let mut out = Vec::new();
510        self.each(|key| {
511            if yo_common::glob_matches(pattern, key) {
512                out.push(key.to_vec());
513            }
514        })?;
515        Ok(out)
516    }
517
518    /// One key, chosen at random, or `None` if the database is empty.
519    /// `RANDOMKEY`.
520    ///
521    /// A constant number of loads whatever the database holds, because it picks
522    /// a place in the index and takes a key from there rather than walking to
523    /// find one.
524    ///
525    /// # Errors
526    ///
527    /// As [`Keys::exists`].
528    pub fn random(&self) -> Result<Option<Vec<u8>>> {
529        self.db.run(|inner| Ok(inner.strings.random_key()))
530    }
531}
532
533/// Both ways of applying a deadline answer the same question, so they say so in
534/// the same place.
535fn apply(done: Applied) -> Result<bool> {
536    Ok(match done {
537        Applied::Ok | Applied::Deleted => true,
538        Applied::Missing | Applied::NotMet => false,
539    })
540}
541
542/// A wall clock moment as milliseconds since the epoch.
543///
544/// Anything before the epoch is zero, which is a moment that has already gone
545/// and therefore removes the key. That is the same answer `PEXPIREAT key 0`
546/// gets and there is nothing else it could sensibly mean.
547fn moment(at: SystemTime) -> Result<u64> {
548    let ms = at
549        .duration_since(UNIX_EPOCH)
550        .map_or(0, |since| since.as_millis());
551    reachable(u64::try_from(ms).map_err(|_| too_far())?)
552}
553
554/// The wire clamps a deadline past the year 4199 because a real server accepts
555/// the number, which is D-17. Nothing is being answered for here, so this says
556/// no instead.
557fn reachable(at: u64) -> Result<u64> {
558    if at > MAX_AT {
559        return Err(too_far());
560    }
561    Ok(at)
562}
563
564fn too_far() -> Error {
565    Error::new(
566        Code::Invalid,
567        "that deadline is further away than a millisecond timestamp reaches, which is the year 4199",
568    )
569}
570
571#[cfg(test)]
572mod tests {
573    use super::*;
574    use crate::{MEMORY, open};
575
576    #[test]
577    fn a_key_is_there_until_it_is_not() {
578        let db = open(MEMORY).unwrap();
579        let keys = db.keys();
580
581        assert!(!keys.exists("k").unwrap());
582        assert_eq!(keys.kind("k").unwrap(), None);
583        assert!(!keys.del("k").unwrap());
584
585        db.strings().set("k", "v").unwrap();
586        assert!(keys.exists("k").unwrap());
587        assert_eq!(keys.kind("k").unwrap(), Some(Kind::String));
588        assert!(keys.del("k").unwrap());
589        assert!(!keys.exists("k").unwrap());
590    }
591
592    #[test]
593    fn a_rename_carries_the_deadline_and_a_copy_is_a_second_value() {
594        let db = open(MEMORY).unwrap();
595        let keys = db.keys();
596        db.strings().set("a", "v1").unwrap();
597        keys.expire_in("a", Duration::from_secs(100)).unwrap();
598        db.strings().set("b", "v2").unwrap();
599
600        assert_eq!(keys.rename("a", "b").unwrap(), Moved::Ok);
601        assert_eq!(db.strings().get("b").unwrap().as_deref(), Some(&b"v1"[..]));
602        assert!(keys.ttl("b").unwrap().left().is_some(), "a's and not b's");
603        assert!(!keys.exists("a").unwrap());
604
605        db.set("s").add("m1").unwrap();
606        assert_eq!(keys.copy("s", "t").unwrap(), Moved::Ok);
607        db.set("t").add("m2").unwrap();
608        assert_eq!(db.set("s").len().unwrap(), 1, "the original is intact");
609        assert_eq!(db.set("t").len().unwrap(), 2);
610    }
611
612    #[test]
613    fn the_three_answers_a_move_can_give_are_three_and_not_two() {
614        let db = open(MEMORY).unwrap();
615        let keys = db.keys();
616        db.strings().set("a", "v1").unwrap();
617        db.strings().set("b", "v2").unwrap();
618
619        // Missing and Taken both mean nothing happened, and a caller that has
620        // to tell them apart should not need a second call to find out which.
621        assert_eq!(keys.rename_if_new("nosuch", "z").unwrap(), Moved::Missing);
622        assert_eq!(keys.rename_if_new("a", "b").unwrap(), Moved::Taken);
623        assert_eq!(keys.copy("a", "b").unwrap(), Moved::Taken);
624        assert_eq!(db.strings().get("b").unwrap().as_deref(), Some(&b"v2"[..]));
625
626        assert_eq!(keys.copy_over("a", "b").unwrap(), Moved::Ok);
627        assert_eq!(db.strings().get("b").unwrap().as_deref(), Some(&b"v1"[..]));
628        // Onto itself is the one call the two renames disagree about.
629        assert_eq!(keys.rename("a", "a").unwrap(), Moved::Ok);
630        assert_eq!(keys.rename_if_new("a", "a").unwrap(), Moved::Taken);
631    }
632
633    #[test]
634    fn several_keys_at_once_count_the_way_redis_counts_them() {
635        let db = open(MEMORY).unwrap();
636        let keys = db.keys();
637        db.strings().set("a", "1").unwrap();
638        db.strings().set("b", "2").unwrap();
639
640        assert_eq!(keys.count(&["a", "b", "missing"]).unwrap(), 2);
641        assert_eq!(keys.count(&["a", "a"]).unwrap(), 2, "the same key twice");
642        assert_eq!(keys.del_many(&["a", "b", "missing"]).unwrap(), 2);
643        assert_eq!(keys.count(&["a", "b"]).unwrap(), 0);
644    }
645
646    #[test]
647    fn a_deadline_lands_on_a_key_whatever_the_key_holds() {
648        let db = open(MEMORY).unwrap();
649        let keys = db.keys();
650        db.strings().set("s", "v").unwrap();
651        db.set("t").add("member").unwrap();
652
653        for key in ["s", "t"] {
654            assert!(keys.expire_in(key, Duration::from_secs(600)).unwrap());
655            let left = keys.ttl(key).unwrap().left().expect("a deadline");
656            assert!(left <= Duration::from_secs(600) && left > Duration::from_secs(590));
657        }
658
659        assert_eq!(keys.kind("t").unwrap(), Some(Kind::Set), "still a set");
660        assert_eq!(db.set("t").len().unwrap(), 1, "with its member");
661    }
662
663    #[test]
664    fn the_three_answers_are_three_and_not_two() {
665        let db = open(MEMORY).unwrap();
666        let keys = db.keys();
667
668        assert_eq!(keys.ttl("nothing").unwrap(), Ttl::Missing);
669        assert!(!keys.ttl("nothing").unwrap().found());
670
671        db.strings().set("k", "v").unwrap();
672        assert_eq!(keys.ttl("k").unwrap(), Ttl::Forever);
673        assert!(keys.ttl("k").unwrap().found());
674        assert_eq!(keys.ttl("k").unwrap().left(), None, "forever has none left");
675
676        keys.expire_in("k", Duration::from_secs(60)).unwrap();
677        assert!(matches!(keys.ttl("k").unwrap(), Ttl::In(_)));
678    }
679
680    #[test]
681    fn a_moment_that_has_gone_removes_the_key_now() {
682        let db = open(MEMORY).unwrap();
683        let keys = db.keys();
684        db.strings().set("k", "v").unwrap();
685
686        assert!(keys.expire_at("k", UNIX_EPOCH).unwrap(), "it was applied");
687        assert!(!keys.exists("k").unwrap(), "and applying it took the key");
688    }
689
690    #[test]
691    fn a_deadline_comes_back_as_the_moment_it_was_set_to() {
692        let db = open(MEMORY).unwrap();
693        let keys = db.keys();
694        db.strings().set("k", "v").unwrap();
695        assert_eq!(keys.deadline("k").unwrap(), None, "no deadline yet");
696
697        let at = UNIX_EPOCH + Duration::from_millis(4_000_000_000_000);
698        assert!(keys.expire_at("k", at).unwrap());
699        assert_eq!(keys.deadline("k").unwrap(), Some(at));
700
701        assert!(keys.persist("k").unwrap());
702        assert_eq!(keys.deadline("k").unwrap(), None);
703        assert!(
704            !keys.persist("k").unwrap(),
705            "there was nothing left to take"
706        );
707        assert!(keys.exists("k").unwrap(), "and the key is still here");
708    }
709
710    #[test]
711    fn a_condition_decides_whether_the_deadline_moves() {
712        let db = open(MEMORY).unwrap();
713        let keys = db.keys();
714        db.strings().set("k", "v").unwrap();
715
716        let hour = Duration::from_secs(3600);
717        let day = Duration::from_secs(86400);
718
719        assert!(keys.expire_in_when("k", hour, When::Unset).unwrap());
720        assert!(
721            !keys.expire_in_when("k", day, When::Unset).unwrap(),
722            "taken"
723        );
724        assert!(keys.expire_in_when("k", day, When::AlreadySet).unwrap());
725        assert!(!keys.expire_in_when("k", hour, When::Later).unwrap(), "in");
726        assert!(keys.expire_in_when("k", hour, When::Earlier).unwrap());
727        assert!(keys.expire_in_when("k", day, When::Later).unwrap());
728
729        keys.persist("k").unwrap();
730        assert!(
731            !keys.expire_in_when("k", hour, When::Later).unwrap(),
732            "no deadline is infinitely far out, so nothing is further"
733        );
734        assert!(
735            keys.expire_in_when("k", hour, When::Earlier).unwrap(),
736            "and by the same reading everything is nearer"
737        );
738        keys.persist("k").unwrap();
739        assert!(
740            !keys
741                .expire_in_when("k", hour, When::EarlierAndAlreadySet)
742                .unwrap(),
743            "unless XX takes that reading away"
744        );
745    }
746
747    #[test]
748    fn a_condition_that_says_no_leaves_a_key_that_would_have_gone() {
749        let db = open(MEMORY).unwrap();
750        let keys = db.keys();
751        db.strings().set("k", "v").unwrap();
752        keys.expire_in("k", Duration::from_secs(60)).unwrap();
753
754        assert!(
755            !keys.expire_at_when("k", UNIX_EPOCH, When::Unset).unwrap(),
756            "the condition is checked before the moment is"
757        );
758        assert!(keys.exists("k").unwrap());
759    }
760
761    #[test]
762    fn a_deadline_past_the_year_4199_is_refused_rather_than_clamped() {
763        let db = open(MEMORY).unwrap();
764        let keys = db.keys();
765        db.strings().set("k", "v").unwrap();
766
767        let err = keys
768            .expire_in("k", Duration::from_secs(u64::MAX))
769            .unwrap_err();
770        assert_eq!(err.code(), Code::Invalid);
771        let far = UNIX_EPOCH + Duration::from_millis(MAX_AT + 1);
772        assert_eq!(keys.expire_at("k", far).unwrap_err().code(), Code::Invalid);
773        assert_eq!(keys.ttl("k").unwrap(), Ttl::Forever, "and nothing moved");
774    }
775
776    #[test]
777    fn nothing_reads_the_clock_until_a_deadline_exists_to_read_it_for() {
778        let db = open(MEMORY).unwrap();
779        let keys = db.keys();
780        db.strings().set("k", "v").unwrap();
781
782        keys.exists("k").unwrap();
783        keys.kind("k").unwrap();
784        keys.ttl("k").unwrap();
785        assert!(!db.reads_the_clock(), "asking is not creating");
786
787        keys.expire_in("k", Duration::from_secs(60)).unwrap();
788        assert!(db.reads_the_clock());
789    }
790
791    #[test]
792    fn a_walk_sees_every_key_whatever_it_holds() {
793        let db = open(MEMORY).unwrap();
794        let keys = db.keys();
795        assert!(keys.all().unwrap().is_empty());
796        assert_eq!(keys.random().unwrap(), None);
797
798        db.strings().set("a", "v").unwrap();
799        db.set("s").add("m").unwrap();
800        for i in 0..1_000 {
801            db.strings().set(format!("n:{i}"), "v").unwrap();
802        }
803
804        let mut all = keys.all().unwrap();
805        all.sort();
806        assert_eq!(all.len(), 1_002);
807        assert_eq!(all[0], b"a");
808        assert_eq!(keys.matching("n:*").unwrap().len(), 1_000);
809        assert_eq!(keys.matching("s").unwrap(), vec![b"s".to_vec()]);
810        assert!(keys.matching("nothing").unwrap().is_empty());
811
812        // A random key is one of the keys, and not the same one every time.
813        let mut picked = std::collections::HashSet::new();
814        for _ in 0..100 {
815            picked.insert(keys.random().unwrap().expect("the database is not empty"));
816        }
817        assert!(
818            picked.len() > 5,
819            "randomkey is stuck on {} keys",
820            picked.len()
821        );
822        assert!(picked.iter().all(|k| all.contains(k)));
823    }
824
825    #[test]
826    fn a_walk_does_not_hand_out_a_key_that_has_expired() {
827        let db = open(MEMORY).unwrap();
828        let keys = db.keys();
829        db.strings().set("alive", "v").unwrap();
830        db.strings().set("dead", "v").unwrap();
831        keys.expire_at("dead", UNIX_EPOCH + Duration::from_secs(1))
832            .unwrap();
833
834        assert_eq!(keys.all().unwrap(), vec![b"alive".to_vec()]);
835        assert_eq!(keys.random().unwrap(), Some(b"alive".to_vec()));
836    }
837
838    /// The closure holds the database, so a call back into it from inside the
839    /// walk is refused rather than deadlocked or, worse, allowed.
840    #[test]
841    fn a_walk_cannot_be_reentered() {
842        let db = open(MEMORY).unwrap();
843        let keys = db.keys();
844        db.strings().set("k", "v").unwrap();
845
846        let mut inner = Ok(true);
847        keys.each(|_| inner = keys.exists("k")).unwrap();
848        assert_eq!(inner.unwrap_err().code(), Code::Invalid);
849    }
850}