use crate::Id;
use std::{
sync::atomic::AtomicU64,
time::{SystemTime, UNIX_EPOCH},
};
pub struct Local<F>
where
F: Fn(u64) + Send + Sync + 'static,
{
next: AtomicU64,
max: AtomicU64,
step: u64,
remain_step: u64,
save_fn: F,
last_write: AtomicU64,
}
impl<F> Id for Local<F>
where
F: Fn(u64) + Send + Sync + 'static,
{
fn next(&self) -> u64 {
let cur = self.next.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
if cur % self.remain_step == 0 {
self.schedule_write(cur);
}
cur
}
}
impl<F> Local<F>
where
F: Fn(u64) + Send + Sync + 'static,
{
pub fn new(next: u64, step: u64, remain_step: u64, write: F) -> Self {
let max = next - (next % step) + step;
Local {
next: AtomicU64::new(next),
max: AtomicU64::new(max),
step,
save_fn: write,
remain_step,
last_write: AtomicU64::new(0),
}
}
fn apply(&self, n: u64) {
self.max
.fetch_add(n * self.step, std::sync::atomic::Ordering::Relaxed);
}
#[inline]
fn schedule_write(&self, id: u64) {
let max = self.max.load(std::sync::atomic::Ordering::Relaxed);
if max - id >= self.remain_step {
let last_write = self.last_write.load(std::sync::atomic::Ordering::Acquire);
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as u64;
if now > last_write {
match self.last_write.compare_exchange(
last_write,
now,
std::sync::atomic::Ordering::Relaxed,
std::sync::atomic::Ordering::Relaxed,
) {
Ok(_) => {
if now - last_write < 10000 {
tracing::warn!("两次写间隔时间小于10秒,扩容10倍");
self.apply(1);
} else {
tracing::debug!("扩容id");
self.apply(1);
}
(self.save_fn)(self.max.load(std::sync::atomic::Ordering::Relaxed));
}
Err(_) => {
tracing::debug!("并发,忽略写入");
}
}
} else {
tracing::debug!("并发,忽略写入");
}
}
}
}