készletkezelés

Fórumok

Sziasztok!

Tipikus készlekezelési probléma.
Adott egy termék és a következő lépések:
1. user A olvas, 10 db van belőle hozzá szeretne tenni 5 db-ot
2. user B olvas, 10 db van belőle el szeretne venni 2 db-ot
3. user B visszaírja az adtot 10-2 = 8
4. user A visszírja az adatot 10+5 = 15
A készle hibásan 15 lesz, pedig 13-nak kellene lenni.

A környezet amiben meg van valósítva Java EE.

A problémát úgy oldjuk meg, hogy soba állítjuk ezeket a kéréseket, és egy singleton osztály kezeli a készletet. Így atombiztosan működik, mert az egyes ejb-k a singleton sztálynak adják át, hogy az xy azonosítójú terméknek a készletét változtasd meg x-el. Mivel csak ő olvassa és írja az adatokat azért az adat mindíg konzisztens lesz.

Ez a megoldás bőven kiszolgálja a jelenlegi igényeket, viszont ha drasztikusan meg fog növekedni a felhasználószám, akkor gond lesz a kérések sorba állítása, drasztikusan csökkenhet a teljesítmény.
Mert ha van A, B, C, D, E termék készletének módosítására kérés, azok mehetnek nyugodtan párhuzamosan, ellenben ha 10 db A-termék készletének módosítására van igény, akkor azok lehetnek sorba állítva.

Erre a problémára van valami design pattern?

Hozzászólások

Bár én már évek óta nem programozok komolyabban, de az ilyesmit nem adatbázis szinten illik megoldani? Hogy X másodperce kiolvastál 10db-ot, az senkit nem érdekel. Beírni nem a 15-öt kell, hanem db query-ben "mennyiseg=mennyiseg+5"

(igaz, arra nem tértél ki, hogy az adatot miben tárolod)

----------------------------------^v--------------------------------------
"Probléma esetén nyomják meg a piros gombot és nyugodjanak békében!"

Csak egy ellenpélda: MySQL-ben amit írtál, mint egy fajta inkrementálás, nem működik megfelelően, kiolvasás és módosítás külön történik meg, nem tranzakcióban. Elég ritka állítólag, ezért el is terjedt, hogy jó ez így, aztán néha mégsem. Megoldásként ott vannak a tranzakciók, lock-olások, amik gyakorlatilag egy adatbázis oldali singleton-nak felelnek meg. A lényeg, hogy valahol lesz egy egyszálúsítás, ami bottleneck lehet majd, és én is nyitott vagyok mindenféle design pattern-re :).

Tipikus race-conditionnak latszik, tehat valahol mindenkepp "egyszalusitani" kell a feldolgozast.

1. Message queue, ahol csak annyi utazik, hogy [A:+5, B:-2], es ezeket szepen sorban feldolgozza a program.

2. https://en.wikipedia.org/wiki/Double-checked_locking

Szerk: ha a vegeredmeny fontos a userek szempontjabol ("ha csak 3 maradna, akkor inkabb csak 1-et veszek ki, nem 2-t"), akkor ellenorizni kell, hogy valtozott-e a kiindulo allapot amiben a dontest hoztak ("mar nem 10 van osszesen, csak 5"), es ha igen, akkor errort, warningot kell visszacsatornazni a UI, a user fele.

Ezt játszották sok raktárkezelőben, hogy session elején állapot mentése, session végén összehasonlítás, volt-e azóta változás, csak ne nézd meg a user-ek arcát, amikor ezt értelmezni próbálják, főleg úgy, hogy valójában ilyenkor elég, ha a "gép" újrapróbálja, és nem írja ki, hogy "10 van, de lehet az adat elavult", hanem ilyen esetben már csak az 5-öt írja ki (háttérben persze elévülés miatt gyakorlatilag újraszámol, és reménykedik, hogy a számítás alatt nem változik mégegyszer).

Fizikailag is kérdéses, hogy mennyire lehetséges pl. foglalásokat csinálni, sok megoldást láttam erre is, de mindben volt valami eszetlen ritkán előforduló, ezért alig megtalálható hiba, aminél jóval olcsóbb a singleton alá nagyobb teljesítményt rakni.

Ha DB, akkor serializable transaction.
Vagy AtomicReference (meg AtomicInteger, ...) és compareAndSet. Csak akkor változtat, ha az általad ismert adat van benne.

Önmagában az nem baj, ha van egy singleton, aki garantálja a sorosítást. Az adatbázisban megvalósítva is sorosítva lennének a kérések. Inkább baj a rendelkezésre állás. Ha több szerver irányába akarsz skálázódni, és miért ne akarnál, akkor több energiát bele kell ebbe ölni. Egyszerűbb adatbázissal, pl. optimista konkurenciakezeléssel megoldani. Ha egyszerre fut le két read+write tranzakció, az egyik inkonzisztens lesz, el kell szállnia, meg kell ismételni.

Létezik olyan megoldás is, hogy a mennyiséget több sor összegeként tárolod, és 2 elem elvétele esetén nem a korábbi sorokat módosítod, hanem beszúrsz egy új sort -2 értékkel. Csak ebben a konkrét esetben gond lenne, ha mínuszba mennél.

Remélem, nem túl nagy butaságot írok, de skálázhatod a singletont is termékek szerint.

Ha A-ra nagyon nagy a kereslet, akkor ő kap egy külön instance-t, a B, C, D meg futhat egy másikban. Egy message queue pedig tudja routolni neked, hogy melyik instance mit kapjon.
Persze ez is el tud dugulni.
--
HUP Firefox extension | Hupper hibajelentés

A legjobb megoldás amit eddig láttam erre az volt, hogy egy programrész azt figyelte, hogy melyik termékre mennyi message van a sorban, amire x fölötti volt, arra nyitott egy instance-ot, és valami alapján prioritást is kaphattak ezek a instance-ok (ennek kezelésében nem vagyok biztos, hogy hogy zajlott, a message queue-ben blokkosított egybe tranzakciókat, és csak az abszolút értéket érvényesítette, előrébb vett egy terméket amire sokan várnak ha csak arra várnak a kosár tartalmát illetően, ..., nem tudom melyik megoldásokat választották).

Megjegyzés-tanács: UI oldalról is tudjátok kezelni a kérdést, gondolom azért vetetted fel a kérdést, mert fontos a darabszám visszajelzése, de érdemes megnézni, hogy mindig az-e, pl. ahol nagy eséllyel nem fog elfogyni a készlet, ...

message queue-k

--
Gábriel Ákos

Egyrészt "fura" megoldás mikor manapság az ilyesmit mindenki adatbáziskezelőben szokta csinálni. Ahogy a többiek is írták.

Másrészt viszont szerintem érdemes kicsit jobban utánaszámolni, hogy mégis hány ilyen műveletet tud egy mag megcsinálni? Nehezen tudok elképzelni olyan raktárat, aminek a ki-beviteli műveleteit ne lehetne egy szálon megvalósítani. Vagy nagyon hülyén van megvalósítva a program, vagy alaptalan a félelmed. Mértél? Vannak konkrét számaid?

Szerintem tranzakciót az adatbázis kezeljen.