Wpis z mikrobloga

#programowanie #java #systemdesign
Mirki, czytam sobie różne pytania z system design i natrafiłem na coś takiego:

System do wydawania kuponów gdzie miałem się skupić na funkcjonalności do zapewnienie, że jeden kupon może być użyty tylko określoną liczbę razy. Z takich wymagań konkretnych to wysokie availability 99,999, response poniżej 100ms, założenie że będzie 2000tps


Jakbyście do tego podeszli? ChatGPT sugeruje

@Transactional
@lock(LockModeType.PESSIMISTIC_WRITE)
@query("SELECT k FROM Kupon k WHERE k.kod = :kod")
Optional<Kupon> findByKodForUpdate(String kod);

i

@Transactional
public boolean wykorzystajKupon(String kod) {
return kuponRepository.findByKodForUpdate(kod).map(kupon -> {
if (kupon.getWykorzystania() < kupon.getLimitUzyc()) {
kupon.setWykorzystania(kupon.getWykorzystania() + 1);
kuponRepository.save(kupon);
return true;
} else {
return false;
}
}).orElse(false);
}

już pomijając polskie nazwy i save na już persisted encji
Czy ma to sens, czy jest to wystarczające?
Zakładając, że tak - to czy byłoby to wydajne i skalowane? W NoSQLowej bazie byłby pewnie łatwiej o wydajność, ale czy da się wtedy zrobić takiego locka?
  • 29
  • Odpowiedz
@Patres: ale availability nie zalezy od kodu biznesowego. response time tez jest w sumie zalezne od wielu czynnikow, nie tylko od kodu.

W takim zadaniu sam kod to najmniejszy problem. Pesimistic lock powoduje ze inne watki beda czekac co sie nie skaluje dobrze.
  • Odpowiedz
  • 0
Widzę, że śmieszki z chata ale po odpowiedziach widać, że jest on dużo bardziej konkretny niż wszystkie odpowiedzi na wykopie xD

@newbie_235235: Dokładnie tak jest, ale nie przybliża mnie to do docelowego rozwiązania - skoro "Pesimistic lock powoduje że inne watki beda czekac co sie nie skaluje dobrze" <w pełni się z tym zgadzam> oznacza też to, że response poniżej 100ms może być mniej osiągalny przy większej ilości. Dlatego zastanawiam się
  • Odpowiedz
  • 6
@Patres nie lepiej to ogarnąć pojedynczym update'em SQL? Coś jak "UPDATE coupon SET usagesLeft = usagesLeft - 1 WHERE Id = ? AND usagesLeft > 0"
I tylko sprawdzić czy się udało z odpowiedzi bazy.
Całą robotę zapewnienia spójności zrzucamy na bazę. A jak to za wolno działa to rozrzucić kupony przez sharding na wiele baz danych.
  • Odpowiedz
@Patres no ale odpowiedź poniżej 100ms to znaczy, że musisz wywalić request jak minie więcej niż 100ms? Generalnie żeby nie wiem jakie robić optymalizacje, to zawsze może się coś wydarzyć, co wydłuży czas odpowiedzi. Jeśli to twarde wymaganie, to trzeba do tego jakoś inaczej podejść - mierzyć czas wykonania czy inne sztuczki. Brzmi jak rtos, ale nic o tym nie wiem.
  • Odpowiedz
via Android
  • 0
@VlIN Dzięki! Wydaje mi się że to podejście ma taką istotną wadę że sprawdza jednocześnie czy może być kupon użyty i jednocześnie go zmieniać jeżeli tak. Po wywołaniu tego query użytkownika nie wie czy kupon został naliczony czy nie a to ma już duże znaczenie biznesowe
  • Odpowiedz
  • 4
@Patres Query zwróci liczbę zaktualizowanych rekordów. Będziemy wiedzieli czy się udało czy nie sprawdzając czy wynik Query > 0
  • Odpowiedz
@ElTalento: jedną vmke wyklucza wymaganie HA.
@Patres: Co do zadania to przypadek wręcz książkowy dla Cassandra / Datastax Astra. Rozproszona architektura, shared nothing, dostępność 99,9999 a 2000 TPS to pikuś (ta baza robi 350 tys zapytań na sekundę na... laptopie). Tickety oczywiście aktualizujesz jednym transakcyjnym UPDATE I baza ogarnie za Ciebie spójność automatycznie. Ty po stronie aplikacji tylko musisz sprawdzić wynik zapytania czy update się udał.
  • Odpowiedz
Tickety oczywiście aktualizujesz jednym transakcyjnym UPDATE I baza ogarnie za Ciebie spójność automatycznie.


@Krolik: Czyli dodawałbyś adnotację @Transactional? Jeżeli tak, to jaki poziom izolacji, ja bym dał dla pewności - isolation repeatable read.
  • Odpowiedz
@exori_vis: pytania o system design nie dotyczą tego czy będziesz dawał jakaś adnotacje jakiegoś frameworka. Te pytania z zasady powinny być niezależne od języka / frameworka.
  • Odpowiedz
@Krolik: Dobra to inaczej. Jaki poziom izolacji trazsakcji (te poziomy są uniewersalne, uczy się m.in o nich na studiach) powinien być przy takim zapytaniu update? Jeżeli nie wiesz, to spoko, pytam z ciekawości ;)
  • Odpowiedz
  • 1
@Krolik: To ja też dopytam. Nie pracowałem na poważnie z NoSql bazami danymi, ale zawsze jak czytałem porównanie to było "Most of the NoSQL solutions sacrifice ACID compliance for performance and scalability.". Czy w takim przypadku Cassandra + " transakcyjnym UPDATE" nie są sprzecznością?
  • Odpowiedz
  • 1
@Krolik: To że transakcyjność to nie jest tylko ACID to wiem. Jednak większość źródeł jakie przeglądałem mówią wprost, że transakcyjność nie jest wspierana a jak już jest to at the row-level - a to nie jest wystraczające (musze zmienić wartość na kupony, ale też jednocześnie go obsłużyć - zmniejszyć wartość zamówienia). A jak już widzę jakieś źródła które twierdzą, że ta transakcyjność istnieje to wygląda mi bardziej na workaroundy niż docelowe
  • Odpowiedz