Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

У доповіді Андрій Бородін розповість, як вони врахували досвід масштабування PgBouncer під час проектування пулера з'єднань Одіссея, Як викочували його в production. Крім того, обговоримо які функції пулера хотілося б бачити в нових версіях: нам важливо не лише закривати свої потреби, а й розвивати спільноту користувачів Одіссея.

Відео:

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Всім привіт! Мене звати Андрій.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

В Яндексі займаюся розробкою open source баз даних. І сьогодні ми маємо тему про connection pooler з'єднань.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Якщо ви знаєте, як назвати connection pooler російською, то скажіть мені. Я дуже хочу знайти добрий технічний термін, який повинен устоятись у технічній літературі.

Тема досить складна, тому що в багатьох базах даних connection pooler убудований і про нього навіть знати не треба. Якісь налаштування, звичайно, скрізь є, але у Postgres так не виходить. І паралельно (на HighLoad++ 2019) йде доповідь Миколи Самохвалова про налаштування запитів до Postgres. І я так розумію, що сюди прийшли люди, які вже налаштували запити ідеально, і це люди, які стикаються з більш рідкісними системними проблемами, пов'язаними з мережею, утилізацією ресурсів. І місцями це було досить складно у тому плані, що проблеми неочевидні.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

У Яндексі є Postgres. В Яндекс.Хмарі живе багато сервісів Яндекса. І у нас кілька петабайт даних, які генерують не менше мільйона запитів на секунду у Postgres.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

І ми надаємо досить типовий кластер усім сервісам – це основна primary нода вузла, звичайні дві репліки (синхронна та асинхронна), резервне копіювання, масштабування запитів, що читають на репліці.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Кожен вузол кластера – це Postgres, на якому крім Postgres та систем моніторингу ще встановлено connection pooler. Connection pooler використовується для fencing та за своїм основним призначенням.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Що таке основне призначення Connection Pooler?

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

У Postgres прийнято процесну модель під час роботи з базою даних. Це означає, що одна сполука – це один процес, один бекенд Postgres. І в цьому бекенд дуже багато різних кешів, які досить дорого робити різними для різних з'єднань.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Крім того, у коді Postgres є масив, який називається procArray. Він містить основні дані про мережеві з'єднання. І майже всі алгоритми обробки procArray мають лінійну складність, вони пробігаються по всьому масиву мережевих з'єднань. Це досить швидкий цикл, але з великою кількістю вхідних мережних з'єднань все стає трохи дорожчим. А коли все стало трохи дорожче, у результаті можна заплатити дуже велику ціну за велику кількість мережевих з'єднань.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Існують 3 можливі підходи:

  • На стороні програми.
  • На боці бази даних.
  • І між, тобто всілякі комбінації.

На жаль, вбудований pooler зараз перебуває у стадії розробки. Друзі у компанії PostgreSQL Professional переважно цим займаються. Коли він з'явиться, важко передбачити. І фактично на вибір архітектора маємо два рішення. Це application-side pool та proxy pool.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Application-side pool – це найпростіший спосіб. І майже всі клієнтські драйвера вам надають спосіб: мільйони ваших з'єднань у коді представити у вигляді десятків з'єднань у базу даних.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Виникає проблема з тим, що в певний момент ви хочете масштабувати бекенд, ви хочете розгорнути його на безліч віртуальних машин.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Потім ви усвідомлюєте, що у вас ще кілька зон доступностей, кілька дата-центрів. І підхід з client side pooling призводить до великих чисел. Великі – це близько 10 000 з'єднань. Це край, який може нормально працювати.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Якщо говорити про proxy poolers, то є два poolers, які вміють багато всього. Вони не тільки poolers. Вони poolers + ще класна функціональність. Це Pgpool и Crunchy-Proxy.

Але, на жаль, ця додаткова функціональність не всім потрібна. І вона призводить до того, що poolers підтримують тільки сесійний pooling, тобто один вхідний клієнт, один вихідний клієнт базу даних.

