reinhardt_query/types/zone.rs
1//! Zone configuration types for CockroachDB
2//!
3//! This module provides types for zone configuration, which controls replica placement
4//! and constraints in CockroachDB.
5//!
6//! # Overview
7//!
8//! Zone configurations in CockroachDB determine how data is distributed across your
9//! cluster. They control:
10//!
11//! - **Replication Factor**: How many copies of data to maintain
12//! - **Placement Constraints**: Where replicas should be located
13//! - **Lease Preferences**: Which replicas should serve reads
14//!
15//! # Use Cases
16//!
17//! ## High Availability
18//!
19//! Increase the number of replicas to survive more simultaneous failures:
20//!
21//! ```rust
22//! use reinhardt_query::types::ZoneConfig;
23//!
24//! let zone = ZoneConfig::new()
25//! .num_replicas(5); // Survives 2 simultaneous node failures
26//! ```
27//!
28//! ## Data Locality
29//!
30//! Place data in specific regions for compliance or performance:
31//!
32//! ```rust
33//! use reinhardt_query::types::ZoneConfig;
34//!
35//! let zone = ZoneConfig::new()
36//! .add_constraint("+region=us-east-1")
37//! .add_constraint("+region=us-west-1");
38//! ```
39//!
40//! ## Read Performance
41//!
42//! Direct reads to specific zones for lower latency:
43//!
44//! ```rust
45//! use reinhardt_query::types::ZoneConfig;
46//!
47//! let zone = ZoneConfig::new()
48//! .add_lease_preference("+region=us-east-1"); // Prioritize us-east-1 for reads
49//! ```
50//!
51//! ## Complete Multi-Region Setup
52//!
53//! ```rust
54//! use reinhardt_query::types::ZoneConfig;
55//!
56//! let zone = ZoneConfig::new()
57//! .num_replicas(7) // 3 regions × 2 replicas + 1 for fault tolerance
58//! .add_constraint("+region=us-east-1")
59//! .add_constraint("+region=us-west-1")
60//! .add_constraint("+region=eu-west-1")
61//! .add_lease_preference("+region=us-east-1"); // Primary region for reads
62//! ```
63
64/// Zone configuration for CockroachDB
65///
66/// This struct represents zone configuration options for databases, tables, or indexes.
67/// Zone configurations control how CockroachDB distributes and replicates data across
68/// your cluster.
69///
70/// # Fields
71///
72/// - `num_replicas`: Number of copies of data to maintain (default: 3)
73/// - `constraints`: Placement rules for replicas (required/prohibited locations)
74/// - `lease_preferences`: Preferences for which replicas serve reads
75///
76/// # Constraint Format
77///
78/// Constraints use a `+` (required) or `-` (prohibited) prefix:
79///
80/// - `+region=us-east-1`: Replicas MUST be in us-east-1
81/// - `-region=us-west-1`: Replicas MUST NOT be in us-west-1
82/// - `+zone=a`: Replicas MUST be in zone 'a'
83///
84/// # Lease Preferences Format
85///
86/// Lease preferences determine which replica serves reads. They use the same
87/// format as constraints but represent priorities rather than requirements.
88///
89/// # Examples
90///
91/// ## Basic Configuration
92///
93/// ```rust
94/// use reinhardt_query::types::ZoneConfig;
95///
96/// let zone = ZoneConfig::new()
97/// .num_replicas(3)
98/// .add_constraint("+region=us-east-1")
99/// .add_lease_preference("+region=us-east-1");
100/// ```
101///
102/// ## Multi-Region High Availability
103///
104/// ```rust
105/// use reinhardt_query::types::ZoneConfig;
106///
107/// // 5 replicas across 3 regions
108/// let zone = ZoneConfig::new()
109/// .num_replicas(5)
110/// .add_constraint("+region=us-east-1")
111/// .add_constraint("+region=us-west-1")
112/// .add_constraint("+region=eu-west-1")
113/// .add_lease_preference("+region=us-east-1"); // Prefer us-east-1 for reads
114/// ```
115///
116/// ## Exclude Specific Zones
117///
118/// ```rust
119/// use reinhardt_query::types::ZoneConfig;
120///
121/// // Avoid certain zones for compliance
122/// let zone = ZoneConfig::new()
123/// .num_replicas(3)
124/// .add_constraint("+region=us-east-1")
125/// .add_constraint("-zone=deprecated"); // Exclude deprecated zones
126/// ```
127///
128/// ## Tiered Read Preferences
129///
130/// ```rust
131/// use reinhardt_query::types::ZoneConfig;
132///
133/// // Primary: us-east-1, Secondary: us-west-1
134/// let zone = ZoneConfig::new()
135/// .num_replicas(3)
136/// .add_lease_preference("+region=us-east-1")
137/// .add_lease_preference("+region=us-west-1"); // Fallback if us-east-1 unavailable
138/// ```
139#[derive(Debug, Clone, Default)]
140pub struct ZoneConfig {
141 pub(crate) num_replicas: Option<i32>,
142 pub(crate) constraints: Vec<String>,
143 pub(crate) lease_preferences: Vec<String>,
144}
145
146impl ZoneConfig {
147 /// Create a new zone configuration
148 pub fn new() -> Self {
149 Self {
150 num_replicas: None,
151 constraints: Vec::new(),
152 lease_preferences: Vec::new(),
153 }
154 }
155
156 /// Set the number of replicas
157 ///
158 /// # Examples
159 ///
160 /// ```rust
161 /// use reinhardt_query::types::ZoneConfig;
162 ///
163 /// let zone = ZoneConfig::new().num_replicas(3);
164 /// ```
165 pub fn num_replicas(mut self, replicas: i32) -> Self {
166 self.num_replicas = Some(replicas);
167 self
168 }
169
170 /// Add a constraint
171 ///
172 /// # Examples
173 ///
174 /// ```rust
175 /// use reinhardt_query::types::ZoneConfig;
176 ///
177 /// let zone = ZoneConfig::new()
178 /// .add_constraint("+region=us-east-1")
179 /// .add_constraint("+zone=a");
180 /// ```
181 pub fn add_constraint<S: Into<String>>(mut self, constraint: S) -> Self {
182 self.constraints.push(constraint.into());
183 self
184 }
185
186 /// Add a lease preference
187 ///
188 /// # Examples
189 ///
190 /// ```rust
191 /// use reinhardt_query::types::ZoneConfig;
192 ///
193 /// let zone = ZoneConfig::new()
194 /// .add_lease_preference("+region=us-east-1");
195 /// ```
196 pub fn add_lease_preference<S: Into<String>>(mut self, preference: S) -> Self {
197 self.lease_preferences.push(preference.into());
198 self
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205 use rstest::*;
206
207 #[rstest]
208 fn test_zone_config_new() {
209 let zone = ZoneConfig::new();
210 assert!(zone.num_replicas.is_none());
211 assert!(zone.constraints.is_empty());
212 assert!(zone.lease_preferences.is_empty());
213 }
214
215 #[rstest]
216 fn test_zone_config_num_replicas() {
217 let zone = ZoneConfig::new().num_replicas(3);
218 assert_eq!(zone.num_replicas, Some(3));
219 }
220
221 #[rstest]
222 fn test_zone_config_add_constraint() {
223 let zone = ZoneConfig::new()
224 .add_constraint("+region=us-east-1")
225 .add_constraint("+zone=a");
226 assert_eq!(zone.constraints.len(), 2);
227 assert_eq!(zone.constraints[0], "+region=us-east-1");
228 assert_eq!(zone.constraints[1], "+zone=a");
229 }
230
231 #[rstest]
232 fn test_zone_config_add_lease_preference() {
233 let zone = ZoneConfig::new()
234 .add_lease_preference("+region=us-east-1")
235 .add_lease_preference("+zone=a");
236 assert_eq!(zone.lease_preferences.len(), 2);
237 assert_eq!(zone.lease_preferences[0], "+region=us-east-1");
238 assert_eq!(zone.lease_preferences[1], "+zone=a");
239 }
240
241 #[rstest]
242 fn test_zone_config_all_options() {
243 let zone = ZoneConfig::new()
244 .num_replicas(5)
245 .add_constraint("+region=us-east-1")
246 .add_constraint("-region=us-west-1")
247 .add_lease_preference("+region=us-east-1");
248 assert_eq!(zone.num_replicas, Some(5));
249 assert_eq!(zone.constraints.len(), 2);
250 assert_eq!(zone.lease_preferences.len(), 1);
251 }
252}