use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use thiserror::Error;
use tokio::sync::Mutex;
use crate::storage::Storage;
use super::{Id, IdGenerator, IdType};
use std::future::Future;
use std::pin::Pin;
#[derive(Debug, Error)]
pub enum SnowflakeError {
#[error("Clock moved backwards. Refusing to generate ID.")]
ClockMovedBackwards,
#[error("Storage error: {0}")]
StorageError(#[from] Box<dyn std::error::Error + Send + Sync>),
#[error("Worker ID {0} is out of range (0-{MAX_WORKER_ID})", MAX_WORKER_ID = SnowflakeConfig::MAX_WORKER_ID)]
InvalidWorkerId(u16),
}
#[derive(Debug, Clone, Copy)]
pub struct SnowflakeConfig {
pub worker_id: u16,
pub epoch: u64,
}
impl Default for SnowflakeConfig {
fn default() -> Self {
Self {
worker_id: 0,
epoch: 1735689600000,
}
}
}
impl SnowflakeConfig {
pub const MAX_WORKER_ID: u16 = 1023;
}
#[derive(Debug, Clone, Copy, Default)]
struct SnowflakeState {
last_timestamp: u64,
last_sequence: u64,
}
#[derive(Debug)]
pub struct Snowflake<S: Storage> {
config: SnowflakeConfig,
storage: Arc<S>,
state: Mutex<SnowflakeState>,
}
impl<S: Storage> Snowflake<S> {
const SEQUENCE_BITS: u8 = 12;
const WORKER_ID_BITS: u8 = 10;
const MAX_SEQUENCE: u64 = (1 << Self::SEQUENCE_BITS) - 1;
const WORKER_ID_SHIFT: u8 = Self::SEQUENCE_BITS;
const TIMESTAMP_SHIFT: u8 = Self::SEQUENCE_BITS + Self::WORKER_ID_BITS;
pub async fn new(config: SnowflakeConfig, storage: Arc<S>) -> Result<Self, SnowflakeError> {
if config.worker_id > SnowflakeConfig::MAX_WORKER_ID {
return Err(SnowflakeError::InvalidWorkerId(config.worker_id));
}
let stored = storage.load().await?;
let initial_state = SnowflakeState {
last_timestamp: stored.last_timestamp,
last_sequence: stored.last_sequence,
};
Ok(Self {
config,
storage,
state: Mutex::new(initial_state),
})
}
pub async fn generate_u64(&self) -> Result<u64, SnowflakeError> {
let mut state = self.state.lock().await;
let current_timestamp = self.current_timestamp()?;
let (timestamp, sequence) = if current_timestamp < state.last_timestamp {
return Err(SnowflakeError::ClockMovedBackwards);
} else if current_timestamp == state.last_timestamp {
let sequence = state.last_sequence + 1;
if sequence > Self::MAX_SEQUENCE {
let timestamp = self.wait_for_next_millis(state.last_timestamp).await;
(timestamp, 0)
} else {
(current_timestamp, sequence)
}
} else {
(current_timestamp, 0)
};
let id = ((timestamp - self.config.epoch) << Self::TIMESTAMP_SHIFT)
| (self.config.worker_id as u64) << Self::WORKER_ID_SHIFT
| sequence;
state.last_timestamp = timestamp;
state.last_sequence = sequence;
self.storage
.save(crate::storage::GeneratorState {
worker_id: self.config.worker_id,
last_timestamp: timestamp,
last_sequence: sequence,
})
.await?;
Ok(id)
}
fn current_timestamp(&self) -> Result<u64, SnowflakeError> {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.map_err(|_| SnowflakeError::ClockMovedBackwards)
}
async fn wait_for_next_millis(&self, last_timestamp: u64) -> u64 {
let mut timestamp = self.current_timestamp().unwrap_or(last_timestamp);
while timestamp <= last_timestamp {
tokio::time::sleep(std::time::Duration::from_millis(1)).await;
timestamp = self.current_timestamp().unwrap_or(last_timestamp);
}
timestamp
}
}
impl<S: Storage + 'static> IdGenerator for Snowflake<S> {
type Error = SnowflakeError;
fn generate(&self) -> Pin<Box<dyn Future<Output = Result<Id, Self::Error>> + Send + '_>> {
Box::pin(async move {
let id = self.generate_u64().await?;
Ok(Id::Numeric64(id))
})
}
fn id_type(&self) -> IdType {
IdType::Numeric64
}
}
impl Snowflake<crate::storage::MemoryStorage> {
pub fn with_default() -> Self {
let config = SnowflakeConfig::default();
let storage = Arc::new(crate::storage::MemoryStorage::with_state(
crate::storage::GeneratorState {
worker_id: config.worker_id,
last_timestamp: 0,
last_sequence: 0,
},
));
tokio::task::block_in_place(|| {
tokio::runtime::Handle::current()
.block_on(async { Self::new(config, storage).await.unwrap() })
})
}
}