Struct TimestampIdGenerator
pub struct TimestampIdGenerator;Expand description
Generator for creating TimestampId values with monotonicity guarantees.
TimestampIdGenerator is a stateless utility that encapsulates the clock access
and monotonicity logic required to safely generate time-ordered identifiers. It
ensures that generated timestamps are strictly increasing, even in scenarios where:
- The system clock hasn’t advanced between calls
- The system clock moves backward (clock skew)
- Multiple IDs are generated in rapid succession
§Design Philosophy
This generator follows the principle of separating concerns:
TimestampIdis a pure value object (clock-free, immutable)TimestampIdGeneratorhandles the impure operations (clock access, monotonicity logic)
This separation enables:
- Easy testing of
TimestampIdwithout mocking time - Clear understanding of when system clock is accessed
- Explicit control over monotonicity guarantees
§Monotonicity Guarantees
When a previous timestamp is provided, the generator ensures the new timestamp is
strictly greater than the previous one. If the current time hasn’t advanced past
the previous timestamp, the generator adds 1 millisecond to the previous value.
This guarantees total ordering of events:
T1 < T2 < T3 < ... < TnEven if generated at the same instant or during clock skew.
§Thread Safety and Synchronization
Important: While the generator itself is stateless and thread-safe, maintaining monotonicity across multiple threads requires external synchronization. The typical pattern is:
- Acquire a per-patient lock
- Read the most recent timestamp ID for that patient
- Generate the next ID using
generate(Some(previous)) - Write the new ID
- Release the lock
This ensures that even with concurrent writes to different patients, each patient’s timeline remains strictly ordered.
§Millisecond Precision
The generator uses millisecond precision (3 decimal places). This means:
- Maximum theoretical throughput: 1,000 events per second per patient
- In practice, with locking overhead: ~100-500 events per second
- For higher throughput scenarios, consider batching or sequence numbers
§Clock Skew Handling
If the system clock moves backward, the generator detects this by comparing against the previous timestamp and advances from the previous value instead of using the (earlier) current time. This prevents:
- Timestamp collisions
- Out-of-order events
- Audit log inconsistencies
§Stateless Design
The generator is stateless - it doesn’t store any previous timestamps internally. This means:
- No memory overhead per patient
- No cleanup required
- Caller controls what “previous” means (from database, cache, etc.)
- Easy to use in distributed systems
§Performance Characteristics
- Time complexity: O(1) - constant time for all operations
- Memory: Zero-cost abstraction - compiles to a simple function call
- System calls: One call to
Utc::now()per invocation
§When to Use
Use this generator when you need:
- Time-ordered event logging
- Audit trails with strict ordering
- Patient record versioning
- Any scenario requiring monotonically increasing timestamps
§When Not to Use
Don’t use this generator for:
- Random identifiers (use
Uuid::new_v4()directly) - High-frequency events requiring sub-millisecond precision
- Scenarios where logical clocks (Lamport/Vector clocks) are more appropriate
§See Also
TimestampId- The value object produced by this generatorUuid- For the UUID component
Implementations§
§impl TimestampIdGenerator
impl TimestampIdGenerator
pub fn generate(previous: Option<&str>) -> Result<TimestampId, UuidError>
pub fn generate(previous: Option<&str>) -> Result<TimestampId, UuidError>
Generates a new TimestampId with optional monotonicity relative to a previous ID.
This is the primary method for creating time-ordered identifiers in the VPR system. It combines the current UTC time with a freshly generated UUID to create a globally unique, time-ordered identifier.
§Monotonicity Behavior
- If
previousisNone: Uses the current system time (Utc::now()) - If
previousisSome(id): Ensures the new timestamp is strictly greater than the previous one, advancing by 1ms if necessary
§Arguments
previous- Optional string representation of the previousTimestampId. Must be in valid format if provided:YYYYMMDDTHHMMSS.mmmZ-<uuid>
§Returns
Ok(TimestampId)- A new identifier with guaranteed monotonicityErr(UuidError)- If thepreviousstring is malformed
§Errors
Returns [UuidError::InvalidInput] if previous is provided but cannot be parsed.
This typically indicates:
- Invalid timestamp format
- Malformed UUID
- Missing required components
§Monotonicity Algorithm
now = current_time()
if previous exists and now <= previous.timestamp:
new_timestamp = previous.timestamp + 1ms
else:
new_timestamp = nowThis ensures strictly increasing timestamps even during:
- Rapid successive calls
- System clock adjustments
- NTP corrections
§Thread Safety
This method is thread-safe but does not provide atomicity guarantees across multiple calls. For maintaining monotonicity across threads, wrap calls in a per-patient mutex or other synchronization primitive.
§Performance
- Single system call to
Utc::now() - Single UUID generation
- Optional parsing if
previousis provided - Total time: ~1-5 microseconds (depending on system)
Auto Trait Implementations§
impl Freeze for TimestampIdGenerator
impl RefUnwindSafe for TimestampIdGenerator
impl Send for TimestampIdGenerator
impl Sync for TimestampIdGenerator
impl Unpin for TimestampIdGenerator
impl UnwindSafe for TimestampIdGenerator
Blanket Implementations§
§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request