kvid 0.1.7

Distributed ID generator with dual-segment preloading and lock-free fast path / 双号段预加载、无锁快速路径的分布式 ID 生成器
Documentation
# kvid 设计文档

## 概述

kvid 是一个基于 KVRocks 的分布式自增 ID 生成器,采用双号段设计,实现高性能、低延迟的 ID 分配。

## 核心设计

### 双号段机制

```
┌─────────────┐     ┌─────────────┐
│  当前号段    │     │  缓冲号段    │
│  id → max   │     │  next: Option│
│             │     │  <Seg>      │
└─────────────┘     └─────────────┘
      ↓ 用尽时切换        ↑ 取走即填充
```

1. 维护一个 `Option<Seg>` 缓冲号段
2. 每次取走缓冲号段后,立即异步填充新的号段
3. 当前号段用尽时,无缝切换到缓冲号段
4. 简单直接:取走就填充,始终保持缓冲可用

### 动态步长调整

根据上次步长和时间间隔动态计算新 step:

```
step = prev_step * PRELOAD_SEC / elapsed
```

- `prev_step`: 上次 fetch 的步长
- `elapsed`: 两次 fetch 之间的时间间隔(秒)
- `PRELOAD_SEC`: 预加载时长(默认 60 秒)

这样可以根据业务负载自动调整,高峰期获取更多 ID,低谷期减少浪费。

## 数据结构

### KvId 成员变量

| 字段   | 类型          | 说明                                |
| ------ | ------------- | ----------------------------------- |
| `name` | `SmolStr`     | ID 名称,用作 KVRocks hash 的 field |
| `fast` | `Fast`        | 快速路径状态,原子操作无锁访问      |
| `slow` | `Mutex<Slow>` | 慢路径状态,包含缓冲号段和统计信息  |

### Fast 结构(无锁快速路径)

通过 `Fast::new()` 常量函数初始化,每个 `KvId` 实例拥有独立的原子变量。

| 字段   | 类型         | 说明                 |
| ------ | ------------ | -------------------- |
| `id`   | `AtomicU64`  | 当前号段已分配的 ID  |
| `max`  | `AtomicU64`  | 当前号段的最大值     |
| `lock` | `AtomicBool` | 填充锁,防止重复填充 |

### Slow 结构(锁保护慢路径)

通过 `Slow::new()` 常量函数初始化。

| 字段   | 类型          | 说明                                      |
| ------ | ------------- | ----------------------------------------- |
| `next` | `Option<Seg>` | 缓冲号段,取走即填充                      |
| `step` | `u64`         | 当前步长(每次从 KVRocks 获取的 ID 数量) |
| `ts`   | `u64`         | 上次 fetch 的时间戳                       |

### Seg 结构

| 字段  | 类型  | 说明        |
| ----- | ----- | ----------- |
| `id`  | `u64` | 号段起始 ID |
| `max` | `u64` | 号段结束 ID |

## 执行流程

```
next() 调用
    │
    ▼
┌─────────────────┐
│ 快速路径 CAS    │ ← 大多数请求走这里,无锁
│ id < max ?      │
└────────┬────────┘
         │ 是
         ▼
    返回 id+1
    缓冲为空? → 异步填充
         │
         │ 否(号段用尽)
         ▼
┌─────────────────┐
│ 慢路径(加锁)   │
│ 1. 再次尝试 CAS │
│ 2. 切换缓冲号段 │ → 异步填充
│ 3. 同步 fetch   │ → 异步填充
└─────────────────┘
```

## 性能优化

1. **无锁快速路径**: 使用 `AtomicU64` + CAS (`compare_exchange_weak`),绝大多数请求无需加锁
2. **缓冲填充机制**: 取走即填充,始终保持缓冲可用,避免同步等待
3. **动态步长**: 根据负载自动调整,平衡性能和 ID 浪费,范围 [1, 1000000]
4. **最小化锁范围**: 只在号段切换时短暂持锁,锁不跨越 await 点
5. **结构分离**: Fast/Slow 分离,快速路径完全无锁
6. **parking_lot**: 使用 `parking_lot::Mutex` 替代标准库,性能更优

## 常量定义

| 常量          | 值      | 说明             |
| ------------- | ------- | ---------------- |
| `PRELOAD_SEC` | 60      | 预加载时长(秒) |
| `STEP_MIN`    | 1       | 最小步长         |
| `STEP_MAX`    | 1000000 | 最大步长         |
| `KVID_KEY`    | "kvid"  | Redis hash key   |

## 错误处理

| 错误           | 说明                       |
| -------------- | -------------------------- |
| `Empty`        | 号段为空(理论上不应发生) |
| `StepOverflow` | step 超出 i64 范围         |
| `Kv`           | Redis/KVRocks 操作错误     |

## KVRocks 存储

使用 Hash 结构存储:

```
Key: "kvid"
Field: {name}
Value: {当前最大 ID}
```

通过 `HINCRBY` 原子操作获取新号段,保证分布式环境下的唯一性。