1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! The cold-value stub — the 24-byte in-map remainder of a demoted
//! value, shared by both backings (the per-boot vlog and the
//! persistent row segments). Split from `value.rs` for the 500-LOC
//! house rule.
/// Type tag a [`ColdRef`] carries so `TYPE` / SCAN's `TYPE` filter / the
/// WRONGTYPE precheck answer with zero IO.
///
/// ```
/// use kevy_store::{COLD_TAG_HASH, COLD_TAG_STRING, SetCondition, Store, StoreError};
/// # let dir = std::env::temp_dir().join(format!("kevy-doc-tag-string-{}", std::process::id()));
/// assert_ne!(COLD_TAG_STRING, COLD_TAG_HASH);
/// let mut s = Store::new();
/// s.enable_tiering(&dir, 1 << 20)?;
/// s.set(b"k", vec![b'x'; 4096], None, SetCondition::Always);
/// s.set_tier_budget(1);
/// s.demote_to_watermark();
/// // the string tag answers TYPE and refuses a hash read with no disk read
/// assert_eq!(s.type_of(b"k"), "string");
/// assert_eq!(s.hget(b"k", b"f").map(|_| ()), Err(StoreError::WrongType));
/// assert_eq!(s.tier_stats().preads_total, 0);
/// # std::fs::remove_dir_all(&dir)?;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub const COLD_TAG_STRING: u8 = 1;
/// Hash tag — see [`COLD_TAG_STRING`].
///
/// ```
/// use kevy_store::{Store, StoreError};
/// # let dir = std::env::temp_dir().join(format!("kevy-doc-tag-hash-{}", std::process::id()));
/// let mut s = Store::new();
/// s.enable_tiering(&dir, 1 << 20)?;
/// s.hset(b"row", &[(b"f".as_slice(), [b'x'; 4096].as_slice())])?;
/// s.set_tier_budget(1);
/// s.demote_to_watermark();
/// assert_eq!(s.type_of(b"row"), "hash");
/// assert_eq!(s.get(b"row").map(|_| ()), Err(StoreError::WrongType));
/// assert_eq!(s.tier_stats().preads_total, 0);
/// # std::fs::remove_dir_all(&dir)?;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub const COLD_TAG_HASH: u8 = 2;
/// The in-map stub a demoted (cold) value leaves behind: its vlog
/// record's address + enough metadata to answer stage-1 questions
/// (existence, TYPE, weight) with zero IO. Byte math: offset u64 (8) +
/// file_id/len/weight u32 (12) + type_tag/touched u8 (2) = 22, padded
/// to 24 by u64 alignment — fits `Value`'s 24 B payload (≤32 B assert).
///
/// ```
/// use kevy_store::{ColdRef, SetCondition, Store, Value};
/// # let dir = std::env::temp_dir().join(format!("kevy-doc-coldref-{}", std::process::id()));
/// let mut s = Store::new();
/// s.enable_tiering(&dir, 1 << 20)?;
/// s.set(b"k", vec![b'x'; 4096], None, SetCondition::Always);
/// s.set_tier_budget(1);
/// s.demote_to_watermark();
/// let mut stub = None;
/// s.snapshot_each(|_, v, _| if let Value::Cold(c) = v { stub = Some(*c) });
/// // a vlog-backed stub, not a row-segment one
/// assert_eq!(stub.and_then(ColdRef::seg_parts), None);
/// // a row-segment stub round-trips through its snapshot record
/// assert_eq!(ColdRef::from_seg_parts(7, 512).seg_parts(), Some((7, 512)));
/// # std::fs::remove_dir_all(&dir)?;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
/// High bit of `ColdRef::file_id`: the stub's backing store is a row
/// SEGMENT (persistent, keyed by the row key) rather than the per-boot
/// vlog. Segment stubs reuse the same 24-byte shape — `offset` holds
/// the segment's stable seq, `len` is unused.
pub const SEG_BACKING: u32 = 1 << 31;