Для наших завдань це не дуже добре підходить, тому ми використовуємо PgBouncer, який реалізує transaction pooling, тобто серверні з'єднання зіставляються клієнтським з'єднанням лише на час транзакції.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

І на нашому навантаженні – це правда. Але є кілька проблем.Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Проблеми починаються, коли ви хочете діагностувати сесію, тому що всі вхідні з'єднання локальні. Усі прийшли з loopback і якось трасувати сесію стає складно.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Звичайно, можна використовувати application_name_add_host. Це спосіб на стороні Bouncer додати IP-адресу до application_name. Але application_name встановлюється додатковим з'єднанням.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

На цьому графіку, де жовта лінія – це реальні запити, а синя – запити, які влітають у базу. І ось ця різниця – це саме установка application_name, яка потрібна тільки для трасування, але вона абсолютно не безкоштовна.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Крім того, у Bouncer не можна обмежити один pool, тобто кількість з'єднань з базою даних на певного користувача, на певну базу.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

До чого це призводить? У вас є навантажений сервіс, написаний на C++ і десь поруч маленький сервіс на ноді, який нічого страшного з базою не робить, але його драйвер божеволіє. Він відкриває 20 000 з'єднань, а все інше зачекає. У вас навіть нормальний код.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Ми, звичайно, написали маленький патч для Bouncer, який додав це налаштування, тобто обмеження клієнтів на pool.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Можна було б це зробити на боці Postgres, тобто ролі в базі даних обмежити кількістю з'єднань.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Але тоді ви втрачаєте можливість зрозуміти, чому у вас немає з'єднань із сервером. PgBouncer не прокидає помилку з'єднання, він повертає завжди одну й ту саму інформацію. І ви не можете зрозуміти: можливо, у вас пароль помінявся, можливо, просто база лягла, можливо, щось не так. Але діагностики немає. Якщо сесію не можна встановити, ви не дізнаєтесь, чому це не можна зробити.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

У певний момент ви дивитеся на графіки програми та бачите, що програма не працює.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Дивіться в топ і бачите, що Bouncer однопотоковий. Це поворотний момент у житті сервісу. Ви знаєте, що ви готувалися до масштабування бази даних через півтора роки, а вам потрібно масштабувати pooler.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Ми дійшли того, що нам треба більше PgBouncer'ів.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

https://lwn.net/Articles/542629/

Трішки запатчили Bouncer.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

І зробили так, що можуть бути підняті з перевикористанням TCP-порту кілька Bouncers. І вже операційна система вхідні TCP-з'єднання між ними автоматично round-robin'ом перекладає.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Це прозоро для клієнтів, тобто все виглядає так, ніби у вас один Bouncer, але у вас є фрагментація idle-з'єднань між запущеними Bouncer'ами.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

І в певний момент ви можете помітити, що ці 3 Bouncers з'їдають кожен своє ядро ​​на 100 %. Вам потрібно багато Bouncers. Чому?

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Тому що у вас є TLS. Ви маєте шифроване з'єднання. І якщо пробенчмаркати Postgres з TLS і без TLS, ви виявите, що кількість встановлених з'єднань падає майже на два порядки з включенням шифрування, тому що TLS handshake споживає ресурси центрального процесора.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

І в топі ви можете побачити багато криптографічних функцій, які виконуються при хвилі вхідних з'єднань. Оскільки у нас primary може перемикатися між зонами доступності, хвиля вхідних з'єднань – це досить типова ситуація. Т. е. з якоїсь причини старий primary був недоступний, все навантаження відправили в інший дата-центр. Вони всі одночасно прийдуть привітатись з TLS.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

