Preskoči na vsebino

Mehanizmi

Izbira mehanizma

Vsi trije mehanizmi shranjujejo isto stvar: ID UUIDv7 in polje podatkov, vendar jo na disku razporedijo različno. Ta razporeditev določa, za kaj je posamezen mehanizem primeren:

DocStore SegmentedLog Queue
Na disku ena datoteka JSONC za vsak zapis, razdeljena po zadnjih znakih UUID segmenti, v katere se samo dodaja, z vrsticami JSON, pred katerimi sta dolžina in kontrolna vsota, ter manifest en dnevnik dogodkov, v katerega se samo dodaja
Pisanje atomska zamenjava datoteke za vsak zapis dodajanje s kontrolno vsoto; prepisi in brisanja dodajo nove vnose, prostor se sprosti s compact() dodajanje s kontrolno vsoto za vsak dogodek čakalne vrste
Branje get() po ID-ju, poizvedbe z indeksi pregledi s kazalcem in po časovnem razponu, get() prek indeksa stanja claim() po vrstnem redu dodajanja
Najprimernejša uporaba branje posameznih zapisov in iskanje z indeksi pri kateri koli velikosti zbirke pretoki z veliko dodajanji in približno do 1M zapisov trajno razpošiljanje opravil med procesi
Šibka točka pregledi neindeksiranih polj pri več kot približno 10k zapisih pogosti prepisi posameznih zapisov (stare različice zasedajo prostor do strnjevanja) ni splošna shramba zapisov; vsebina se po dokončanju opravila zavrže

DocStore in SegmentedLog uporabljata isti tekoči poizvedbe API: ista veriga where() se izvaja v obeh mehanizmih, razlikuje pa se samo izvedba. DocStore lahko odgovori na podlagi sekundarnih indeksov, medtem ko SegmentedLog filtrira pri pregledu segmentov. Queue temelji na prevzemanju, zato po njem ni mogoče poizvedovati: delavci claim() naslednje opravilo, namesto da bi opravila filtrirali.

Praktična pravila:

  • Zapisi, ki jih posamično pridobivate, posodabljate ali brišete ter poizvedujete po poljih: DocStore. Dodajte indekse za polja, po katerih filtrirate.
  • Dogodki ali različice, ki jih večinoma dodajate ter berete nazaj po vrstnem redu ali časovnem oknu: SegmentedLog.
  • Opravila, ki jih producenti posredujejo procesom delavcev: Queue. Prevzemi in dokončanja so varni pri več procesih, metode claimMany()/completeMany() pa porazdelijo stroške zaklepanja in sinhronizacije z diskom.
  • Združevanja, iskanje ali poročanje za katero koli zbirko: dodajte zrcalno kopijo SQL. Datoteke ostanejo kanonične; zrcalna kopija je projekcija, ki jo je mogoče znova zgraditi, v SQLite ali MySQL.

Mehanizmi se dopolnjujejo: delijo si samo korenski imenik, zato lahko aplikacija v istem korenu shrambe hrani vsebino v DocStore, revizijsko sled v SegmentedLog in opravila v ozadju v Queue.

DocStore

DocStore je namenjen branju posameznih zapisov in preprostim pregledom trajnih zapisov. Za vsak zapis zapiše eno datoteko JSONC:

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

Datoteke zapisov so razdeljene po zadnjih znakih UUID, tako da se UUIDv7 obremenitve z zapisi porazdelijo med imenike:

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

SegmentedLog

SegmentedLog je namenjen zapisom, ki jih pogosto dodajamo, branju s kazalcem in branju časovnih razponov. Zapisi se shranijo v aktivni segment, po ena vrstica za vsak zapis v obliki <byte-length>\t<xxh32>\t<json> (glejte Atomičnost). Datoteke segmentov imajo pripono .ndjson, vendar zaradi predpone z dolžino in kontrolno vsoto običajna orodja NDJSON ne morejo brati njihove vsebine; zapise preberite nazaj z stream() ali z gradnikom poizvedb. Polni segmenti se zamenjajo z zapečatenimi segmenti z redkimi indeksi.

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

Queue

Queue je namenjen trajnim čakalnim vrstam z visoko prepustnostjo. Dodajanje, prevzem, dokončanje, ponovno uvrščanje in čiščenje zapisujejo dogodke, ki se samo dodajajo, v queue.log; ob odprtju čakalne vrste se njeno stanje ponovno predvaja v pomnilnik. Paketne metode porazdelijo stroške zaklepanja in izpiranja med več opravil. Dnevnik se veča z vsakim dogodkom; stats() poroča o trenutni velikosti v bajtih.

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

Trajnost in sočasnost

storh objavlja zapise datotek z atomsko preimenovanjem v ciljnem imeniku. Ob ponovnem odprtju in repair() se odstranijo opuščene začasne datoteke, katerih lastniški proces ne deluje več, aktivne začasne datoteke zapisovalcev pa ostanejo nedotaknjene. Oznake zapisovalcev omogočajo stalnočasovno preverjanje, zato se zbirka ob odprtju pregleda samo po sesutju.

SegmentedLog in Queue zapisujeta vrstice dnevnika z zaščito dolžine in kontrolne vsote. Ob ponovnem odprtju ali popravilu se odtrgani zaključki odrežejo do zadnjega potrjenega dogodka, stanje v pomnilniku pa se znova zgradi iz trajne vsebine dnevnika. Zapisi v dnevnik in čakalno vrsto so serializirani z zaklepi datotečnega sistema.

Spremembe DocStore se serializirajo z zaklepom za pisanje na ravni zbirke, zato se datoteke zapisov in sekundarni indeksi posodobijo kot ena meja doslednosti med procesi. Sočasno zapisovanje različnih ID-jev zapisov je varno ob enakih predpostavkah za atomsko preimenovanje datotek. Pri sočasnih zapisih istega ID-ja zmaga zadnje preimenovanje.

storh izprazni medpomnilnike in sinhronizira ročice datotek pred dokončanjem preimenovanja ali dodajanja ter poskuša sinhronizirati nadrejene imenike po atomskih preimenovanjih. Trajnost ob izpadu napajanja je še vedno odvisna od tega, ali osnovni datotečni sistem in možnosti priklopa upoštevajo te sinhronizacije.