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}