armdb 0.1.13

sharded bitcask key-value storage optimized for NVMe
Documentation
Для задачи написания **memtable** в key-value базе данных:

**Однозначный выбор: `seize`.**

### Почему `seize` лучше для Memtable?

В контексте БД есть один критический сценарий: **Iterators (Range Scans)**.
Memtable — это не просто хеш-мапа для точечных чтений. Ты будешь делать по ней итераторы (сканирование диапазона ключей) или создавать снапшоты.

1. **Проблема `sdd` (и чистого Epoch-based):**
Если у тебя есть поток, который делает долгий скан (iterator) по memtable, он держит "эпоху". Пока этот поток читает (даже если он медленный), `sdd` **не может освободить ни один удаленный узел** во всей структуре. Если в это время идет активная запись (heavy write workload), у тебя произойдет взрывной рост потребления RAM (memory bloat), потому что сборщик мусора полностью заблокирован одним читателем.
2. **Преимущество `seize`:**
Благодаря алгоритму Hyaline, долгий читатель (итератор) защищает только те конкретные узлы (батчи), на которые он смотрит, и не блокирует глобальную очистку остальных удаленных объектов. Для БД, где важна стабильность latency и предсказуемое потребление памяти при смешанной нагрузке (Read+Write), это критически важно.

---

### Сравнение DX (Developer Experience) и Эргономики

**Победитель по DX: `seize**`.

Он ощущается как современный, переосмысленный `crossbeam-epoch`. API чище, меньше бойлерплейта, и он лучше документирован как *самостоятельная библиотека*, а не как "движок для другой либы".

#### 1. Инициализация и структуры

В `seize` ты работаешь с `Collector`. Это интуитивно понятно:

```rust
// Seize: Создаем коллектор, который можно клонировать и шарить
let collector = seize::Collector::new();

// В lock-free структуре
struct MyMemtable {
    collector: seize::Collector,
    // ...
}

```

В `sdd` API чуть более сырой, так как он заточен под внутренности `scc`. Там часто приходится жонглировать `sdd::Guard` чуть более явно, и документация предполагает, что ты глубоко понимаешь контекст `scc`.

#### 2. Защита указателей (Guard)

Оба используют паттерн Guard, но `seize` ведет себя более "Растово":

**Seize:**

```rust
let guard = self.collector.enter();
// Получаем atomic pointer
let ptr = some_atomic.load(Ordering::Acquire, &guard);
// Разыменовываем безопасно
let value = unsafe { guard.deref(ptr) }; // Возвращает ссылку &T

```

`seize` имеет очень приятный метод `map`, который позволяет безопасно проецировать лайфтайм гарда на данные, что удобно для итераторов.

**Sdd:**
Также использует `sdd::Guard`, но API методов иногда требует передачи `&Guard` параметром там, где этого не ожидаешь, или возвращает специфические типы, заточенные под оптимизацию, что немного усложняет написание своего кода с нуля.

#### 3. Удаление (Retiring)

Самая важная часть для твоей реализации Lock-Free SkipList или Tree.

**Seize:**

```rust
// Просто и понятно: удаляем, когда никто не смотрит
unsafe { self.collector.retire(ptr, seize::reclaim::boxed::<Node>) };

```

У `seize` очень удобная система кастомных реклеймеров (если ты аллоцируешь не через `Box`, а через свой арена-аллокатор, что популярно в БД, это легко настроить).

### Резюме

Ты пишешь базу данных, а значит тебе нужны **стабильные Latency** и **устойчивость к долгим чтениям** (scan/iter).

* Используй **`seize`**. Это даст тебе лучшую гарантию от OOM (Out Of Memory) при долгих транзакциях чтения и более приятный API для написания сложной структуры вроде SkipList.
* **`sdd`** стоит брать только в том случае, если ты *точно* знаешь, что у тебя не будет долгих итераторов, и тебе нужно выжать последние наносекунды на простых `get/put` операциях (как в кэше). Но для полноценной memtable это риск.