Пробудження баз даних без мертвих з'єднань
Опубліковані застосунки на Mythex і їхні бази даних засинають, коли ними ніхто не користується. Коли застосунок прокидався, він міг тримати перший запит 10–12 секунд, бо намагався повторно використати з'єднання з базою, які померли, поки все спало. Тепер ми даємо застосункам Spring Boot налаштування бази, безпечні після сну, і вказуємо агенту будувати застосунки будь-якого стеку так само. На хостинговій платформі перший запит після пробудження скоротився з 12,7 до 2,3 секунди.
Чому застосунки і бази засинають
Опублікований застосунок Mythex працює на машині, яка призупиняється після кількох хвилин простою, а його база даних це серверлес-Postgres, що засинає після п'яти хвилин простою й прокидається з наступним запитом. Саме це утримує тихий застосунок дешевим: ви платите за те, що база справді використала, а застосунок, що спить, не витрачає ресурсів на обчислення. Ми писали про білінг з цього боку в оплаті баз даних за фактичним використанням.
Пастка в тому, що вони засинають окремо і не згодні щодо того, що таке з'єднання.
Симптом: повільний перший візит
Більшість серверних застосунків тримають пул з'єднань: кілька відкритих з'єднань із базою, готових заздалегідь, щоб кожен запит не платив за відкриття нового. Це правильний дизайн для застосунку, який працює завжди.
Ось що відбувається зі сном:
- Застосунок працює, у його пулі є кілька неактивних з'єднань.
- Ніхто не заходить. Машина застосунку призупиняється, заморожена разом з пулом.
- База даних засинає і закриває свій кінець кожного з'єднання.
- Приходить відвідувач. Застосунок прокидається точно таким, яким був, і досі тримає з'єднання, про які база вже не знає.
- Пул видає одне з таких з'єднань. Він має з'ясувати, що воно мертве, спробувавши його і почекавши, перш ніж відмовитися.
У застосунку Spring Boot перевірка цих мертвих з'єднань блокувала перший запит приблизно на 10 секунд, перш ніж він завершувався помилкою «Connection is not available». Для відвідувача сайт просто зависав, а потім, може, показував помилку.
Чому Spring-застосункам було найгірше
Spring Boot це популярний Java-фреймворк, і за замовчуванням він використовує пул з'єднань HikariCP. Spring може налаштувати цей пул через параметри, зокрема скільки неактивних з'єднань тримати.
Але Mythex дає кожному застосунку його базу як одну змінну DATABASE_URL у форматі postgresql://user:pass@host/db. Spring не може прочитати цей формат напряму. Тому Spring-застосунки на Mythex створювали власний пул у коді, розбираючи цей URL, а пул, створений вручну, ігнорує всі налаштування Spring spring.datasource.*. Навіть якщо хтось знав правильні налаштування пулу, не було місця, де вони б подіяли.
Виправлення, частина 1: налаштування, які Spring може використати
Коли в проєкту є база, тепер ми також задаємо змінні середовища, які Spring читає нативно:
SPRING_DATASOURCE_URL,SPRING_DATASOURCE_USERNAMEіSPRING_DATASOURCE_PASSWORD, перетворені зDATABASE_URL(з вимогою SSL, якщо URL не каже інакше).- Налаштування пулу, що переживають сон:
| Налаштування | Значення | Чому |
|---|---|---|
| Мінімум неактивних з'єднань | 0 | Між запитами нічого не тримати відкритим, тож нема чого застаріти |
| Тайм-аут простою | 30 секунд | Швидко закривати невикористовувані з'єднання |
| Максимальний термін життя | 2 хвилини | Вивести з обігу кожне з'єднання до того, як воно постаріє |
| Тайм-аут з'єднання | 5 секунд | Не тримати запит довго, якщо база повільно відповідає |
Коли неактивних з'єднань не тримається, застосунок, що прокинувся, відкриває одне нове з'єднання замість того, щоб продиратися крізь мертві.
Коли ці змінні присутні, Spring сам налаштовує пул, і налаштування працюють. Усе, що проєкт налаштував сам, завжди має перевагу; ми заповнюємо лише те, чого бракує. Не-Spring застосунки ігнорують ці змінні.
Деталь, що майже нічого не зробила
Spring зіставляє змінні середовища з налаштуваннями за правилом, яке легко порушити. Параметр minimum-idle треба писати як SPRING_DATASOURCE_HIKARI_MINIMUMIDLE: крапки й дефіси прибрано, слова злиті. Якщо написати SPRING_DATASOURCE_HIKARI_MINIMUM_IDLE, що виглядає природніше, він зіставляється з іншим, неіснуючим параметром (minimum.idle) і тихо ігнорується.
Така помилка проходить усі перевірки і нічого не робить у продакшені. Тому тест перевіряє і те, що значення задані, і те, що жодна змінна не використовує форму з підкресленнями.
Виправлення, частина 2: кожен інший стек
Spring не єдиний фреймворк із пулом, а ми не можемо заздалегідь налаштувати кожну бібліотеку на кожній мові. Тому агент Mythex, що пише ваш застосунок, тепер отримує постійну інструкцію для будь-якого стеку: опубліковані застосунки та їхні бази засинають, коли простоюють, тож з'єднання, відкрите раніше, може бути мертвим під час наступного запиту. Агент має:
- не тримати неактивних з'єднань із базою відкритими або перевіряти кожне перед використанням,
- один раз повторити запит у разі помилки з'єднання,
- використовувати налаштування бази, які платформа надає в середовищі, а не створювати власний пул, розбираючи
DATABASE_URL.
Останній пункт замикає коло з частиною 1: застосунок, побудований на наданих налаштуваннях, автоматично отримує конфігурацію, безпечну для сну.
Результат
Вимірявши на хостинговій платформі, де ми запускаємо опубліковані застосунки, ми отримали: перший запит після пробудження скоротився з 12,7 секунди до 2,3 секунди.
Що ми з цього взяли
- Кожен шар, що засинає, має погодитися щодо того, що зберігається. Заморожений застосунок і спляча база кожен зробили розумний вибір. Разом застосунок прокинувся з станом, який уже не був правдивим.
- Значення за замовчуванням, розраховані на серверні машини, що працюють завжди, не пасують застосункам, які масштабуються до нуля. Тримати з'єднання теплими добра ідея, доки все навколо не остигло.
- Давайте фреймворкам їхні дані у їхньому форматі. Один URL був простим для нас, але змусив кожен Spring-застосунок вручну писати пул, який ігнорував власну конфігурацію.
- Перевіряйте, що налаштування написане так, як його читає фреймворк. Правильне значення під неправильною назвою нічого не робить, тихо.
Якщо ви тільки знайомитеся з тим, як працюють бази даних на Mythex, дивіться документацію з баз даних і наше пояснення що таке Postgres.