d_engine_client/lib.rs
1//! # d-engine-client
2//!
3//! Client library for interacting with d-engine Raft clusters via gRPC
4//!
5//! ## ⚠️ You Probably Don't Need This Crate
6//!
7//! **Use [`d-engine`](https://crates.io/crates/d-engine) instead:**
8//!
9//! ```toml
10//! [dependencies]
11//! d-engine = { version = "0.2", features = ["client"] }
12//! ```
13//!
14//! This provides the same API with simpler dependency management. The `d-engine-client` crate
15//! is automatically included when you enable the `client` feature.
16//!
17//! ## For Contributors
18//!
19//! This crate exists for architectural reasons:
20//! - Clean boundaries between client and server
21//! - Faster builds during development
22//! - Isolated client testing
23//!
24//! ## Quick Start
25//!
26//! ```rust,ignore
27//! use d_engine_client::Client;
28//!
29//! #[tokio::main]
30//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
31//! let client = Client::connect(vec!["http://localhost:50051"]).await?;
32//!
33//! // Write data
34//! client.put(b"key".to_vec(), b"value".to_vec()).await?;
35//!
36//! // Read data
37//! if let Some(value) = client.get(b"key".to_vec()).await? {
38//! println!("Value: {:?}", value);
39//! }
40//!
41//! Ok(())
42//! }
43//! ```
44//!
45//! ## Read Consistency
46//!
47//! Choose consistency level based on your needs:
48//!
49//! - `get_linearizable()` - Strong consistency (read from Leader)
50//! - `get_eventual()` - Fast local reads (stale OK)
51//! - `get_lease()` - Optimized with leader lease
52//!
53//! ## Features
54//!
55//! This crate provides:
56//! - [`Client`] - Main entry point with cluster access
57//! - [`ClientBuilder`] - Configurable client construction
58//! - [`KvClient`] - Key-value store operations trait
59//! - [`ClusterClient`] - Cluster management operations
60//!
61//! ## Documentation
62//!
63//! For comprehensive guides:
64//! - [Read Consistency](https://docs.rs/d-engine/latest/d_engine/docs/client_guide/read_consistency/index.html)
65//! - [Error Handling](https://docs.rs/d-engine/latest/d_engine/docs/client_guide/error_handling/index.html)
66
67mod builder;
68mod cluster;
69mod config;
70mod error;
71mod grpc_kv_client;
72pub mod kv_client;
73pub mod kv_error;
74mod pool;
75mod proto;
76mod scoped_timer;
77mod utils;
78
79pub use builder::*;
80pub use cluster::*;
81pub use config::*;
82pub use error::*;
83pub use grpc_kv_client::*;
84pub use kv_client::*;
85pub use kv_error::*;
86pub use pool::*;
87pub use utils::*;
88
89// ==================== Protocol Types (Essential for Public API) ====================
90
91/// Protocol types needed for client operations
92///
93/// These types are used in the public API and must be imported for client usage:
94/// - `ClientResult`: Response type from read operations
95/// - `ReadConsistencyPolicy`: Consistency guarantees for reads
96/// - `WriteCommand`: Write operation specifications
97pub mod protocol {
98 pub use d_engine_proto::client::ClientResult;
99 pub use d_engine_proto::client::ReadConsistencyPolicy;
100 pub use d_engine_proto::client::WatchEventType;
101 pub use d_engine_proto::client::WatchRequest;
102 pub use d_engine_proto::client::WatchResponse;
103 pub use d_engine_proto::client::WriteCommand;
104}
105
106/// Cluster management protocol types
107///
108/// Types required for cluster administration operations:
109/// - `NodeMeta`: Cluster node metadata
110/// - `NodeStatus`: Node status enumeration
111pub mod cluster_types {
112 pub use d_engine_proto::common::NodeStatus;
113 pub use d_engine_proto::server::cluster::NodeMeta;
114}
115
116// ==================== Hide Implementation Details ====================
117pub(crate) use proto::*;
118
119#[cfg(test)]
120mod cluster_test;
121#[cfg(test)]
122mod error_test;
123#[cfg(test)]
124mod kv_test;
125#[cfg(test)]
126mod mock_rpc;
127#[cfg(test)]
128mod mock_rpc_service;
129#[cfg(test)]
130mod pool_test;
131#[cfg(test)]
132mod utils_test;
133
134/// Main entry point for interacting with the d_engine cluster
135///
136/// Manages connections and provides access to specialized clients:
137/// - Use [`kv()`](Client::kv) for data operations
138/// - Use [`cluster()`](Client::cluster) for cluster administration
139///
140/// Created through the [`builder()`](Client::builder) method
141#[derive(Clone)]
142pub struct Client {
143 /// Key-value store client interface
144 kv: GrpcKvClient,
145
146 /// Cluster management client interface
147 cluster: ClusterClient,
148
149 inner: std::sync::Arc<arc_swap::ArcSwap<ClientInner>>,
150}
151
152#[derive(Clone)]
153pub struct ClientInner {
154 pool: ConnectionPool,
155 client_id: u32,
156 config: ClientConfig,
157 endpoints: Vec<String>,
158}
159
160impl Client {
161 /// Access the key-value operations client
162 ///
163 /// # Examples
164 /// ```rust,ignore
165 /// client.kv().put("key", "value").await?;
166 /// ```
167 pub fn kv(&self) -> &GrpcKvClient {
168 &self.kv
169 }
170
171 /// Access the cluster management client
172 ///
173 /// # Examples
174 /// ```rust,ignore
175 /// client.cluster().add_node("node3:9083").await?;
176 /// ```
177 pub fn cluster(&self) -> &ClusterClient {
178 &self.cluster
179 }
180
181 /// Create a configured client builder
182 ///
183 /// Starts client construction process with specified bootstrap endpoints.
184 /// Chain configuration methods before calling
185 /// [`build()`](ClientBuilder::build).
186 ///
187 /// # Arguments
188 /// * `endpoints` - Initial cluster nodes for discovery
189 ///
190 /// # Panics
191 /// Will panic if no valid endpoints provided
192 pub fn builder(endpoints: Vec<String>) -> ClientBuilder {
193 assert!(!endpoints.is_empty(), "At least one endpoint required");
194 ClientBuilder::new(endpoints)
195 }
196
197 pub async fn refresh(
198 &mut self,
199 new_endpoints: Option<Vec<String>>,
200 ) -> std::result::Result<(), ClientApiError> {
201 // Get a writable lock
202 let old_inner = self.inner.load();
203 let config = old_inner.config.clone();
204 let endpoints = new_endpoints.unwrap_or(old_inner.endpoints.clone());
205
206 let new_pool = ConnectionPool::create(endpoints.clone(), config.clone()).await?;
207
208 let new_inner = std::sync::Arc::new(ClientInner {
209 pool: new_pool,
210 client_id: old_inner.client_id,
211 config,
212 endpoints,
213 });
214
215 self.inner.store(new_inner);
216 Ok(())
217 }
218}