
# Сегментиране

`DocStore` записва по един файл за всеки запис. Без сегментиране голяма колекция би струпала всички файлове в една директория, а операциите с директории се забавят с увеличаването на броя записи. Затова storh разпределя файловете със записи в групи според два знака от идентификатора:

```text
pages/data/2d/018bcfe5-6800-7000-8000-2d5dba6274da.jsonc
```

Групата се определя от началото на последната група в UUID (знаци 24 и 25), което е случайна ентропия. Два шестнадесетични знака дават 256 групи, така че колекция със 100k записа има средно под 400 файла във всяка директория.

## Защо последните, а не първите знаци

Идентификаторите UUIDv7 започват с 48-битов времеви отпечатък в милисекунди, затова идентификаторите, създадени близо един до друг, споделят началните си знаци. Сегментирането по префикс би насочило всеки изблик на записи към една и съща натоварена директория и би сменяло групите едва с течение на времето. Последните байтове са случайни за всеки запис, така че записите се разпределят равномерно още от първия.

Сегментирането не се вижда при четене и заявки: `get()` изчислява групата от идентификатора, а `stream()` връща записите, сортирани по идентификатор във всички групи, така че страничното прелистване с курсор работи така, сякаш колекцията е един подреден списък.

## При голям мащаб

Броят на групите никога не нараства: два шестнадесетични знака означават 256 директории при всякакъв размер на колекцията. Нараства броят на файловете във всяка група:

| Записи | Файлове във всяка група |
| ------- | ---------------- |
| 10k     | ~40              |
| 100k    | ~390             |
| 1M      | ~3,900           |
| 10M     | ~39,000          |

Съвременните файлови системи индексират записите в директориите, така че няколко хиляди файла във всяка група поддържат бързо търсене на отделни записи; 1M записа е удобен обем за `get()` и индексирани заявки. Отварянето на хранилище отнема постоянно време независимо от размера на колекцията: почистването на осиротели временни файлове се изпълнява само когато маркер за прекъснал запис показва, че предишен процес се е сринал по време на запис. Операциите, които растат линейно при мащабиране, са операциите върху цялата колекция: неиндексираните сканирания, `reindex()` и `verify()` обхождат всеки файл със запис, затова ги планирайте като пакетни задачи. При над приблизително 10M записа в колекция започват да имат значение броят във всяка група и общото използване на inode: разделете колекциите или използвайте сегментирания журнал за данни, които се сканират, тъй като той поддържа малък брой файлове по замисъл.

## Другите машини

`SegmentedLog` и `Queue` не използват сегментиране, защото не създават по един файл за всеки запис. Журналът добавя записи към сегментни файлове в `segments/`, ограничени от `max_segment_bytes` (по подразбиране 1 MiB), а опашката добавя събития към един `queue.log`. Броят на файловете остава нисък по замисъл.
