d_engine_core/storage/
lease.rs

1//! Lease (Time-based key expiration) trait for d-engine.
2//!
3//! Provides the interface for automatic key expiration through lease-based lifecycle management.
4//! This is a framework-level abstraction that allows custom lease implementations.
5//!
6//! # Design Philosophy
7//!
8//! d-engine provides lease-based key expiration management as a framework-level
9//! feature that all state machines (including custom implementations) can leverage.
10//!
11//! # Architecture
12//!
13//! - **Trait-based**: `Lease` trait defines the interface
14//! - **Implementation**: d-engine-server provides `DefaultLease` with high-performance dual-index
15//!   design
16//! - **Zero overhead**: Completely disabled when not used
17//! - **Snapshot support**: Full persistence through Raft snapshots
18//!
19//! # Concurrency Model
20//!
21//! Implementations should follow these guidelines:
22//! - **Read path (hot)**: Lock-free, supports high concurrency
23//! - **Write path (cold)**: Single-threaded (CommitHandler), Mutex acceptable
24//! - **Read-write**: Concurrent safe, reads don't block on cleanup
25
26use std::time::SystemTime;
27
28use bytes::Bytes;
29
30use crate::Result;
31
32/// Lease management interface for key expiration.
33///
34/// Manages key lifecycles through time-based leases. d-engine provides a default
35/// implementation (`DefaultLease` in d-engine-server), but developers can implement
36/// custom lease management strategies.
37///
38/// # Thread Safety
39///
40/// All methods must be thread-safe as they will be called concurrently from:
41/// - Read path: Multiple concurrent client reads
42/// - Write path: Single-threaded apply operations
43///
44/// # Performance Requirements
45///
46/// - `is_expired()`: Must be O(1) and lock-free (hot path, called on every read)
47/// - `register()` / `unregister()`: Should be O(log N) or better
48/// - `get_expired_keys()`: Should be O(K log N) where K = expired keys
49///
50/// # Example Implementation
51///
52/// ```ignore
53/// use d_engine_core::storage::Lease;
54/// use bytes::Bytes;
55/// use std::time::SystemTime;
56///
57/// struct MyCustomLease {
58///     // Your data structures
59/// }
60///
61/// impl Lease for MyCustomLease {
62///     fn register(&self, key: Bytes, ttl_secs: u64) {
63///         // Your implementation
64///     }
65///
66///     fn is_expired(&self, key: &[u8]) -> bool {
67///         // Your implementation
68///     }
69///
70///     // ... other methods
71/// }
72/// ```
73pub trait Lease: Send + Sync + 'static {
74    /// Register a lease for a key.
75    ///
76    /// If the key already has a lease, updates to the new expiration time.
77    ///
78    /// # Arguments
79    /// * `key` - Key to set expiration for
80    /// * `ttl_secs` - Time-to-live in seconds from now
81    ///
82    /// # Performance
83    /// Should be O(log N) or better. Called on every insert with TTL.
84    fn register(
85        &self,
86        key: Bytes,
87        ttl_secs: u64,
88    );
89
90    /// Remove lease for a key (on update/delete).
91    ///
92    /// Called when a key is updated without TTL or explicitly deleted.
93    ///
94    /// # Performance
95    /// Should be O(log N) or better.
96    fn unregister(
97        &self,
98        key: &[u8],
99    );
100
101    /// Check if a key's lease has expired.
102    ///
103    /// # Returns
104    /// * `true` - Key has expired and should be treated as non-existent
105    /// * `false` - Key has not expired or has no lease
106    ///
107    /// # Performance
108    /// **CRITICAL**: Must be O(1) and lock-free. This is the hot path,
109    /// called on every read operation.
110    fn is_expired(
111        &self,
112        key: &[u8],
113    ) -> bool;
114
115    /// Get all keys with expired leases.
116    ///
117    /// Removes returned keys from internal lease indexes.
118    ///
119    /// # Arguments
120    /// * `now` - Current time to check expiration against
121    ///
122    /// # Returns
123    /// List of expired keys (keys are removed from lease tracking)
124    ///
125    /// # Performance
126    /// Should be O(K log N) where K = number of expired keys.
127    fn get_expired_keys(
128        &self,
129        now: SystemTime,
130    ) -> Vec<Bytes>;
131
132    /// Called on every apply operation (piggyback cleanup).
133    ///
134    /// The lease implementation decides internally whether to perform cleanup
135    /// based on its configuration (e.g., piggyback strategy with frequency control).
136    ///
137    /// # Returns
138    /// List of expired keys that were cleaned up (may be empty)
139    ///
140    /// # Performance
141    /// Should be O(1) most of the time (fast path when not cleaning).
142    fn on_apply(&self) -> Vec<Bytes>;
143
144    /// Check if any key has ever been registered with a lease.
145    ///
146    /// Used for fast-path optimization to skip lease logic entirely when
147    /// leases are not used.
148    ///
149    /// # Returns
150    /// * `true` - At least one key has been registered (even if expired)
151    /// * `false` - No keys have ever been registered
152    ///
153    /// # Performance
154    /// Must be O(1) - simple flag check.
155    fn has_lease_keys(&self) -> bool;
156
157    /// Fast check: returns true if there might be expired keys.
158    ///
159    /// This is an O(1) optimization to avoid full expired key scan.
160    ///
161    /// # Returns
162    /// * `false` - Definitely no expired keys (fast path)
163    /// * `true` - Maybe has expired keys (need full check)
164    ///
165    /// # Performance
166    /// Should be O(1) or O(log N) at most.
167    fn may_have_expired_keys(
168        &self,
169        now: SystemTime,
170    ) -> bool;
171
172    /// Get number of keys with active leases.
173    ///
174    /// # Performance
175    /// Should be O(1).
176    fn len(&self) -> usize;
177
178    /// Check if no keys have active leases.
179    fn is_empty(&self) -> bool {
180        self.len() == 0
181    }
182
183    /// Serialize lease state for snapshot.
184    ///
185    /// Used by Raft snapshot mechanism to persist lease metadata.
186    ///
187    /// # Returns
188    /// Serialized bytes (format is implementation-defined)
189    fn to_snapshot(&self) -> Vec<u8>;
190
191    /// Reload lease state from snapshot data.
192    ///
193    /// Used during snapshot restoration. Should filter out already-expired leases.
194    ///
195    /// # Arguments
196    /// * `data` - Serialized snapshot data
197    ///
198    /// # Errors
199    /// Returns error if deserialization fails.
200    fn reload(
201        &self,
202        data: &[u8],
203    ) -> Result<()>;
204}