І велика кількість TLS handshake може не вітатись вже з Bouncer, а горло йому здавити. Через таймаут хвиля вхідних з'єднань може стати незатухаючою. Якщо у вас retry у базу без exponential backoff, вони не будуть приходять знову і знову когерентною хвилею.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Ось приклад 16 PgBouncer, які вантажать 16 ядер на 100%.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Ми прийшли до каскадного PgBouncer. Це найкраща конфігурація, яку можна досягти на нашому навантаженні із Bouncer. Зовнішні Bouncers у нас служать для TCP handshake, а внутрішні Bouncers служать для реального pooling, щоб не сильно фрагментувати зовнішні з'єднання.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

У такій конфігурації можливий плавний рестарт. Можна рестартити всі ці 18 Bouncers по одному. Але підтримувати таку конфігурацію досить складно. Системні адміністратори, DevOps та люди, які справді несуть відповідальність за цей сервер, будуть не дуже раді такій схемі.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Здавалося б, можна всі наші доопрацювання просувати в Open Source, але Bouncer не дуже добре сапортується. Наприклад, можливість запустити кілька PgBouncers на одному порту закоммітували місяць тому. А pull request із цією функцією був кілька років тому.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

https://www.postgresql.org/docs/current/libpq-cancel.html

https://github.com/pgbouncer/pgbouncer/pull/79

Або ще один приклад. У Postgres ви можете скасувати запит, відправивши секрет по-іншому з'єднанню без зайвої аутентифікації. Але деякі клієнти надсилають просто TCP-reset, тобто рвуть мережне з'єднання. Що при цьому зробить Bouncer? Він нічого не зробить. Він продовжить виконувати запит. Якщо у вас прийшло безліч з'єднань, які невеликими запитами поклали базу, то просто відірвати з'єднання від Bouncer буде недостатньо, необхідно ще завершити ті запити, які біжать в базі.

Це було запатчено і цю проблему досі не вмержили у upstream Bouncer'а.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

І так ми дійшли того, що нам потрібен свій connection pooler, який буде розвиватися, патчитися, в якому можна буде оперативно виправляти проблеми і який, звичайно, має бути багатопотоковим.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Багатопотоковість ми поставили як основне завдання. Нам потрібно добре витримувати хвилю вхідних TLS-з'єднань.

Для цього нам довелося розробити окрему бібліотеку, яка називається Machinarium, призначена для опису машинних станів мережного з'єднання як послідовного коду. Якщо ви подивіться у вихідний код libpq, то ви побачите досить складні виклики, які можуть повернути вам результат і сказати: «Виклич мене трохи пізніше. Зараз у мене поки що IO, але коли IO пройде у мене навантаження для процесора». І це багаторівнева схема. Мережеву взаємодію зазвичай описують машиною станів. Багато правил типу "Якщо я раніше отримав заголовок пакета розміру N, то зараз я чекаю N байт", "Якщо я відправляв пакет SYNC, то зараз я чекаю пакет з метаданими результату". Виходить досить важкий контрінтуїтивний код, якби лабіринт перетворили на малий розгортку. Ми зробили так, що замість машини станів програміст описує основний шлях взаємодії як звичайного імперативного коду. Просто в цьому імперативному коді потрібно вставляти місця, де послідовність виконання потрібно перервати очікуванням даних від мережі, передавши контекст виконання іншій корутині (green thread-у). Цей підхід аналогічний тому, що ми найочікуваніший шлях у лабіринті записуємо поспіль, а потім додаємо до нього відгалуження.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

У результаті ми маємо один потік, який робить TCP accept і round-robin'ом передає безлічі workers TPC-з'єднання.

При цьому кожне з'єднання клієнта працює завжди на одному процесорі. І це дозволяє зробити його cache-friendly.

І крім того, ми допрацювали трошки збирання маленьких пакетів в один великий пакет для того, щоб розвантажити системний TCP-stack.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Крім того, ми покращили транзакційний пулінг у тому плані, що Odyssey при встановленому налаштуванні може відправити CANCEL та ROLLBACK у разі обриву мережного з'єднання, тобто якщо запит ніхто не чекає, Odyssey скаже базі, щоб вона не намагалася виконати той запит, який може витрачати дорогоцінні ресурси.

