d_engine_client/kv_client.rs
1//! KV client trait - unified interface for key-value operations.
2//!
3//! Provides a common abstraction for both remote (gRPC) and embedded (local) access
4//! to d-engine's key-value store.
5//!
6//! # Implementations
7//!
8//! - `GrpcKvClient` (d-engine-client): Remote access via gRPC protocol
9//! - `LocalKvClient` (d-engine-server): Zero-overhead embedded access
10//!
11//! # Design Principles
12//!
13//! - **Unified Interface**: Same API for remote and embedded modes
14//! - **Async-first**: All operations are async for non-blocking I/O
15//! - **Type Safety**: Strong typing with clear error handling
16//! - **Performance**: Zero-cost abstractions, no runtime overhead
17//!
18//! # Example
19//!
20//! ```rust,ignore
21//! async fn store_config<C: KvClient>(client: &C) -> Result<()> {
22//! client.put(b"config:timeout", b"30s").await?;
23//! let value = client.get(b"config:timeout").await?;
24//! Ok(())
25//! }
26//! ```
27
28use bytes::Bytes;
29
30use crate::kv_error::KvResult;
31
32/// Unified key-value store interface.
33///
34/// This trait abstracts over different client implementations, allowing applications
35/// to write generic code that works with both remote (gRPC) and embedded (local) access.
36///
37/// # Consistency Guarantees
38///
39/// - **put()**: Strong consistency, linearizable writes
40/// - **get()**: Linearizable reads by default
41/// - **delete()**: Strong consistency, linearizable deletes
42///
43/// # Thread Safety
44///
45/// All implementations must be `Send + Sync`, safe for concurrent access.
46///
47/// # Performance Characteristics
48///
49/// - `GrpcKvClient`: 1-2ms latency (network + serialization)
50/// - `LocalKvClient`: <0.1ms latency (direct function call)
51#[async_trait::async_trait]
52pub trait KvClient: Send + Sync {
53 /// Stores a key-value pair with strong consistency.
54 ///
55 /// The write is replicated to a quorum of nodes before returning,
56 /// ensuring durability and linearizability.
57 ///
58 /// # Arguments
59 ///
60 /// * `key` - The key to store (arbitrary bytes)
61 /// * `value` - The value to store (arbitrary bytes)
62 ///
63 /// # Errors
64 ///
65 /// - [`crate::kv_error::KvClientError::ChannelClosed`] if node is shutting down
66 /// - [`crate::kv_error::KvClientError::Timeout`] if operation exceeds timeout
67 /// - [`crate::kv_error::KvClientError::ServerError`] for server-side errors (e.g., not leader)
68 ///
69 /// # Example
70 ///
71 /// ```rust,ignore
72 /// client.put(b"user:1001", b"Alice").await?;
73 /// ```
74 async fn put(
75 &self,
76 key: impl AsRef<[u8]> + Send,
77 value: impl AsRef<[u8]> + Send,
78 ) -> KvResult<()>;
79
80 /// Stores a key-value pair with time-to-live (TTL).
81 ///
82 /// The key will automatically expire after `ttl_secs` seconds.
83 ///
84 /// # Arguments
85 ///
86 /// * `key` - The key to store
87 /// * `value` - The value to store
88 /// * `ttl_secs` - Time-to-live in seconds
89 ///
90 /// # Errors
91 ///
92 /// Same as [`put()`](Self::put)
93 ///
94 /// # Example
95 ///
96 /// ```rust,ignore
97 /// // Session expires after 1 hour
98 /// client.put_with_ttl(b"session:abc", b"user_data", 3600).await?;
99 /// ```
100 async fn put_with_ttl(
101 &self,
102 key: impl AsRef<[u8]> + Send,
103 value: impl AsRef<[u8]> + Send,
104 ttl_secs: u64,
105 ) -> KvResult<()>;
106
107 /// Retrieves the value associated with a key.
108 ///
109 /// Uses linearizable reads by default, ensuring the returned value
110 /// reflects all previously committed writes.
111 ///
112 /// # Arguments
113 ///
114 /// * `key` - The key to retrieve
115 ///
116 /// # Returns
117 ///
118 /// * `Ok(Some(value))` if key exists
119 /// * `Ok(None)` if key does not exist or has expired
120 /// * `Err(_)` if operation failed
121 ///
122 /// # Errors
123 ///
124 /// - [`crate::kv_error::KvClientError::ChannelClosed`] if node is shutting down
125 /// - [`crate::kv_error::KvClientError::Timeout`] if operation exceeds timeout
126 /// - [`crate::kv_error::KvClientError::ServerError`] for server-side errors
127 ///
128 /// # Example
129 ///
130 /// ```rust,ignore
131 /// match client.get(b"user:1001").await? {
132 /// Some(value) => println!("User: {:?}", value),
133 /// None => println!("User not found"),
134 /// }
135 /// ```
136 async fn get(
137 &self,
138 key: impl AsRef<[u8]> + Send,
139 ) -> KvResult<Option<Bytes>>;
140
141 /// Retrieves multiple keys in a single request.
142 ///
143 /// More efficient than multiple individual `get()` calls when fetching
144 /// multiple keys, as it batches the requests.
145 ///
146 /// # Arguments
147 ///
148 /// * `keys` - Slice of keys to retrieve
149 ///
150 /// # Returns
151 ///
152 /// Vector of results in the same order as input keys.
153 /// `None` for keys that don't exist.
154 ///
155 /// # Errors
156 ///
157 /// Same as [`get()`](Self::get)
158 ///
159 /// # Example
160 ///
161 /// ```rust,ignore
162 /// let keys = vec![
163 /// Bytes::from("user:1001"),
164 /// Bytes::from("user:1002"),
165 /// ];
166 /// let results = client.get_multi(&keys).await?;
167 /// ```
168 async fn get_multi(
169 &self,
170 keys: &[Bytes],
171 ) -> KvResult<Vec<Option<Bytes>>>;
172
173 /// Deletes a key-value pair with strong consistency.
174 ///
175 /// The deletion is replicated to a quorum before returning.
176 /// Returns successfully even if the key does not exist (idempotent).
177 ///
178 /// # Arguments
179 ///
180 /// * `key` - The key to delete
181 ///
182 /// # Errors
183 ///
184 /// - [`crate::kv_error::KvClientError::ChannelClosed`] if node is shutting down
185 /// - [`crate::kv_error::KvClientError::Timeout`] if operation exceeds timeout
186 /// - [`crate::kv_error::KvClientError::ServerError`] for server-side errors
187 ///
188 /// # Example
189 ///
190 /// ```rust,ignore
191 /// client.delete(b"temp:session_123").await?;
192 /// ```
193 async fn delete(
194 &self,
195 key: impl AsRef<[u8]> + Send,
196 ) -> KvResult<()>;
197}