ITHub

Mi az event sourcing?

Mi az event sourcing?
Farkas Gábor
Farkas Gábor
| ~4 perc olvasás

Minden alkalmazás adatokkal dolgozik, amelyek aktuális állapota a felhasználó cselekvéseitől függően változik. Például egy egyszerű CRUD (create, read, update, delete) modellben egy tipikus folyamat, amikor először kiolvasunk valamilyen adatot az adatbázisból, változtatásokat végzünk rajta, majd az új értékeket elmentjük az adatbázisba. Ennek a klasszikus megoldásnak egy alternatívája az event sourcing.

Miért van erre szükség?

A jól ismert CRUD rendelkezik bizonyos korlátokkal. A közvetlenül az adatbázisban végrehajtott műveletek jelentős terhet róhatnak a rendszerünkre performancia szempontjából, amely igencsak ronthatja az alkalmazásunk skálázhatóságát.

Ha sok folyamatunk fut párhuzamosan, gyakran fordulhatnak elő ütközések az adatbázis frissítésekor, mivel a műveletek potenciálisan ugyanazon adatelemeken történnek.

Fontos azt is látni, hogy ha nincs az egész adatmodell felett egy naplózási mechanizmus, semmilyen módon nem tudjuk kinyerni a rendszerből a múltban történt műveleteket, az adatok története a múlté.

Az event sourcing tervezési mintában az adatokon végzett műveleteket közvetlenül tároljuk egy ún. "append-only" adatbázisban, amelyből a törlés nem megengedett, csak új rekordokat illeszthetünk be. Ez persze nem újkeletű dolog, gondoljunk csak például a bankszámlakivonatunkra.

Az adatok aktuális állapotát így értelemszerűen a múltban történt esemény rekonstrukciójával kapjuk meg. A bankszámlakivonatnál maradva: az aktuális egyenlegünk bármikor megállapítható, ha az összes múltbeli tranzakciót újra "lejátsszuk".

Előnyök és hátrányok

Az event sourcing megközelítésnek számos előnye van.

  • A jelenlegi állapot reprezentációja független a tárolt eseményektől.
  • A nyers adatok mellett a felhasználók szándékait is tároljuk, ami újfajta elemzésekre ad lehetőséget.
  • Kikerüljük az objektum-orientált és relációs modellek közti feloldhatatlan kompatibilitási problémát.
  • Az event sourcingban "ingyen" kapunk egy audit logot, amely garantáltan teljes, hiszen ha valamiről nincs rögzített esemény, az nem létezik.

Természetesen számolnunk kell a hátrányokkal is.

  • Felléphet bizonyos késleltetés a rekonstrukció és az új események beillesztése közt, amelynek eredményeképpen bizonyos esetekben nem az aktuális, hanem egy korábbi állapotot kapunk.
  • Egyelőre nincs egy standard, például az SQL-hez hasonló szabvány, amellyel az események lekérdezhetők az eseménytárból. Az egyetlen mód erre jelenleg az, hogy kikérjük az adott entitáshoz tartozó összes eseményt, és újra végrehajtjuk azokat az entitás eredeti állapotából kiindulva.
  • Ha egy entitáshoz sok esemény tartozik, az aktuális állapot kiszámítása egyre lassabb és lassabb lesz, és egy idő után jelentősen ronthatja az alkalmazásunk teljesítményét. Erre egy jó megoldás, ha bizonyos időközönként snapshotokat is tárolunk, hogy ne kelljen mindig a kezdeti állapotból indulni.

Bonyolult domainek esetén a pozitívumok sok esetben az event sourcing felé billentik a mérleg nyelvét. Egyszerű problémák esetén, vagy ahol nincs szükség audit bejegyzésekre, esetleges rollback műveletekre (viszont fontos például a real-time jelleg), nem biztos, hogy ez a legjobb választás. Akkor sem biztos, hogy érdemes használnunk, ha viszonylag kevés a konkurrens művelet a rendszerben, ami ütközésekhez vezehet.

Egy egyszerű példa

Ha az eddigiek kissé zavarosak lettek volna, nézzük meg egy egyszerű példán, hogy is néz ki a gyakorlatban az event sourcing. Képzeljünk el, hogy egy helyfoglaló rendszert tervezünk, tipikus felhasználói műveletek pedig egy szabad hely lefoglalása/lemondása.

Egy klasszikus CRUD adatmodellben valószínűleg lenne egy táblánk, amely tárolja a rendelkezésre álló helyeket, illetve hogy azokat ki foglalta le, például így:

-------------------------
| seat_id | reserved_by |
-------------------------
| 0000001 |             |
| 0000002 | user1       | 
-------------------------

Amikor user2 felhasználó lefoglalja a jelenleg szabad, 0000001 azonosítójú helyet, a tábla tartalma a következő lesz:

-------------------------
| seat_id | reserved_by |
-------------------------
| 0000001 | user2       |
| 0000002 | user1       | 
-------------------------

Kiderül aztán, hogy user1 mégsem tart igényt a helyre, így végrehajt egy lemondási műveletet. Ezután a tábla így fog kinézni:

-------------------------
| seat_id | reserved_by |
-------------------------
| 0000001 | user2       |
| 0000002 |             | 
-------------------------

Bár ismerjük az adatmodellünk aktuális állapotát, a múltbeli események elvesztek, hiszen ezekből az adatokból nem derül ki, hogy a 0000002 azonosítójú hely valaha user1-é volt.

Ezzel szemben ha event sourcingot alkalmaznánk, valami ilyesmi lenne a tárban:

----------------------------------
| event_type | seat_id | user_id |
----------------------------------
| reserve    | 0000002 | user1   |
| reserve    | 0000001 | user2   | 
| cancel     | 0000002 | user1   | 
----------------------------------

Ezekből az adatokból bármikor előállítható a klasszikus modellben közvetlenül tárolt aktuális állapot.

További olvasnivaló

Ezt a cikket természetesen szokás szerint csak egy végletekig leegyszerűsített bevezetésnek, kedvcsinálónak szántuk, akit mélyebben érdekel az event sourcing, érdemes áttanulmányoznia az alábbi linkeket: