EF + Cache - thread safe (lock)

0

Cześć,
Chciałbym w WebApi skorzystać z Cache'a aby ograniczyć pobieranie danych z bazy danych.
Założenie jest proste:

  1. Gdy pobieram dane to najpierw weryfikuję czy są one w Cache'u - jeśli nie to pobieram je z bazy danych i aktualizuję cache.
  2. Gdy dodaję, usuwam lub modyfikuję dane (add / update / delete) - pobieram dane z Cache'u / DB dodaję do nich nowy obiekt lub modyfikuję już istniejący, następnie kasuję to co jest w cache'u -> dodaję zaktualizowane dane do Cache'a -> aktualizuję DB

Mam jednak kilka wątpliwości:

  1. Cache nie jest thread safe co oznacza, że zmiany powinny być dokonywane w bloku lock.
  2. W bloku lock powinna być również uwzględniona zmiana w bazie danych, ale tutaj wykorzystuję słowo kluczowe await a z tego co przeczytałem to nie powinno się używać await w blocku lock.

Moje pytania:
Ad 1: Czy słowo kluczowe lock blokuje mi wszystkie wątki - nawet takie, które nie pracują na zmiennych, które są wykorzystane w bloku lock? - czyli zablokuje mi całe API?
Ad 2: W jaki sposób można połączyć lock w zakresie zmiany w Cache'u z aktualizacją danych w DB, gdy korzystam ze słowa await?

0

pobieram dane z Cache'u / DB dodaję do nich nowy obiekt lub modyfikuję już istniejący, następnie kasuję to co jest w cache'u -> dodaję zaktualizowane dane do Cache'a -> aktualizuję DB

Czemu aż tyle tych updatów?

Update w db
Usunięcie z cache lub nadpisanie w cache danymi ze źródła (db) - mniejsze prawdopodobieństwo rozjazdu cache/db

Ad 1:czyli zablokuje mi całe API?

Zależy gdzie go postawisz, wydaje mi się że gdybyś go dał w middleware, a nie np. akcji kontrolera to mogłoby się tak stać, ale tego chyba nie chcesz.

3

A co do użycia await w bloku lock to jest alternatywa
Klasa SemaphorSlim https://docs.microsoft.com/en-us/dotnet/api/system.threading.semaphoreslim?view=net-6.0
Szybki tutorial https://www.youtube.com/shorts/mr8kdAauc7E

0

Locka nie użyjesz z awaitem. Ja bym się nie bawił w ogóle w takie rzeczy jak cacheowanie danych z bazy. Nie wydaje mi się, żeby poprawiło to wydajność lub działanie aplikacji. Obecne bazy są na tyle wydajne, że takie rzeczy raczej są bez sensu.

0

@szydlak:

Obecne bazy są na tyle wydajne, że takie rzeczy raczej są bez sensu.

dziwna opinia, przecież to wszystko zależy.

0

Ok, dziękuję za pomoc. Faktycznie SemaphoreSlim chyba rozwiąże problem. Wstępne testy wykazały, że zatrzymanie takiego obiektu nie blokuje całej aplikacji - chociaż będę musiał to jeszcze potwierdzić.

szydlak napisał(a):

Locka nie użyjesz z awaitem. Ja bym się nie bawił w ogóle w takie rzeczy jak cacheowanie danych z bazy. Nie wydaje mi się, żeby poprawiło to wydajność lub działanie aplikacji. Obecne bazy są na tyle wydajne, że takie rzeczy raczej są bez sensu.

Pobieranie danych z dysku zawsze będzie wolniejsze niż pobieranie z RAM-u. Jedną z rzeczy, które chcę przechowywać w cache'u to tablica Userów + ich wszystkie konfiguracje aplikacji (każdy user może sobie wiele rzeczy z customizować) + dostępy userów do modułów oraz poszczególnych funkcji w modułach etc., ewentualnie inne rzeczy z nimi związane. Wydobywanie tego za każdym razem z bazy jest trochę bez sensu, a jest tutaj sporo zależności. A muszę to robić za każdym razem, gdy generuję JWT, więc dosyć często.