І по можливості ми зберігаємо з'єднання за одним і тим самим клієнтом. Це дозволяє не встановлювати заново application_name_add_host. Якщо це можливо, то ми не маємо додаткової переустановки параметрів, які потрібні для діагностики.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Ми працюємо на користь Яндекс.Хмари. І якщо ви використовуєте managed PostgreSQL і у вас встановлений connection pooler, ви можете створити логічну реплікацію назовні, тобто поїхати від нас, якщо вам захочеться, за допомогою логічної реплікації. Bouncer назовні потік логічної реплікації не віддасть.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Це приклад налаштування логічної реплікації.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Крім того, ми маємо підтримку фізичної реплікації назовні. В Хмарі вона, звичайно, неможлива, тому що тоді вам кластер віддасть надто багато інформації про себе. Але у ваших інсталяціях, якщо вам потрібна фізична реплікація через connection pooler в Odyssey, це можливо.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

У Odyssey повністю сумісний моніторинг із PgBouncer. У нас така ж консоль, яка виконує майже ті самі команди. Якщо чогось не вистачає, надсилайте pull request, або хоча б issue в GitHub, дороблятимемо потрібні команди. Але основна функціональність консолі PgBouncer ми вже маємо.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

І, звичайно, ми маємо error forwarding. Ми повернемо ту помилку, що її повідомила база. Ви отримаєте інформацію, чому ви не потрапляєте до бази, а не просто, що ви до неї не потрапляєте.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Ця можливість вимикається на випадок, якщо вам потрібна 100% сумісність з PgBouncer. Ми можемо поводитися так само, як Bouncer, просто про всяк випадок.

Розробка

Декілька слів про вихідний код Odyssey.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

https://github.com/yandex/odyssey/pull/66

Наприклад, є команди "Pause / Resume". Вони зазвичай використовуються для оновлення бази даних. Якщо вам потрібно оновити Postgres, ви можете поставити його на паузу в connection pooler, зробити pg_upgrade, після цього зробити resume. І з боку клієнта це виглядатиме так, ніби база просто гальмувала. Цю функціональність нам принесли люди із спільноти. Вона поки що не помержена, але скоро все буде. (Вже помержено)

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

https://github.com/yandex/odyssey/pull/73 - вже помержена

Крім того, одна з нових фіч в PgBouncer, це підтримка SCRAM Authentication, яку нам теж принесла людина, яка не працює в Яндекс.Хмарі. І те, й інше – це складна функціональність і важлива.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Тому хочеться розповісти з чого зроблено Odyssey, раптом ви теж зараз хочете трохи пописати код.

Ви маєте вихідну базу Odyssey, яка спирається на дві основні бібліотеки. Бібліотека Kiwi – це реалізація Postgres'ового протоколу повідомлень. Т. е. нативний proto 3 у Postgres - це стандартні повідомлення, якими фронтенди, бекенди можуть обмінюватися. Вони реалізовані у бібліотеці Kiwi.

Бібліотека Machinarium – це бібліотека реалізації потоків. Невеликий фрагмент цього Machinarium написано на асемблері. Але, не лякайтеся, там лише 15 рядків.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Архітектура Odyssey. Існує основна машина, в якій запущені coroutines. У цій машині реалізується вхід вхідних TCP-з'єднань і розподіл по workers.

Усередині одного worker може працювати обробник кількох клієнтів. А також в основному потоці крутяться консоль і обробка crone-задач видалення з'єднань, які більше не потрібні в pool.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Для тестування Odyssey використовується стандартний набір тестів Postgres. Просто запускаємо install-check через Bouncer і через Odyssey, отримуємо нульовий div. Там кілька тестів, пов'язані з форматуванням дат, не проходять абсолютно однаково в Bouncer і в Odyssey.

Крім того, є багато драйверів, які мають своє тестування. І їх тести ми використовуємо для тестування Odyssey.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Крім того, через нашу каскадну конфігурацію нам доводиться тестувати різні зв'язки: Postgres + Odyssey, PgBouncer + Odyssey, Odyssey + Odyssey для того, щоб бути впевненими в тому, що, якщо Odyssey опинився в якійсь із частин у каскаді, він теж, як і раніше, працює так, як ми очікуємо.

