У нас виникла ситуація, коли мені доводиться стикатися з масовим напливом подій, що надходять на наш сервер, в середньому близько 1000 подій в секунду (пік може бути ~ 2000).
Проблема
Наша система розміщується на Heroku та використовує відносно дорогий БД Heroku Postgres , який дозволяє максимум 500 підключень до БД. Ми використовуємо пул з'єднань для підключення з сервера до БД.
Події надходять швидше, ніж може працювати пул з'єднань з БД
Проблема в нас полягає в тому, що події відбуваються швидше, ніж пул з'єднань може впоратися. До того моменту, коли одне з'єднання закінчило мережевий перехід із сервера в БД, тож воно може бути відпущене назад до пулу, і не n
відбудеться більше додаткових подій.
Врешті-решт події складаються, очікуючи збереження, і тому що в пулі немає доступних з'єднань, вони вичерпуються, і вся система виводиться з ладу.
Ми вирішили надзвичайну ситуацію, випускаючи від клієнтів високочастотні події повільнішими темпами, але ми все ще хочемо знати, як поводитися з цими сценаріями у випадку, якщо нам потрібно обробляти ці високочастотні події.
Обмеження
Інші клієнти можуть одночасно читати події
Інші клієнти постійно вимагають прочитати всі події певним ключем, навіть якщо вони ще не збережені в БД.
Клієнт може запитувати GET api/v1/events?clientId=1
та отримувати всі події, надіслані клієнтом 1, навіть якщо ці події ще не виконані, зберігаючи в БД.
Чи є приклади «класної кімнати», як з цим боротися?
Можливі рішення
Замовте події на нашому сервері
Ми можемо передбачити події на сервері (з чергою максимальна сумісність 400, щоб пул зв’язків не закінчувався).
Це погана ідея, оскільки:
- Він з'їсть доступну пам'ять сервера. Складені захоплені події будуть використовувати велику кількість оперативної пам’яті.
- Наші сервери перезапускаються раз на 24 години . Це жорстка межа, нав'язана Героку. Сервер може перезапуститись, коли події вмикаються, внаслідок чого ми втрачаємо події, що перебувають у спаді
- Він вводить стан на сервері, тим самим шкода масштабованості. Якщо у нас є налаштування декількох серверів, і клієнт хоче прочитати всі зафіксовані + збережені події, ми не будемо знати, на якому сервері живуть події, котрі стосуються.
Скористайтеся окремою чергою повідомлень
Я припускаю, що ми могли б використовувати чергу черг (наприклад, RabbitMQ ?), Де ми перекачуємо повідомлення в ній, а на іншому кінці є ще один сервер, який займається збереженням подій у БД.
Я не впевнений, чи дозволяють черги повідомлень запитувати події, пов’язані з запитом (які ще не збереглися), тому якщо інший клієнт хоче прочитати повідомлення іншого клієнта, я можу просто отримати збережені повідомлення з БД та очікувані повідомлення з черги і з'єднати їх разом, щоб я міг відправити їх назад клієнту із запитом на читання.
Використовуйте кілька баз даних, кожна з яких зберігає частину повідомлень на центральному сервері координатора БД для управління ними
Ще одне рішення, яке ми маємо, - це використання декількох баз даних з центральним "координатором БД / балансиром завантаження". Отримавши подію, цей координатор вибере одну з баз даних, на яку написати повідомлення. Це повинно дозволяти нам використовувати декілька баз даних Heroku, таким чином збільшуючи межу з'єднання до 500 x кількості баз даних.
Після запиту читання, цей координатор може видати SELECT
запити до кожної бази даних, об'єднати всі результати та відправити їх тому клієнту, який запросив прочитане.
Це погана ідея, оскільки:
- Ця ідея звучить як ... гм .. надмірна інженерія? Був би кошмаром і для керування (резервне копіювання тощо). Складно створювати та підтримувати, і якщо це абсолютно не потрібно, це звучить як порушення KISS .
- Він жертвує послідовністю . Здійснення транзакцій у кількох БД - це безрезультатно, якщо ми з цією ідеєю.
ANALYZE
над запитами, і вони не є проблемою. Я також створив прототип, щоб перевірити гіпотезу пулу підключень і переконався, що це справді проблема. База даних і сам сервер живуть на різних машинах, отже, і затримка. Крім того, ми не хочемо відмовлятися від Heroku, якщо це абсолютно не потрібно, не турбуючись про розгортання - це величезний плюс для нас.
select null
на 500 підключень. Б'юсь об заклад, ви виявите, що пул з'єднань тут не проблема.