From 104b81151737ce79dfd510bd89cb578846b99471 Mon Sep 17 00:00:00 2001 From: Kacy Fortner Date: Sat, 14 Feb 2026 16:39:31 -0500 Subject: [PATCH] mix random bits into migration ID to prevent clock-failure collisions when SystemTime::now() is unavailable (returns Duration::ZERO via unwrap_or_default), migration IDs degenerated to just the slot number, causing collisions if the same slot was migrated more than once. mix 16 bits of randomness into the upper bits of the ID so uniqueness doesn't depend solely on clock availability. --- crates/ember-cluster/src/migration.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/ember-cluster/src/migration.rs b/crates/ember-cluster/src/migration.rs index 9c69ccc9..4a30cc25 100644 --- a/crates/ember-cluster/src/migration.rs +++ b/crates/ember-cluster/src/migration.rs @@ -38,15 +38,21 @@ use crate::NodeId; pub struct MigrationId(pub u64); impl MigrationId { - /// Generate a new migration ID from timestamp and slot. + /// Generate a new migration ID from timestamp, slot, and random bits. + /// + /// The ID combines nanosecond timestamp with the slot number and 16 bits + /// of randomness. The random component prevents collisions when the + /// system clock is unavailable (`unwrap_or_default` returns zero) or + /// when two migrations for the same slot start within the same nanosecond. pub fn new(slot: u16) -> Self { + use rand::Rng; use std::time::{SystemTime, UNIX_EPOCH}; let ts = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_default() .as_nanos() as u64; - // Combine timestamp with slot for uniqueness - Self(ts ^ (slot as u64)) + let noise: u16 = rand::rng().random(); + Self(ts ^ (slot as u64) ^ ((noise as u64) << 48)) } }