Граблі

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Ми використовуємо Odyssey у виробництві. І було б не чесно, якби я сказав, що все просто працює. Ні, тобто так, але не завжди. Наприклад, у production все просто працювало, потім прийшли наші друзі з PostgreSQL Professional і сказали, що ми маємо memory leak. Вони справді були, ми їх виправили. Але це просто.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Потім ми виявили, що в connection pooler є вхідні TLS-з'єднання та вихідні TLS-з'єднання. І у з'єднаннях потрібні клієнтські сертифікати та серверні сертифікати.

Серверні сертифікати Bouncer і Odyssey перечитують їх pcache, але клієнтські сертифікати перечитувати з pcache не треба, тому що наш масштабований Odyssey у результаті впирається в системну продуктивність читання цього сертифіката. Це для нас стало несподіванкою, бо вперся він далеко не одразу. Спочатку він лінійно масштабувався, а після 20 000 одночасних з'єднань, що входять, ця проблема себе проявила.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Pluggable Authentication Method – це можливість аутентифікуватись вбудованими lunux'овими засобами. У PgBouncer вона реалізована так, що є окремий потік на очікування відповіді від PAM і є основним потіком PgBouncer, який обслуговує поточне з'єднання і може попросити їх пожити в потоці PAM.

Ми це не стали реалізовувати з однієї простої причини. А в нас багато потоків. Навіщо нам це потрібне?

В результаті це може створювати проблеми з тим, що, якщо у вас є PAM-аутентифікація і не PAM-аутентифікації, то велика хвиля PAM-аутентифікації може суттєво затримувати PAM-аутентифікацію. Це одна із тих штук, яку ми не виправили. Але якщо ви хочете це виправити, можете зайнятися цим.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Ще одні граблі були з тим, що ми маємо один потік, який робить accept всім вхідним з'єднанням. І потім їх передає на worker pool, де відбуватиметься TLS handshake.

У результаті, якщо у вас є когерентна хвиля з 20 000 мережевих з'єднань, вони будуть прийняті. І на стороні клієнта libpq розпочне звіт таймаутів. За замовчуванням на зразок 3 секунд там стоїть.

Якщо всі вони не можуть одночасно зайти в базу, то вони не можуть зайти в базу, тому що все це може бути покрите не експоненційним ретрі.

Ми прийшли до того, що скопіювали тут схему з PgBouncer з тим, що ми маємо throttling кількості TCP-з'єднань, яким ми робимо accept.

Якщо ми бачимо, що ми аксептимо з'єднання, а вони в результаті не встигають похендшейкуватися, ми ставимо їх у чергу для того, щоб вони не витрачали ресурси центрального процесора. Це призводить до того, що одночасний handshake може здійснюватися не для всіх з'єднань, які прийшли. Але хоча б хтось у базу зайде, навіть якщо навантаження досить сильне.

Дорожня карта

Чого хотілося б побачити у майбутньому в Odyssey? Що ми готові розробляти і що ми чекаємо від спільноти?

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

На серпень 2019 року.

Ось так виглядав roadmap Odyssey у серпні:

  • Ми хотіли SCRAM та PAM authentication.
  • Ми хотіли forward читаючих запитів на standby.
  • Хотілося б online-restart.
  • І можливість робити паузу на сервері.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Половина цього roadmap виконана, причому не нами. І це добре. Тому обговоримо те, що залишилося і додамо ще.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Що стосується forward read-only queries to standby? У нас є репліки, які без виконання запитів просто грітимуть повітря. Вони нам необхідні для забезпечення failover та switchover. У разі проблем в одному із дата-центрі хотілося б їх зайняти якоюсь корисною роботою. Тому що ті ж центральні процесори, ту ж саму пам'ять ми не можемо сконфігурувати по-іншому, тому що інакше не працюватиме реплікація.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

