@@ -7,10 +7,9 @@ use std::{
77 time:: { Duration , SystemTime , UNIX_EPOCH } ,
88} ;
99
10+ use thiserror:: Error ;
1011use uuid:: Uuid ;
1112
12- use super :: aio_protocol_error:: AIOProtocolError ;
13-
1413/// Recommended default value for max clock drift if not specified.
1514pub const DEFAULT_MAX_CLOCK_DRIFT : Duration = Duration :: from_secs ( 60 ) ;
1615
@@ -52,17 +51,17 @@ impl HybridLogicalClock {
5251 /// is a no-op, and will not result in an error.
5352 ///
5453 /// # Errors
55- /// [`AIOProtocolError `] of kind [`InternalLogicError `](crate::common::aio_protocol_error::AIOProtocolErrorKind::InternalLogicError ) if
54+ /// [`HLCError `] of kind [`OverflowWarning `](HLCErrorKind::OverflowWarning ) if
5655 /// the [`HybridLogicalClock`]'s counter would be set to a value that would overflow beyond [`u64::MAX`]
5756 ///
58- /// [`AIOProtocolError `] of kind [`StateInvalid `](crate::common::aio_protocol_error::AIOProtocolErrorKind::StateInvalid ) if
59- /// the latest [`HybridLogicalClock`] (of `Self` or `other`)'s timestamp is too far in the future (determined by `max_clock_drift`)
57+ /// [`HLCError `] of kind [`ClockDrift `](HLCErrorKind::ClockDrift ) if the latest [`HybridLogicalClock`]
58+ /// (of `Self` or `other`)'s timestamp is too far in the future (determined by `max_clock_drift`)
6059 /// compared to [`SystemTime::now()`]
6160 pub fn update (
6261 & mut self ,
6362 other : & HybridLogicalClock ,
6463 max_clock_drift : Duration ,
65- ) -> Result < ( ) , AIOProtocolError > {
64+ ) -> Result < ( ) , HLCError > {
6665 let now = now_ms_precision ( ) ;
6766 // Don't update from the same node.
6867 if self . node_id == other. node_id {
@@ -103,12 +102,13 @@ impl HybridLogicalClock {
103102 /// Updates the [`HybridLogicalClock`] based on the current time
104103 ///
105104 /// # Errors
106- /// [`AIOProtocolError `] of kind [`InternalLogicError `](crate::common::aio_protocol_error::AIOProtocolErrorKind::InternalLogicError ) if
107- /// the [`HybridLogicalClock`]'s counter would be incremented and overflow beyond [`u64::MAX`]
105+ /// [`HLCError `] of kind [`OverflowWarning `](HLCErrorKind::OverflowWarning ) if
106+ /// the [`HybridLogicalClock`]'s counter would be set to a value that would overflow beyond [`u64::MAX`]
108107 ///
109- /// [`AIOProtocolError`] of kind [`StateInvalid`](crate::common::aio_protocol_error::AIOProtocolErrorKind::StateInvalid) if
110- /// the [`HybridLogicalClock`]'s timestamp is too far in the future (determined by `max_clock_drift`) compared to [`SystemTime::now()`]
111- pub fn update_now ( & mut self , max_clock_drift : Duration ) -> Result < ( ) , AIOProtocolError > {
108+ /// [`HLCError`] of kind [`ClockDrift`](HLCErrorKind::ClockDrift) if the [`HybridLogicalClock`]
109+ /// timestamp is too far in the future (determined by `max_clock_drift`) compared to [`SystemTime::now()`]
110+ /// compared to [`SystemTime::now()`]
111+ pub fn update_now ( & mut self , max_clock_drift : Duration ) -> Result < ( ) , HLCError > {
112112 let now = now_ms_precision ( ) ;
113113
114114 // if now later than self, set the time to that and reset the counter
@@ -126,34 +126,18 @@ impl HybridLogicalClock {
126126 /// and that the counter will not overflow if it is increased.
127127 ///
128128 /// # Errors
129- /// [`AIOProtocolError `] of kind [`InternalLogicError `](crate::common::aio_protocol_error::AIOProtocolErrorKind::InternalLogicError)
130- /// if the [`HybridLogicalClock`] counter is at [`u64::MAX`]
129+ /// [`HLCError `] of kind [`OverflowWarning `](HLCErrorKind::OverflowWarning) if
130+ /// the [`HybridLogicalClock`]'s counter would be set to a value that would overflow beyond [`u64::MAX`]
131131 ///
132- /// [`AIOProtocolError `] of kind [`StateInvalid `](crate::common::aio_protocol_error::AIOProtocolErrorKind::StateInvalid)
133- /// if the [`HybridLogicalClock`]'s timestamp is too far in the future (determined by `max_clock_drift`)
134- fn validate ( & self , now : SystemTime , max_clock_drift : Duration ) -> Result < ( ) , AIOProtocolError > {
132+ /// [`HLCError `] of kind [`ClockDrift `](HLCErrorKind::ClockDrift) if the [`HybridLogicalClock`]
133+ /// timestamp is too far in the future (determined by `max_clock_drift`) compared to [`SystemTime::now()`]
134+ fn validate ( & self , now : SystemTime , max_clock_drift : Duration ) -> Result < ( ) , HLCError > {
135135 if self . counter == u64:: MAX {
136- return Err ( AIOProtocolError :: new_internal_logic_error (
137- true ,
138- false ,
139- None ,
140- "Counter" ,
141- None ,
142- Some ( "Integer overflow on HybridLogicalClock counter" . to_string ( ) ) ,
143- None ,
144- ) ) ;
136+ return Err ( HLCErrorKind :: OverflowWarning ) ?;
145137 }
146138 if let Ok ( diff) = self . timestamp . duration_since ( now) {
147139 if diff > max_clock_drift {
148- return Err ( AIOProtocolError :: new_state_invalid_error (
149- "MaxClockDrift" ,
150- None ,
151- Some (
152- "HybridLogicalClock drift is greater than the maximum allowed drift"
153- . to_string ( ) ,
154- ) ,
155- None ,
156- ) ) ;
140+ return Err ( HLCErrorKind :: ClockDrift ) ?;
157141 }
158142 } // else negative time difference is ok, we only care if the HLC is too far in the future
159143
@@ -177,58 +161,46 @@ impl Display for HybridLogicalClock {
177161}
178162
179163impl FromStr for HybridLogicalClock {
180- type Err = AIOProtocolError ;
164+ type Err = ParseHLCError ;
181165
182- fn from_str ( s : & str ) -> Result < Self , AIOProtocolError > {
166+ fn from_str ( s : & str ) -> Result < Self , ParseHLCError > {
183167 let parts: Vec < & str > = s. split ( ':' ) . collect ( ) ;
184168 if parts. len ( ) != 3 {
185- return Err ( AIOProtocolError :: new_header_invalid_error (
186- "HybridLogicalClock" ,
187- s,
188- false ,
189- None ,
190- None ,
191- ) ) ;
169+ return Err ( ParseHLCError {
170+ message : "Incorrect format" . to_string ( ) ,
171+ input : s. to_string ( ) ,
172+ } ) ;
192173 }
193174
194175 // Validate first part (timestamp)
195176 let ms_since_epoch = match parts[ 0 ] . parse :: < u64 > ( ) {
196177 Ok ( ms) => ms,
197178 Err ( e) => {
198- return Err ( AIOProtocolError :: new_header_invalid_error (
199- "HybridLogicalClock" ,
200- s,
201- false ,
202- Some ( format ! (
179+ return Err ( ParseHLCError {
180+ message : format ! (
203181 "Malformed HLC. Could not parse first segment as an integer: {e}"
204- ) ) ,
205- None ,
206- ) ) ;
182+ ) ,
183+ input : s . to_string ( ) ,
184+ } )
207185 }
208186 } ;
209187 let Some ( timestamp) = UNIX_EPOCH . checked_add ( Duration :: from_millis ( ms_since_epoch) ) else {
210- return Err ( AIOProtocolError :: new_header_invalid_error (
211- "HybridLogicalClock" ,
212- s,
213- false ,
214- Some ( "Malformed HLC. Timestamp is out of range." . to_string ( ) ) ,
215- None ,
216- ) ) ;
188+ return Err ( ParseHLCError {
189+ message : "Malformed HLC. Timestamp is out of range." . to_string ( ) ,
190+ input : s. to_string ( ) ,
191+ } ) ;
217192 } ;
218193
219194 // Validate second part (counter)
220195 let counter = match parts[ 1 ] . parse :: < u64 > ( ) {
221196 Ok ( val) => val,
222197 Err ( e) => {
223- return Err ( AIOProtocolError :: new_header_invalid_error (
224- "HybridLogicalClock" ,
225- s,
226- false ,
227- Some ( format ! (
198+ return Err ( ParseHLCError {
199+ message : format ! (
228200 "Malformed HLC. Could not parse second segment as an integer: {e}"
229- ) ) ,
230- None ,
231- ) ) ;
201+ ) ,
202+ input : s . to_string ( ) ,
203+ } ) ;
232204 }
233205 } ;
234206
@@ -278,6 +250,41 @@ fn now_ms_precision() -> SystemTime {
278250 now
279251}
280252
253+ /// Represents errors that occur in the use of an HLC
254+ #[ derive( Debug , Error ) ]
255+ #[ error( "{0}" ) ]
256+ pub struct HLCError ( #[ from] HLCErrorKind ) ;
257+
258+ impl HLCError {
259+ /// Returns the corresponding [`HLCErrorKind`] for this error
260+ #[ must_use]
261+ pub fn kind ( & self ) -> & HLCErrorKind {
262+ & self . 0
263+ }
264+ }
265+
266+ /// A list specifying categories of HLC error
267+ #[ derive( Debug , Error ) ]
268+ pub enum HLCErrorKind {
269+ /// The counter would be incremented to a value that would overflow beyond [`u64::MAX`]
270+ #[ error( "counter cannot be incremented" ) ]
271+ OverflowWarning ,
272+ /// The HLC's timestamp is too far in the future compared to the current time
273+ #[ error( "exceeds max clock drift" ) ]
274+ ClockDrift ,
275+ }
276+
277+ /// Represents errors that occur when parsing an HLC from a string
278+ #[ derive( Debug , Error ) ]
279+ #[ error( "{message}" ) ]
280+ pub struct ParseHLCError {
281+ /// The error message
282+ message : String ,
283+ /// The input string that failed to parse
284+ // NOTE: This is only needed for AIOProtocolError compatibility
285+ pub input : String ,
286+ }
287+
281288// Functions to allow manipulation of the system time for testing purposes
282289#[ cfg( test) ]
283290use std:: cell:: Cell ;
@@ -296,12 +303,7 @@ fn set_time_offset(offset: Duration, positive: bool) {
296303
297304#[ cfg( test) ]
298305mod tests {
299- use crate :: common:: {
300- aio_protocol_error:: AIOProtocolErrorKind ,
301- hybrid_logical_clock:: {
302- now_ms_precision, set_time_offset, HybridLogicalClock , DEFAULT_MAX_CLOCK_DRIFT ,
303- } ,
304- } ;
306+ use super :: * ;
305307 use std:: time:: { Duration , UNIX_EPOCH } ;
306308 use test_case:: test_case;
307309 use uuid:: Uuid ;
@@ -330,9 +332,7 @@ mod tests {
330332 Ok ( ( ) ) => assert ! ( should_succeed) ,
331333 Err ( e) => {
332334 assert ! ( !should_succeed) ;
333- assert_eq ! ( e. kind, AIOProtocolErrorKind :: StateInvalid ) ;
334- assert_eq ! ( e. property_name, Some ( "MaxClockDrift" . to_string( ) ) ) ;
335- assert_eq ! ( e. property_value, None ) ;
335+ matches ! ( e. kind( ) , HLCErrorKind :: ClockDrift ) ;
336336 }
337337 }
338338 }
@@ -362,9 +362,7 @@ mod tests {
362362 match hlc. validate ( now_ms_precision ( ) , max_drift) {
363363 Ok ( ( ) ) => panic ! ( "Expected error" ) ,
364364 Err ( e) => {
365- assert_eq ! ( e. kind, AIOProtocolErrorKind :: StateInvalid ) ;
366- assert_eq ! ( e. property_name, Some ( "MaxClockDrift" . to_string( ) ) ) ;
367- assert_eq ! ( e. property_value, None ) ;
365+ matches ! ( e. kind( ) , HLCErrorKind :: ClockDrift ) ;
368366 }
369367 }
370368 }
@@ -378,11 +376,7 @@ mod tests {
378376 match hlc. validate ( now_ms_precision ( ) , DEFAULT_MAX_CLOCK_DRIFT ) {
379377 Ok ( ( ) ) => panic ! ( "Expected error" ) ,
380378 Err ( e) => {
381- assert_eq ! ( e. kind, AIOProtocolErrorKind :: InternalLogicError ) ;
382- assert ! ( e. is_shallow) ;
383- assert ! ( !e. is_remote) ;
384- assert_eq ! ( e. property_name, Some ( "Counter" . to_string( ) ) ) ;
385- assert_eq ! ( e. property_value, None ) ;
379+ matches ! ( e. kind( ) , HLCErrorKind :: OverflowWarning ) ;
386380 }
387381 }
388382
@@ -428,9 +422,7 @@ mod tests {
428422 }
429423 Err ( e) => {
430424 assert ! ( !should_succeed) ;
431- assert_eq ! ( e. kind, AIOProtocolErrorKind :: StateInvalid ) ;
432- assert_eq ! ( e. property_name, Some ( "MaxClockDrift" . to_string( ) ) ) ;
433- assert_eq ! ( e. property_value, None ) ;
425+ matches ! ( e. kind( ) , HLCErrorKind :: ClockDrift ) ;
434426 }
435427 }
436428 }
@@ -555,9 +547,7 @@ mod tests {
555547 }
556548 Err ( e) => {
557549 assert ! ( !should_succeed) ;
558- assert_eq ! ( e. kind, AIOProtocolErrorKind :: StateInvalid ) ;
559- assert_eq ! ( e. property_name, Some ( "MaxClockDrift" . to_string( ) ) ) ;
560- assert_eq ! ( e. property_value, None ) ;
550+ matches ! ( e. kind( ) , HLCErrorKind :: ClockDrift ) ;
561551 // self hlc should not have been updated
562552 assert_eq ! ( self_hlc. counter, 0 ) ;
563553 assert_eq ! ( self_hlc. timestamp, self_ts_copy) ;
@@ -623,8 +613,7 @@ mod tests {
623613 }
624614 Err ( e) => {
625615 assert ! ( !should_succeed) ;
626- assert_eq ! ( e. kind, AIOProtocolErrorKind :: InternalLogicError ) ;
627- assert_eq ! ( e. property_name, Some ( "Counter" . to_string( ) ) ) ;
616+ matches ! ( e. kind( ) , HLCErrorKind :: OverflowWarning ) ;
628617 // self hlc should not have been updated
629618 assert_eq ! ( self_hlc. counter, self_counter_copy) ;
630619 assert_eq ! ( self_hlc. timestamp, self_ts_copy) ;
0 commit comments