Jeszcze na koniec takie pytanie czy ktoś używał klasy CacheSignal? W dokumentacji MS niewiele o tym piszą, ale jest ona wykorzystywana w przykładzie zamiast SemaphoreSlim - piszą tylko, że opiera się na SemaphoreSlim. No i zastanawiam się jakie korzyści daje ta klasa w przeciwieństwie do SemaphoreSlim?

0
Kofcio napisał(a):

Ok, dziękuję za pomoc. Faktycznie SemaphoreSlim chyba rozwiąże problem. Wstępne testy wykazały, że zatrzymanie takiego obiektu nie blokuje całej aplikacji - chociaż będę musiał to jeszcze potwierdzić.

szydlak napisał(a):

Locka nie użyjesz z awaitem. Ja bym się nie bawił w ogóle w takie rzeczy jak cacheowanie danych z bazy. Nie wydaje mi się, żeby poprawiło to wydajność lub działanie aplikacji. Obecne bazy są na tyle wydajne, że takie rzeczy raczej są bez sensu.

Pobieranie danych z dysku zawsze będzie wolniejsze niż pobieranie z RAM-u. Jedną z rzeczy, które chcę przechowywać w cache'u to tablica Userów + ich wszystkie konfiguracje aplikacji (każdy user może sobie wiele rzeczy z customizować) + dostępy userów do modułów oraz poszczególnych funkcji w modułach etc., ewentualnie inne rzeczy z nimi związane. Wydobywanie tego za każdym razem z bazy jest trochę bez sensu, a jest tutaj sporo zależności. A muszę to robić za każdym razem, gdy generuję JWT, więc dosyć często.

Jeszcze na koniec takie pytanie czy ktoś używał klasy CacheSignal? W dokumentacji MS niewiele o tym piszą, ale jest ona wykorzystywana w przykładzie zamiast SemaphoreSlim - piszą tylko, że opiera się na SemaphoreSlim. No i zastanawiam się jakie korzyści daje ta klasa w przeciwieństwie do SemaphoreSlim?

No zapewne z ramu bedzie szybciej. Pytanie tylko bedziesz w stanie zauwazyc roznice? Blokowanie watkow tez ma swoj narzut

1

@Kofcio:

Cache nie jest thread safe co oznacza, że zmiany powinny być dokonywane w bloku lock.

Something is no yes. Nie macie jakiejś biblioteki sensownej do cachy w C#? Takiej która by się opierałaby chociażby o ConcurrentDictionary?

Blokowanie watkow tez ma swoj narzut

Ale to nie wina cachy, tylko złego wyboru implementacji. Zakłądając że ConcurrentDictionary działa podobnie jak ConcurrentHashMap z Javy, to nie widzę powodow żeby blokowac.

5
  1. Samodzielne implementowanie keszowania przez lock czy SemaphoreSlim brzmi jak pomysł na stracenie sporej ilości czasu na stworzenie czegoś, co będzie działać marnie.
  2. Baza danych keszuje dane.
  3. Można ustawić cache na endpoincie za pomocą atrybutu ResponseCache.
  4. Cache na EF też można zrobić np. przy pomocy jakiejś istniejącej, sprawdzonej i przetestowanej przez tysiące ludzi biblioteki jak EF Plus.
  5. A przede wszystkim, zanim się zacznie "poprawiać" wydajność trzeba najpierw zmierzyć, że faktycznie problemy z wydajnością są i stwierdzić, co jest ich przyczyną. Bo jak na razie, to to wszystko jak mocne teoretyzowanie w temacie.
1

a w ogole o jakims Cache myslales? Bo jak in-memory to dla mnie jest to troche bez sensu (zakladam, że jest X replik), no chyba, że przy starcie appki napelnialbys cache. Nie znam sie na C# i EF ale no jak to ORM zazwyczaj cos ma https://entityframework.net/ef-cache

Sama implementacja cache może byc thread safe, ale jak uzyjesz struktury danych, ktora nie jest thread safe to i tak moze byc problem (np. zanim cos wlozysz do cache)

Jakie jest obciążenie aplikacji i bazy? Widać coś w monitoringu? cpu, memory, connection poole? buffer cache hit ratio w bazie?

0

Świeżutki kesz:

1 użytkowników online, w tym zalogowanych: 0, gości: 1