armdb 0.1.16

sharded bitcask key-value storage optimized for NVMe
Documentation
# armdb QA

## Можно ли хранить несколько коллекций в одном файле?

Да, теоретически можно хранить несколько коллекций в одном наборе shard-файлов,
но это уже не текущая модель `database per collection`, а общий storage engine с
логическими коллекциями.

Сейчас модель явно обратная:

- `armdb/src/lib.rs` фиксирует `one tree = one database directory`.
- `Db::tree_path()` кладет коллекцию в отдельный путь `name:vN`.
- Каждый `Engine::open()` открывает `config.shard_count` отдельных shard-директорий.
- Для Bitcask свежий shard держит минимум два file descriptor: active write file
  и active read file. После rotations immutable files тоже остаются открытыми для
  чтения.
- `__seq` дополнительно использует FixedMap с 4 shard'ами.

Грубая оценка для Bitcask:

```text
fd ~= collections * shards * 2
   + immutable rotated files
   + __seq fixed shards
   + rpc/socket/прочие fd процесса
```

Чтобы хранить несколько коллекций в одном физическом логе, нужно добавить
`collection_id` в record/log format и recovery, оставить отдельные in-memory
indexes per collection, а физический append-log сделать общим per shard. Тогда
range, prefix, CAS, delete и typed API функционально сохраняются.

Но "без потерь" это не бесплатная замена:

- общий shard writer даст больше contention между горячими коллекциями;
- compaction станет сложнее, потому что live/dead данные разных коллекций будут
  смешаны в одних files;
- migrations, drop collection, backup, restore и replication должны стать
  collection-aware;
- изоляция отказов и ручная диагностика станут слабее, потому что одна
  поврежденная физическая область потенциально затрагивает несколько коллекций.

Для множества маленьких коллекций общий набор shard-файлов может быть даже лучше:
меньше fd, меньше директорий, лучше locality, меньше startup overhead. Для
нескольких горячих коллекций текущая физическая изоляция может быть быстрее и
предсказуемее.

Практичные промежуточные варианты:

- уменьшать `shard_count` для маленьких коллекций;
- сделать fd budget и lazy-open для immutable files;
- закрывать cold collections;
- шарить `Engine` только внутри группы коллекций одного backend'а;
- отдельно рассматривать Bitcask и FixedStore, потому что Fixed сейчас имеет
  другую модель: один `fixed.data` per shard и in-place writes.

## Вредит ли Linux-серверу много открытых file descriptor?

Сами по себе сотни или тысячи открытых fd на Linux нормальны. Производительность
сервера обычно не падает только из-за того, что у процесса открыто 300, 1000 или
5000 обычных файлов.

I/O latency определяется диском, page/direct I/O, locks, fsync, cache misses,
очередями и contention, а не самим числом открытых fd. Открытый fd в основном
занимает kernel memory и учитывается в лимитах процесса.

Реальные проблемы начинаются, когда:

- процесс упирается в `RLIMIT_NOFILE` и получает `EMFILE` / `Too many open files`;
- системный лимит fd слишком низкий для DB/server процесса;
- код регулярно сканирует все fd или держит слишком много неиспользуемых файлов
  открытыми;
- из-за открытых immutable files растет kernel memory и сложность управления
  lifecycle'ом файлов.

Для среднего или большого server/DB проекта `ulimit -n 512` скорее низкий лимит.
Нормально выставлять `4096`, `8192`, `65536` через systemd `LimitNOFILE=`,
container limits или service manager.

Но если обычный проект с 20 коллекциями уже требует поднятия лимита, это сигнал,
что дефолтная модель fd стоит пересмотреть. В armdb основной множитель сейчас:

```text
collection_count * shard_count * files_per_shard
```

Поэтому ближайшая практичная оптимизация без большого рефакторинга - дать мелким
коллекциям меньший `shard_count` или сделать lazy-open/reopen для immutable files.