В принципі, в Postgres, починаючи з десятки є можливість при з'єднанні вказати так само session_attrs. Ви можете з'єднати всі хости баз даних і сказати, навіщо ви йдете в базу даних: записати або тільки прочитати. І драйвер сам вибере перший за списком хост, який більше подобається, який виконує вимоги session_attrs.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Але проблема цього підходу полягає в тому, що він не контролює лаг реплікації. У вас може бути якась репліка, котра відстала на неприйнятний для вашого сервісу час. Для того щоб зробити повнофункціональне виконання запитів, що читають на репліці, по суті, нам потрібно підтримати в Odyssey можливість не працювати, коли читати не можна.

Odyssey повинен ходити часом у базу і питати реплікаційну відстань від першої. І якщо воно досягло граничного значення, нові запити в базу не пускати, сказати клієнту, що потрібно заново ініціювати з'єднання і, можливо, вибрати інший хост для виконання запитів. Це дозволить базі швидше відновити реплікаційний лаг та знову повернутися для відповіді запитом.

Терміни реалізації складно назвати, бо це є open source. Але, сподіваюся, що не 2,5 роки як у колег із PgBouncer. Ось цю функцію хотілося б бачити в Odyssey.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

У суспільстві люди запитували про підтримку prepared statement. Зараз можна створити prepared statement двома способами. По-перше, ви можете виконати SQL-команду, саме «prepared». Для того щоб зрозуміти цю SQL-команду, нам потрібно навчитися розуміти SQL на стороні Bouncer. Це був би overkill, тому що це перебір, тому що нам потрібен цілком парсер. Ми не можемо ширити кожну SQL-команду.

Але є preparated statement на рівні протоколу повідомлень на proto3. І це те місце, коли інформація про те, що створюється prepared statement, надходить у структурованому вигляді. І ми могли б підтримати розуміння, що на якомусь серверному з'єднанні клієнт попросив створити prepared statements. І навіть якщо транзакція закрилася, нам потрібно, як і раніше, підтримувати зв'язність сервера та клієнта.

Але тут виникає розбіжність у діалозі, тому що хтось говорить про те, що потрібно розуміти, який саме prepared statements створив клієнт і розділяти серверне з'єднання між усіма клієнтами, які створили це серверне з'єднання, тобто які створили такий prepared statement.

Андрій Фройнд сказав, що якщо до вас прийшов клієнт, який вже створював в іншому серверному з'єднанні такий prepared statement, то створіть його за нього. Але, здається, це трохи неправильно виконувати запити в базі замість клієнта, але з погляду розробника, який пише протокол взаємодії з базою, це було б зручно, якби йому просто дали з'єднання мережі, в якому є такий підготовлений запит.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

І ще одна фіча, яку нам потрібно реалізувати. У нас зараз є моніторинг, сумісний із PgBouncer. Ми можемо повернути середнє виконання запиту. Але середній час – це середня температура лікарні: хтось холодний, хтось теплий – у середньому всі здорові. Це не правда.

Нам потрібно реалізувати підтримку перцентилів, які вказували б на те, що є повільні запити, які витрачають ресурси, і зробили б моніторинг більш прийнятним.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Найголовніше - хочеться версію 1.0 (Вже вийшла версія 1.1). Справа в тому, що зараз Odyssey знаходиться у версії 1.0rc, тобто release candidate. І всі граблі, які я перерахував, були полагоджені рівно з тією самою версією, крім memory leak.

Що для нас означатиме версія 1.0? Ми викочуємо Odyssey на свої бази. Він вже зараз працює на наших базах, але коли він досягне точки в 1 запитів в секунду, то ми можемо сказати, що це релізна версія і це версія, яку можна назвати 000.

У спільноті кілька людей попросили, щоб у версії 1.0 були пауза і SCRAM. Але це означатиме, що нам потрібно буде викотити в production вже наступну версію, тому що ні SCRAM, ні пауза досі не помержені. Але швидше за все це питання буде досить оперативно вирішене.

