Архітектура PDO Pool
Ця стаття описує внутрішню будову PDO Pool. Якщо ви шукаєте посібник з використання, див. PDO Pool: Пул з’єднань.
Дворівнева архітектура
PDO Pool складається з двох рівнів:
1. Ядро PDO (pdo_pool.c) — логіка прив’язки з’єднань до корутин,
управління транзакціями, підрахунок посилань на вирази.
2. Async Pool (zend_async_pool_t) — універсальний пул ресурсів з асинхронного розширення.
Керує чергою вільних з’єднань, лімітами та перевірками стану.
Він нічого не знає про PDO — працює з абстрактними значеннями zval.
Таке розділення дозволяє використовувати один і той самий механізм пулінгу для будь-яких ресурсів, не лише для баз даних.
Діаграма компонентів
Шаблонне з’єднання
При створенні PDO з пулом ядро не відкриває реальне TCP-з’єднання.
Натомість створюється шаблон — об’єкт pdo_dbh_t, що зберігає
DSN, ім’я користувача, пароль та посилання на драйвер. Усі реальні з’єднання створюються пізніше,
на вимогу, на основі цього шаблону.
Для шаблону викликається db_handle_init_methods() замість db_handle_factory().
Цей метод встановлює таблицю методів драйвера (dbh->methods),
але не створює TCP-з’єднання та не виділяє driver_data.
Життєвий цикл з’єднання
Створення з’єднання з пулу (послідовність)
Внутрішній API
pdo_pool.c — Публічні функції
| Функція | Призначення |
|---|---|
pdo_pool_create() |
Створює пул для pdo_dbh_t на основі атрибутів конструктора |
pdo_pool_destroy() |
Звільняє всі з’єднання, закриває пул, очищує хеш-таблицю |
pdo_pool_acquire_conn() |
Повертає з’єднання для поточної корутини (повторне використання або acquire) |
pdo_pool_peek_conn() |
Повертає прив’язане з’єднання без acquire (NULL, якщо немає) |
pdo_pool_maybe_release() |
Повертає з’єднання в пул, якщо немає транзакції або виразів |
pdo_pool_get_wrapper() |
Повертає PHP-об’єкт Async\Pool для методу getPool() |
pdo_pool.c — Внутрішні зворотні виклики
| Зворотний виклик | Коли викликається |
|---|---|
pdo_pool_factory() |
Пулу потрібне нове з’єднання (acquire при порожньому пулі) |
pdo_pool_destructor() |
Пул знищує з’єднання (при закритті або витісненні) |
pdo_pool_healthcheck() |
Періодична перевірка — чи живе з’єднання? |
pdo_pool_before_release() |
Перед поверненням у пул — відкат незафіксованих транзакцій |
pdo_pool_free_conn() |
Закриває з’єднання драйвера, звільняє пам’ять |
Прив’язка до корутини
З’єднання прив’язуються до корутин через хеш-таблицю pool_connections,
де ключ — ідентифікатор корутини, а значення — вказівник на pdo_dbh_t.
Ідентифікатор корутини обчислюється функцією pdo_pool_coro_key():
- Якщо корутина є PHP-об’єктом — використовується
zend_object.handle(послідовний uint32_t) - Для внутрішніх корутин — адреса вказівника, зсунута на
ZEND_MM_ALIGNMENT_LOG2
Очищення при завершенні корутини
Коли з’єднання прив’язується до корутини, через coro->event.add_callback() реєструється
pdo_pool_cleanup_callback. Коли корутина завершується (нормально або з помилкою),
зворотний виклик автоматично повертає з’єднання в пул. Це гарантує відсутність витоку з’єднань
навіть при необроблених винятках.
Закріплення: блокування з’єднання
З’єднання закріплюється за корутиною і не повертається в пул, якщо виконується хоча б одна умова:
conn->in_txn == true— активна транзакціяconn->pool_slot_refcount > 0— є живі вирази (PDOStatement), що використовують це з’єднання
Лічильник посилань інкрементується при створенні виразу та декрементується при його знищенні.
Коли обидві умови зняті, pdo_pool_maybe_release() повертає з’єднання в пул.
Управління обліковими даними у фабриці
При створенні нового з’єднання pdo_pool_factory() копіює рядки
DSN, імені користувача та пароля з шаблону через estrdup(). Це необхідно, оскільки
драйвери можуть змінювати ці поля під час db_handle_factory():
- PostgreSQL — замінює
;на пробіли вdata_source - MySQL — виділяє
username/passwordз DSN, якщо вони не були передані - ODBC — повністю перебудовує
data_source, вбудовуючи облікові дані
Після успішного виклику db_handle_factory() копії звільняються через efree().
При помилці звільнення відбувається через pdo_pool_free_conn(),
який також використовується деструктором пулу.
Несумісність з постійними з’єднаннями
Постійні з’єднання (PDO::ATTR_PERSISTENT) несумісні з пулом.
Постійне з’єднання прив’язане до процесу та переживає запити,
тоді як пул створює з’єднання на рівні запиту з автоматичним управлінням життєвим циклом.
Спроба увімкнути обидва атрибути одночасно призведе до помилки.
Що далі?
- PDO Pool: Пул з’єднань — посібник з використання
- Корутини — як працюють корутини
- Scope — управління групами корутин