Motorok
Motorválasztás
Mindhárom motor ugyanazt tárolja: egy UUIDv7 azonosítót és egy adattömböt, de másképp rendezi el őket a lemezen. Ez az elrendezés határozza meg, mire alkalmasak az egyes motorok:
| DocStore | SegmentedLog | Queue | |
|---|---|---|---|
| Lemezen | rekordonként egy JSONC fájl, UUID-farok szerinti particionálással | csak hozzáfűzhető szegmensek, hossz- és ellenőrzőösszeg-előtaggal ellátott JSON sorokkal, valamint jegyzékkel | egy csak hozzáfűzhető eseménynapló |
| Írás | atomi fájlcserével rekordként | ellenőrzőösszeggel védett hozzáfűzések; a felülírások és törlések új bejegyzést fűznek hozzá, a helyet a(z) compact() szabadítja fel |
ellenőrzőösszeggel védett hozzáfűzés minden sorsoreseménynél |
| Olvasás | get() azonosító alapján, indexelt lekérdezések |
kurzor- és időtartomány-pásztázás, get() az állapotindex alapján |
claim() sorba állítási sorrendben |
| Ideális | pontszerű olvasásokhoz és indexelt keresésekhez bármilyen gyűjteményméretnél | sok hozzáfűzésű adatfolyamokhoz, körülbelül 1M rekordig | tartós munkafeladatok folyamatok közötti kiosztásához |
| Hátrány | nem indexelt mezők pásztázása körülbelül 10k rekord felett | gyakori, egyrekordos átírások (a régi verziók a tömörítésig helyet foglalnak) | nem általános rekordtár; a hasznos adatok a feladat befejezése után törlődnek |
A(z) DocStore és a(z) SegmentedLog ugyanazt a gördülékeny
lekérdezési API kezelőfelületet használja: ugyanaz a where() lánc mindkét
motoron fut, és csak a végrehajtás tér el. A(z) DocStore a másodlagos
indexeiből is tud választ adni, míg a(z) SegmentedLog szegmensek pásztázása közben
szűr. A(z) Queue igénylésalapú, nem kérdezhető le: a feldolgozók az
következő feladatot claim(), nem pedig szűrik a feladatokat.
Ökölszabályok:
- Egyenként lekért, frissített vagy törölt, illetve mező alapján lekérdezhető rekordok:
DocStore. Adjon hozzá indexeket a szűréshez használt mezőkhöz. - Többnyire hozzáfűzött, majd sorrendben vagy időablak szerint visszaolvasott események vagy verziók:
SegmentedLog. - A létrehozóktól a feldolgozó folyamatokhoz továbbított feladatok:
Queue. A lefoglalás és a befejezés több folyamat között is biztonságos, aclaimMany()/completeMany()pedig megosztja a zárolás és az fsync költségét. - Összekapcsolások, keresés vagy jelentéskészítés bármely gyűjteményen: adjon hozzá egy SQL tükröt. A fájlok maradnak a hiteles források; a tükör újraépíthető vetület SQLite vagy MySQL formátumban.
A motorok kombinálhatók: csak a gyökérkönyvtárat osztják meg, így egy alkalmazás ugyanazon tárolási gyökér alatt tarthatja a tartalmat egy DocStore-ban, az auditnaplót egy SegmentedLog-ban, a háttérfeladatokat pedig egy Queue-ban.
DocStore
A DocStore tartós rekordok közvetlen lekérésére és egyszerű bejárására szolgál. Rekordonként egy JSONC fájlt ír:
$store = new Storh\DocStore($root, 'articles');
$record = $store->put(['slug' => 'hello', 'published' => true]);
$same = $store->get($record->id());
A rekordfájlok elosztása a UUID utolsó karakterei alapján történik, így a UUIDv7 írási terhelési csúcsok több könyvtár között oszlanak meg:
articles/data/2d/018bcfe5-6800-7000-8000-2d5dba6274da.jsonc
SegmentedLog
A SegmentedLog hozzáfűzés-intenzív rekordokhoz, kurzoros olvasáshoz és időtartomány szerinti olvasáshoz készült. A rekordok az aktív szegmensbe kerülnek, rekordonként egy sorban, <byte-length>\t<xxh32>\t<json> formátumban (lásd: Atomi működés). A szegmensfájlok kiterjesztése .ndjson, de a hossz- és ellenőrzőösszeg-előtag miatt a hagyományos NDJSON-eszközök nem tudják olvasni őket; a rekordokat a stream() vagy a lekérdezésépítő segítségével olvassa vissza. A megtelt szegmensek lezárt szegmensekké válnak, ritka indexekkel.
$events = new Storh\SegmentedLog($root, 'events');
$events->put(['type' => 'user.created']);
$events->compact();
Sor
A Queue nagy áteresztőképességű, tartós feladatsorokhoz készült. A sorba állítás, lefoglalás, befejezés, újrasorba állítás és törlés csak hozzáfűzött eseményeket ír a queue.log fájlba; a sor állapota a sor megnyitásakor kerül visszajátszásra a memóriába. A kötegelt metódusok sok feladat között osztják el a zárolás és a kiürítés költségét. A napló minden eseménnyel növekszik; a stats() jelenti az aktuális méretét bájtban.
$queue = new Storh\Queue($root, 'mail');
$queue->enqueue(['to' => 'person@example.test']);
$job = $queue->claim();
Tartósság és párhuzamosság
A storh atomi átnevezéssel teszi közzé a fájlírásokat a célkönyvtáron belül. Újranyitáskor és a repair() eltávolítja azokat az elhagyott ideiglenes fájlokat, amelyek tulajdonos folyamata már leállt, az aktív írók ideiglenes fájljait viszont érintetlenül hagyja. Az írójelölők révén az ellenőrzés állandó idejű, így a tároló megnyitásakor csak összeomlás után vizsgálja át a gyűjteményt.
A SegmentedLog és a Queue hosszal és ellenőrzőösszeggel védett naplósorokat ír. Újranyitáskor vagy javításkor a rendszer a részlegesen írt napló végét az utolsó véglegesített eseménynél csonkolja, majd a tartós naplótartalomból újraépíti a memóriabeli állapotot. A napló- és sorírásokat fájlrendszer-zárolások sorosítják.
A DocStore módosításait gyűjteményszintű írási zárolás sorosítja, így a rekordfájlok és a másodlagos indexek egyetlen konzisztenciahatár mentén frissülnek a folyamatok között. A különböző rekordazonosítókra irányuló párhuzamos írások biztonságosak, ha a fájlrendszer biztosítja az atomi átnevezés feltételeit. Ugyanazon azonosító egyidejű írásai közül az utoljára átnevezett érvényesül.
A storh kiüríti és fsync-kel szinkronizálja a fájlleírókat az átnevezés vagy a hozzáfűzés befejezése előtt, és az atomi átnevezések után megkísérli az fsync-et a szülőkönyvtárakon. Az áramkimaradással szembeni tartósság továbbra is attól függ, hogy az alapul szolgáló fájlrendszer és a csatolási beállítások betartják-e ezeket a szinkronizálásokat.