Odyssey roadmap: що ми хочемо від пулера з'єднань. Андрій Бородін (2019)

Я чекаю на вашу pull request. І ще хотілося б почути, які у вас проблеми з Bouncer. Давайте їх обговоримо. Можливо, ми зможемо якісь функції реалізувати, які вам потрібні.

На цьому моя частина закінчена, я хотів би вас послухати. Дякую!

Питання

Якщо я поставлю свій application_name, він буде правильно прокидатися, в тому числі в transaction pooling в Odyssey?

В Odyssey чи Bouncer?

В Odyssey. У Bouncer прокидається.

Сет ми зробимо.

І якщо моя реальна сполука стрибатиме за іншими сполуками, то вона передаватиметься?

Ми зробимо мережу всіх параметрів, які перераховані в списку. Я не можу сказати, чи є у цьому списку application_name. Здається, там його бачив. Ми встановимо ті самі параметри. Одним запитом там сет зробить все те, що було встановлене клієнтом при стартапі.

Дякую, Андрію, за доповідь! Гарна доповідь! Радий, що Odyssey з кожною хвилиною розвивається все швидше та швидше. Бажаю так само продовжувати. Ми вже до вас зверталися з проханням мати multi data-source підключення, щоб Odyssey міг підключатися одночасно до різних баз даних, тобто майстер slave, а потім автоматично після failover підключатися до нового майстра.

Так, я, здається, пригадую цю дискусію. Зараз є кілька storages. Але перемикання між ними немає. Ми повинні на своєму боці опитувати сервер, що він живий і розуміти, що стався failover, хто викличе pg_recovery. Я маю стандартний спосіб зрозуміти, що ми прийшли не на майстер. І ми повинні зрозуміти якось із помилок чи як? Т. е. Ідея цікава, вона обговорюється. Напишіть більше коментарів. Якщо у вас є робочі руки, які знають C, це взагалі чудово.

Питання масштабування щодо реплік нас теж цікавить, тому що ми хочемо зробити adoption реплікованих кластерів максимально простим для розробників додатку. Але тут хотілося б більше коментарів, тобто як саме зробити, як добре зробити.

Питання також про репліки. Виходить, що у вас є майстер та кілька реплік. І зрозуміло, що в репліку ходять рідше, ніж у майстер за з'єднаннями, тому що у них може бути відмінність. Ви говорили, що відмінність у даних може бути така, що не задовольнятиме ваш бізнес і ви не туди ходитимете, поки вона не дореплікується. При цьому якщо ви довго туди не ходили, а потім почали ходити, то ті дані, які потрібні, не будуть відразу доступні. Т. е. якщо ми постійно ходимо в майстер, то там кеш прогрітий, а в репліці кеш трохи відстає.

Так це правда. У pcache не буде блоків даних, які ви хочете, у real cache не буде інформації про таблиці, які ви хочете, у планах не буде розпорешених запитів, взагалі нічого не буде.

І коли у вас є якийсь кластер, і ви додаєте туди нову репліку, то доки вона стартує, у ній все погано, тобто вона нарощує свій кеш.

Я зрозумів ідею. Правильним підходом було б запустити невеликий відсоток запитів спочатку на репліку, які прогріли б кеш. Грубо кажучи, ми маємо умову, що ми повинні відставати не більше, ніж на 10 секунд від майстра. І ця умова включатиме не однією хвилею, а плавно для деяких клієнтів.

Так, збільшувати вагу.

Це гарна ідея. Але спочатку потрібно реалізувати це відключення. Спочатку треба вимкнутись, а потім подумаємо, як увімкнутися. Це чудова фіча, щоб плавно вмикатися.

У nginx є така опція slowly start у кластері для сервера. І він поступово збільшує навантаження.

Так, чудова ідея, спробуємо, коли дійдемо до цього.

Джерело: habr.com

Додати коментар або відгук