Mamy sytuację, w której mam do czynienia z ogromnym napływem zdarzeń przychodzących na nasz serwer, średnio przy około 1000 zdarzeń na sekundę (szczyt może wynosić ~ 2000).
Problem
Nasz system jest hostowany na Heroku i używa stosunkowo drogiej bazy danych Heroku Postgres , która pozwala na maksymalnie 500 połączeń DB. Korzystamy z pul połączeń do łączenia się z serwera do bazy danych.
Zdarzenia przychodzą szybciej niż pula połączeń DB może obsłużyć
Problemem jest to, że zdarzenia przychodzą szybciej niż pula połączeń jest w stanie obsłużyć. Zanim jedno połączenie zakończy połączenie sieciowe z serwera do bazy danych, aby można je było zwolnić z powrotem do puli, n
pojawiły się więcej niż dodatkowe zdarzenia.
W końcu zdarzenia gromadzą się, czekając na zapisanie, a ponieważ w puli nie ma dostępnych połączeń, przekroczą limit czasu i cały system przestanie działać.
Rozwiązaliśmy sytuację awaryjną, emitując obrażające zdarzenia o wysokiej częstotliwości w wolniejszym tempie od klientów, ale nadal chcemy wiedzieć, jak radzić sobie z tymi scenariuszami w przypadku, gdy musimy poradzić sobie z tymi zdarzeniami o wysokiej częstotliwości.
Ograniczenia
Inni klienci mogą chcieć czytać zdarzenia jednocześnie
Inni klienci stale żądają odczytania wszystkich zdarzeń z określonym kluczem, nawet jeśli nie są jeszcze zapisane w bazie danych.
Klient może GET api/v1/events?clientId=1
wysłać zapytanie i uzyskać wszystkie zdarzenia wysłane przez klienta 1, nawet jeśli te zdarzenia nie zostały jeszcze zapisane w bazie danych.
Czy są jakieś „klasowe” przykłady, jak sobie z tym poradzić?
Możliwe rozwiązania
Kolejkuj wydarzenia na naszym serwerze
Możemy kolejkować zdarzenia na serwerze (z kolejką o maksymalnej współbieżności wynoszącej 400, aby pula połączeń się nie wyczerpała).
To zły pomysł, ponieważ:
- Zużyje dostępną pamięć serwera. Ułożone w kolejce zdarzenia zużyją ogromne ilości pamięci RAM.
- Nasze serwery restartują się raz na 24 godziny . Jest to twardy limit narzucony przez Heroku. Serwer może się zrestartować, gdy zdarzenia są kolejkowane, co powoduje utratę tych zdarzeń.
- Wprowadza stan na serwerze, co szkodzi skalowalności. Jeśli mamy konfigurację z wieloma serwerami, a klient chce odczytać wszystkie kolejkowane i zapisane zdarzenia, nie będziemy wiedzieć, na którym serwerze kolejkowane zdarzenia są aktywne.
Użyj osobnej kolejki komunikatów
Zakładam, że moglibyśmy użyć kolejki komunikatów (np. RabbitMQ ?), W której pompujemy wiadomości w niej, a na drugim końcu jest inny serwer, który zajmuje się tylko zapisywaniem zdarzeń na DB.
Nie jestem pewien, czy kolejki komunikatów umożliwiają odpytywanie zakolejkowanych zdarzeń (które nie zostały jeszcze zapisane), więc jeśli inny klient chce odczytać wiadomości innego klienta, mogę po prostu pobrać zapisane wiadomości z bazy danych i oczekujące wiadomości z kolejki i łączymy je ze sobą, dzięki czemu mogę wysłać je z powrotem do klienta żądania odczytu.
Korzystaj z wielu baz danych, z których każda zapisuje część wiadomości za pomocą centralnego serwera koordynującego DB, aby nimi zarządzać
Innym rozwiązaniem, które mamy, jest wykorzystanie wielu baz danych z centralnym „koordynatorem DB / modułem równoważącym obciążenie”. Po otrzymaniu zdarzenia koordynator wybierze jedną z baz danych, do których napisze wiadomość. Powinno to pozwolić nam korzystać z wielu baz danych Heroku, zwiększając w ten sposób limit połączeń do 500 x liczby baz danych.
Po zapytaniu dotyczącym odczytu koordynator może SELECT
wysyłać zapytania do każdej bazy danych, scalać wszystkie wyniki i odsyłać je z powrotem do klienta, który zażądał odczytu.
To zły pomysł, ponieważ:
- Ten pomysł brzmi jak ... hmm ... nadmierna inżynieria? Byłby to również koszmar do zarządzania (kopie zapasowe itp.). Kompilacja i utrzymanie jest skomplikowane i chyba, że jest to absolutnie konieczne, brzmi jak naruszenie zasad KISS .
- Poświęca spójność . Robienie transakcji na wielu bazach danych jest nie do przyjęcia, jeśli pójdziemy z tym pomysłem.
ANALYZE
same zapytania i nie stanowią one problemu. Zbudowałem również prototyp, aby przetestować hipotezę puli połączeń i zweryfikowałem, że to rzeczywiście problem. Baza danych i sam serwer żyją na różnych komputerach, stąd opóźnienie. Ponadto nie chcemy rezygnować z Heroku, chyba że jest to absolutnie konieczne, dlatego nie martwienie się o wdrożenia jest dla nas ogromną zaletą .
select null
na 500 połączeń. Założę się, że okaże się, że pula połączeń nie stanowi problemu.