Skip to main content

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}