Сегментиране
DocStore записва по един файл за всеки запис. Без сегментиране голяма колекция би струпала всички файлове в една директория, а операциите с директории се забавят с увеличаването на броя записи. Затова storh разпределя файловете със записи в групи според два знака от идентификатора:
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. Броят на файловете остава нисък по замисъл.