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        // The engine hands back a borrow of its scratch buffer and this is an
530        // owning API, so the copy happens here rather than in there.
531        self.db
532            .run(|inner| Ok(inner.strings.random_key().map(<[u8]>::to_vec)))
533    }
534}
535
536/// Both ways of applying a deadline answer the same question, so they say so in
537/// the same place.
538fn apply(done: Applied) -> Result<bool> {
539    Ok(match done {
540        Applied::Ok | Applied::Deleted => true,
541        Applied::Missing | Applied::NotMet => false,
542    })
543}
544
545/// A wall clock moment as milliseconds since the epoch.
546///
547/// Anything before the epoch is zero, which is a moment that has already gone
548/// and therefore removes the key. That is the same answer `PEXPIREAT key 0`
549/// gets and there is nothing else it could sensibly mean.
550fn moment(at: SystemTime) -> Result<u64> {
551    let ms = at
552        .duration_since(UNIX_EPOCH)
553        .map_or(0, |since| since.as_millis());
554    reachable(u64::try_from(ms).map_err(|_| too_far())?)
555}
556
557/// The wire clamps a deadline past the year 4199 because a real server accepts
558/// the number, which is D-17. Nothing is being answered for here, so this says
559/// no instead.
560fn reachable(at: u64) -> Result<u64> {
561    if at > MAX_AT {
562        return Err(too_far());
563    }
564    Ok(at)
565}
566
567fn too_far() -> Error {
568    Error::new(
569        Code::Invalid,
570        "that deadline is further away than a millisecond timestamp reaches, which is the year 4199",
571    )
572}
573
574#[cfg(test)]
575mod tests {
576    use super::*;
577    use crate::{MEMORY, open};
578
579    #[test]
580    fn a_key_is_there_until_it_is_not() {
581        let db = open(MEMORY).unwrap();
582        let keys = db.keys();
583
584        assert!(!keys.exists("k").unwrap());
585        assert_eq!(keys.kind("k").unwrap(), None);
586        assert!(!keys.del("k").unwrap());
587
588        db.strings().set("k", "v").unwrap();
589        assert!(keys.exists("k").unwrap());
590        assert_eq!(keys.kind("k").unwrap(), Some(Kind::String));
591        assert!(keys.del("k").unwrap());
592        assert!(!keys.exists("k").unwrap());
593    }
594
595    #[test]
596    fn a_rename_carries_the_deadline_and_a_copy_is_a_second_value() {
597        let db = open(MEMORY).unwrap();
598        let keys = db.keys();
599        db.strings().set("a", "v1").unwrap();
600        keys.expire_in("a", Duration::from_secs(100)).unwrap();
601        db.strings().set("b", "v2").unwrap();
602
603        assert_eq!(keys.rename("a", "b").unwrap(), Moved::Ok);
604        assert_eq!(db.strings().get("b").unwrap().as_deref(), Some(&b"v1"[..]));
605        assert!(keys.ttl("b").unwrap().left().is_some(), "a's and not b's");
606        assert!(!keys.exists("a").unwrap());
607
608        db.set("s").add("m1").unwrap();
609        assert_eq!(keys.copy("s", "t").unwrap(), Moved::Ok);
610        db.set("t").add("m2").unwrap();
611        assert_eq!(db.set("s").len().unwrap(), 1, "the original is intact");
612        assert_eq!(db.set("t").len().unwrap(), 2);
613    }
614
615    #[test]
616    fn the_three_answers_a_move_can_give_are_three_and_not_two() {
617        let db = open(MEMORY).unwrap();
618        let keys = db.keys();
619        db.strings().set("a", "v1").unwrap();
620        db.strings().set("b", "v2").unwrap();
621
622        // Missing and Taken both mean nothing happened, and a caller that has
623        // to tell them apart should not need a second call to find out which.
624        assert_eq!(keys.rename_if_new("nosuch", "z").unwrap(), Moved::Missing);
625        assert_eq!(keys.rename_if_new("a", "b").unwrap(), Moved::Taken);
626        assert_eq!(keys.copy("a", "b").unwrap(), Moved::Taken);
627        assert_eq!(db.strings().get("b").unwrap().as_deref(), Some(&b"v2"[..]));
628
629        assert_eq!(keys.copy_over("a", "b").unwrap(), Moved::Ok);
630        assert_eq!(db.strings().get("b").unwrap().as_deref(), Some(&b"v1"[..]));
631        // Onto itself is the one call the two renames disagree about.
632        assert_eq!(keys.rename("a", "a").unwrap(), Moved::Ok);
633        assert_eq!(keys.rename_if_new("a", "a").unwrap(), Moved::Taken);
634    }
635
636    #[test]
637    fn several_keys_at_once_count_the_way_redis_counts_them() {
638        let db = open(MEMORY).unwrap();
639        let keys = db.keys();
640        db.strings().set("a", "1").unwrap();
641        db.strings().set("b", "2").unwrap();
642
643        assert_eq!(keys.count(&["a", "b", "missing"]).unwrap(), 2);
644        assert_eq!(keys.count(&["a", "a"]).unwrap(), 2, "the same key twice");
645        assert_eq!(keys.del_many(&["a", "b", "missing"]).unwrap(), 2);
646        assert_eq!(keys.count(&["a", "b"]).unwrap(), 0);
647    }
648
649    #[test]
650    fn a_deadline_lands_on_a_key_whatever_the_key_holds() {
651        let db = open(MEMORY).unwrap();
652        let keys = db.keys();
653        db.strings().set("s", "v").unwrap();
654        db.set("t").add("member").unwrap();
655
656        for key in ["s", "t"] {
657            assert!(keys.expire_in(key, Duration::from_secs(600)).unwrap());
658            let left = keys.ttl(key).unwrap().left().expect("a deadline");
659            assert!(left <= Duration::from_secs(600) && left > Duration::from_secs(590));
660        }
661
662        assert_eq!(keys.kind("t").unwrap(), Some(Kind::Set), "still a set");
663        assert_eq!(db.set("t").len().unwrap(), 1, "with its member");
664    }
665
666    #[test]
667    fn the_three_answers_are_three_and_not_two() {
668        let db = open(MEMORY).unwrap();
669        let keys = db.keys();
670
671        assert_eq!(keys.ttl("nothing").unwrap(), Ttl::Missing);
672        assert!(!keys.ttl("nothing").unwrap().found());
673
674        db.strings().set("k", "v").unwrap();
675        assert_eq!(keys.ttl("k").unwrap(), Ttl::Forever);
676        assert!(keys.ttl("k").unwrap().found());
677        assert_eq!(keys.ttl("k").unwrap().left(), None, "forever has none left");
678
679        keys.expire_in("k", Duration::from_secs(60)).unwrap();
680        assert!(matches!(keys.ttl("k").unwrap(), Ttl::In(_)));
681    }
682
683    #[test]
684    fn a_moment_that_has_gone_removes_the_key_now() {
685        let db = open(MEMORY).unwrap();
686        let keys = db.keys();
687        db.strings().set("k", "v").unwrap();
688
689        assert!(keys.expire_at("k", UNIX_EPOCH).unwrap(), "it was applied");
690        assert!(!keys.exists("k").unwrap(), "and applying it took the key");
691    }
692
693    #[test]
694    fn a_deadline_comes_back_as_the_moment_it_was_set_to() {
695        let db = open(MEMORY).unwrap();
696        let keys = db.keys();
697        db.strings().set("k", "v").unwrap();
698        assert_eq!(keys.deadline("k").unwrap(), None, "no deadline yet");
699
700        let at = UNIX_EPOCH + Duration::from_millis(4_000_000_000_000);
701        assert!(keys.expire_at("k", at).unwrap());
702        assert_eq!(keys.deadline("k").unwrap(), Some(at));
703
704        assert!(keys.persist("k").unwrap());
705        assert_eq!(keys.deadline("k").unwrap(), None);
706        assert!(
707            !keys.persist("k").unwrap(),
708            "there was nothing left to take"
709        );
710        assert!(keys.exists("k").unwrap(), "and the key is still here");
711    }
712
713    #[test]
714    fn a_condition_decides_whether_the_deadline_moves() {
715        let db = open(MEMORY).unwrap();
716        let keys = db.keys();
717        db.strings().set("k", "v").unwrap();
718
719        let hour = Duration::from_secs(3600);
720        let day = Duration::from_secs(86400);
721
722        assert!(keys.expire_in_when("k", hour, When::Unset).unwrap());
723        assert!(
724            !keys.expire_in_when("k", day, When::Unset).unwrap(),
725            "taken"
726        );
727        assert!(keys.expire_in_when("k", day, When::AlreadySet).unwrap());
728        assert!(!keys.expire_in_when("k", hour, When::Later).unwrap(), "in");
729        assert!(keys.expire_in_when("k", hour, When::Earlier).unwrap());
730        assert!(keys.expire_in_when("k", day, When::Later).unwrap());
731
732        keys.persist("k").unwrap();
733        assert!(
734            !keys.expire_in_when("k", hour, When::Later).unwrap(),
735            "no deadline is infinitely far out, so nothing is further"
736        );
737        assert!(
738            keys.expire_in_when("k", hour, When::Earlier).unwrap(),
739            "and by the same reading everything is nearer"
740        );
741        keys.persist("k").unwrap();
742        assert!(
743            !keys
744                .expire_in_when("k", hour, When::EarlierAndAlreadySet)
745                .unwrap(),
746            "unless XX takes that reading away"
747        );
748    }
749
750    #[test]
751    fn a_condition_that_says_no_leaves_a_key_that_would_have_gone() {
752        let db = open(MEMORY).unwrap();
753        let keys = db.keys();
754        db.strings().set("k", "v").unwrap();
755        keys.expire_in("k", Duration::from_secs(60)).unwrap();
756
757        assert!(
758            !keys.expire_at_when("k", UNIX_EPOCH, When::Unset).unwrap(),
759            "the condition is checked before the moment is"
760        );
761        assert!(keys.exists("k").unwrap());
762    }
763
764    #[test]
765    fn a_deadline_past_the_year_4199_is_refused_rather_than_clamped() {
766        let db = open(MEMORY).unwrap();
767        let keys = db.keys();
768        db.strings().set("k", "v").unwrap();
769
770        let err = keys
771            .expire_in("k", Duration::from_secs(u64::MAX))
772            .unwrap_err();
773        assert_eq!(err.code(), Code::Invalid);
774        let far = UNIX_EPOCH + Duration::from_millis(MAX_AT + 1);
775        assert_eq!(keys.expire_at("k", far).unwrap_err().code(), Code::Invalid);
776        assert_eq!(keys.ttl("k").unwrap(), Ttl::Forever, "and nothing moved");
777    }
778
779    #[test]
780    fn nothing_reads_the_clock_until_a_deadline_exists_to_read_it_for() {
781        let db = open(MEMORY).unwrap();
782        let keys = db.keys();
783        db.strings().set("k", "v").unwrap();
784
785        keys.exists("k").unwrap();
786        keys.kind("k").unwrap();
787        keys.ttl("k").unwrap();
788        assert!(!db.reads_the_clock(), "asking is not creating");
789
790        keys.expire_in("k", Duration::from_secs(60)).unwrap();
791        assert!(db.reads_the_clock());
792    }
793
794    #[test]
795    fn a_walk_sees_every_key_whatever_it_holds() {
796        let db = open(MEMORY).unwrap();
797        let keys = db.keys();
798        assert!(keys.all().unwrap().is_empty());
799        assert_eq!(keys.random().unwrap(), None);
800
801        db.strings().set("a", "v").unwrap();
802        db.set("s").add("m").unwrap();
803        for i in 0..1_000 {
804            db.strings().set(format!("n:{i}"), "v").unwrap();
805        }
806
807        let mut all = keys.all().unwrap();
808        all.sort();
809        assert_eq!(all.len(), 1_002);
810        assert_eq!(all[0], b"a");
811        assert_eq!(keys.matching("n:*").unwrap().len(), 1_000);
812        assert_eq!(keys.matching("s").unwrap(), vec![b"s".to_vec()]);
813        assert!(keys.matching("nothing").unwrap().is_empty());
814
815        // A random key is one of the keys, and not the same one every time.
816        let mut picked = std::collections::HashSet::new();
817        for _ in 0..100 {
818            picked.insert(keys.random().unwrap().expect("the database is not empty"));
819        }
820        assert!(
821            picked.len() > 5,
822            "randomkey is stuck on {} keys",
823            picked.len()
824        );
825        assert!(picked.iter().all(|k| all.contains(k)));
826    }
827
828    #[test]
829    fn a_walk_does_not_hand_out_a_key_that_has_expired() {
830        let db = open(MEMORY).unwrap();
831        let keys = db.keys();
832        db.strings().set("alive", "v").unwrap();
833        db.strings().set("dead", "v").unwrap();
834        keys.expire_at("dead", UNIX_EPOCH + Duration::from_secs(1))
835            .unwrap();
836
837        assert_eq!(keys.all().unwrap(), vec![b"alive".to_vec()]);
838        assert_eq!(keys.random().unwrap(), Some(b"alive".to_vec()));
839    }
840
841    /// The closure holds the database, so a call back into it from inside the
842    /// walk is refused rather than deadlocked or, worse, allowed.
843    #[test]
844    fn a_walk_cannot_be_reentered() {
845        let db = open(MEMORY).unwrap();
846        let keys = db.keys();
847        db.strings().set("k", "v").unwrap();
848
849        let mut inner = Ok(true);
850        keys.each(|_| inner = keys.exists("k")).unwrap();
851        assert_eq!(inner.unwrap_err().code(), Code::Invalid);
852    }
853}