From 33239b612771c529259882a2b25acc8229bfd257 Mon Sep 17 00:00:00 2001 From: Todd Green Date: Fri, 5 Jun 2026 22:52:37 +0000 Subject: [PATCH] Use a random UUID for ctx.new_guid() generate_guid() built UUID-shaped strings from the wall clock plus a thread-local counter, which made the returned identifiers sequential and predictable. Generate a real version-4 UUID instead. The value is produced once inside the built-in syscall activity and recorded in history, so it is still returned unchanged on replay. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Cargo.toml | 1 + src/lib.rs | 41 +++++++++-------------------------------- 2 files changed, 10 insertions(+), 32 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 81dddba..0ca7b35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ tokio-util = "0.7" # For CancellationToken serde = { version = "1", features = ["derive"] } serde_json = "1" async-trait = "0.1" +uuid = { version = "1", features = ["v4"] } tracing = { version = "0.1", features = ["std"] } tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter", "json"] } semver = "1" diff --git a/src/lib.rs b/src/lib.rs index 6faa754..e311385 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2769,11 +2769,11 @@ impl OrchestrationContext { } } - /// Generate a new deterministic GUID. + /// Generate a new GUID. /// - /// This schedules a built-in activity that generates a unique identifier. - /// The GUID is deterministic across replays (the same value is returned - /// when the orchestration replays). + /// This schedules a built-in activity that generates a random version-4 + /// UUID. The value is recorded in history, so the same GUID is returned + /// when the orchestration replays. /// /// # Example /// @@ -2871,36 +2871,13 @@ impl OrchestrationContext { } } -/// Generate a deterministic GUID for use in orchestrations. +/// Generate a GUID for use in orchestrations. /// -/// Uses timestamp + thread-local counter for uniqueness. +/// Returns a random version-4 UUID. The value is produced once inside the +/// built-in syscall activity and recorded in history, so the same value is +/// returned on every replay. pub(crate) fn generate_guid() -> String { - use std::time::{SystemTime, UNIX_EPOCH}; - - let timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .map(|d| d.as_nanos()) - .unwrap_or(0); - - // Thread-local counter for uniqueness within the same timestamp - thread_local! { - static COUNTER: std::cell::Cell = const { std::cell::Cell::new(0) }; - } - let counter = COUNTER.with(|c| { - let val = c.get(); - c.set(val.wrapping_add(1)); - val - }); - - // Format as UUID-like string - format!( - "{:08x}-{:04x}-{:04x}-{:04x}-{:012x}", - (timestamp >> 96) as u32, - ((timestamp >> 80) & 0xFFFF) as u16, - (counter & 0xFFFF) as u16, - ((timestamp >> 64) & 0xFFFF) as u16, - (timestamp & 0xFFFFFFFFFFFF) as u64 - ) + uuid::Uuid::new_v4().to_string() } impl OrchestrationContext {