Към съдържанието

Машини

Избор на машина

И трите машини съхраняват едно и също: ID от UUIDv7 и масив с данни, но го разполагат по различен начин на диска. Това определя за какво е подходяща всяка машина:

DocStore SegmentedLog Queue
На диска по един файл JSONC за запис, с разпределяне по опашните части на UUID сегменти само за добавяне с редове във формат JSON, предшествани от дължината и контролната сума, плюс манифест един журнал само за добавяне на събития
Записване атомарна подмяна на файл за всеки запис добавяне с контролна сума; презаписванията и изтриванията добавят нови записи, а мястото се освобождава чрез compact() добавяне с контролна сума за всяко събитие в опашката
Четене get() по ID, заявки с индекси четене с курсор и сканиране по времеви диапазон, get() чрез индекса на състоянието claim() в реда на добавяне в опашката
Подходящо за директно четене и търсене по индекс при всякакъв размер на колекцията потоци с интензивно добавяне до около 1M записа надеждно разпределяне на задачи между процеси
Слабо място сканиране на полета без индекс при над около 10k записа често пренаписване на отделни записи (старите версии заемат място до компактирането) не е универсално хранилище на записи; полезните данни се изтриват след завършване на задачата

DocStore и SegmentedLog използват еднакъв верижен интерфейс за заявки API: една и съща верига от where() работи и с двете машини; различава се само изпълнението. DocStore може да отговори чрез вторичните си индекси, а SegmentedLog филтрира при сканиране на сегментите. Queue се основава на резервиране, а не на заявки: работниците claim() следващата задача, вместо да филтрират задачите.

Практически насоки:

  • Записи, които извличате, актуализирате или изтривате поотделно и търсите по поле: DocStore. Добавете индекси за полетата, по които филтрирате.
  • Събития или версии, които предимно добавяте и четете обратно по ред или в определен времеви интервал: SegmentedLog.
  • Задачи, предавани от производители към работни процеси: Queue. Заявяването и приключването са безопасни при множество процеси, а claimMany()/completeMany() разпределят разходите за заключване и fsync.
  • Свързвания, търсене или отчети върху произволна колекция: добавете огледало SQL. Файловете остават канонични; огледалото е възстановима проекция в SQLite или MySQL.

Машините се комбинират: споделят само основната директория, така че едно приложение може да съхранява съдържание в DocStore, журнал за одит в SegmentedLog и фонови задачи в Queue под един и същ корен за съхранение.

DocStore

DocStore е предназначен за директно четене и прости сканирания на трайни записи. Той записва по един файл JSONC за всеки запис:

PHP
$store = new Storh\DocStore($root, 'articles');
$record = $store->put(['slug' => 'hello', 'published' => true]);
$same = $store->get($record->id());

Файловете със записи се разпределят по последните знаци на UUID, така че пиковите записи от UUIDv7 се разпределят в различни директории:

Text
articles/data/2d/018bcfe5-6800-7000-8000-2d5dba6274da.jsonc

SegmentedLog

SegmentedLog е предназначен за записи с интензивно добавяне, четене с курсор и четене по времеви диапазон. Записите се добавят към активния сегмент, по един ред на запис във формат <byte-length>\t<xxh32>\t<json> (вижте Атомарност). Файловете на сегментите използват разширение .ndjson, но префиксът с дължината и контролната сума означава, че обикновените инструменти за NDJSON не могат да ги четат; използвайте stream() или конструктора на заявки, за да прочетете записите. Пълните сегменти се запечатват и получават разредени индекси.

PHP
$events = new Storh\SegmentedLog($root, 'events');
$events->put(['type' => 'user.created']);
$events->compact();

Опашка

Queue е предназначен за трайни опашки с висока пропускателна способност. Enqueue, claim, complete, requeue и purge записват събития само за добавяне в queue.log; състоянието на опашката се възпроизвежда в паметта при отваряне. Пакетните методи разпределят разходите за заключване и изчистване между много задачи. Журналът расте с всяко събитие; stats() показва текущия му размер в байтове.

PHP
$queue = new Storh\Queue($root, 'mail');
$queue->enqueue(['to' => 'person@example.test']);
$job = $queue->claim();

Трайност и едновременен достъп

storh публикува файловите записи чрез атомарно преименуване в целевата директория. При повторно отваряне и repair() се премахват изоставените временни файлове, чийто собствен процес е приключил, като временните файлове на активните записващи процеси се запазват. Маркерите на записващите процеси правят проверката с постоянно време, така че колекцията се сканира при отваряне на хранилището само след срив.

SegmentedLog и Queue записват редове в журнала със защитена дължина и контролна сума. При повторно отваряне или поправка прекъснатият край се отрязва до последното потвърдено събитие, а състоянието в паметта се възстановява от трайното съдържание на журнала. Записите в журнала и опашката се сериализират чрез заключвания на файловата система.

Промените в DocStore се сериализират със заключване за запис на ниво колекция, така че файловете със записи и вторичните индекси се актуализират като една граница на съгласуваност между процесите. Едновременните записи с различни ID-та са безопасни при същите допускания за атомарно преименуване във файловата система. При едновременни записи към едно и също ID печели последното преименуване.

storh изчиства буферите и извиква fsync за файловите дескриптори преди завършване на преименуване или добавяне и се опитва да извика fsync за родителските директории след атомарни преименувания. Устойчивостта при загуба на захранване все пак зависи от това дали файловата система и опциите за монтиране спазват тези синхронизации.