Architettura PDO Pool
Questo articolo descrive la progettazione interna del PDO Pool. Se stai cercando una guida all’uso, vedi PDO Pool: Pool di Connessioni.
Architettura a Due Livelli
Il PDO Pool è composto da due livelli:
1. PDO Core (pdo_pool.c) – logica per il binding delle connessioni alle coroutine,
gestione delle transazioni, conteggio dei riferimenti degli statement.
2. Async Pool (zend_async_pool_t) – il pool di risorse universale dell’estensione async.
Gestisce la coda delle connessioni libere, i limiti e gli healthcheck.
Non sa nulla del PDO – lavora con valori zval astratti.
Questa separazione consente di utilizzare lo stesso meccanismo di pooling per qualsiasi risorsa, non solo database.
Diagramma dei Componenti
Connessione Template
Quando si crea un PDO con un pool, il core non apre una connessione TCP reale.
Invece, viene creato un template – un oggetto pdo_dbh_t che memorizza
il DSN, il nome utente, la password e un riferimento al driver. Tutte le connessioni reali vengono create successivamente,
su richiesta, sulla base di questo template.
Per il template, viene chiamato db_handle_init_methods() invece di db_handle_factory().
Questo metodo imposta la tabella dei metodi del driver (dbh->methods)
ma non crea una connessione TCP né alloca driver_data.
Ciclo di Vita della Connessione
Creazione di una Connessione dal Pool (Sequenza)
API Interna
pdo_pool.c – Funzioni Pubbliche
| Funzione | Scopo |
|---|---|
pdo_pool_create() |
Crea un pool per pdo_dbh_t basato sugli attributi del costruttore |
pdo_pool_destroy() |
Rilascia tutte le connessioni, chiude il pool, svuota la tabella hash |
pdo_pool_acquire_conn() |
Restituisce una connessione per la coroutine corrente (riuso o acquire) |
pdo_pool_peek_conn() |
Restituisce la connessione associata senza acquire (NULL se nessuna) |
pdo_pool_maybe_release() |
Restituisce la connessione al pool se non ci sono transazioni o statement |
pdo_pool_get_wrapper() |
Restituisce l’oggetto PHP Async\Pool per il metodo getPool() |
pdo_pool.c – Callback Interni
| Callback | Quando Viene Chiamato |
|---|---|
pdo_pool_factory() |
Il pool necessita di una nuova connessione (acquire quando il pool è vuoto) |
pdo_pool_destructor() |
Il pool distrugge una connessione (alla chiusura o all’evizione) |
pdo_pool_healthcheck() |
Controllo periodico – la connessione è ancora attiva? |
pdo_pool_before_release() |
Prima del ritorno al pool – rollback delle transazioni non committate |
pdo_pool_free_conn() |
Chiude la connessione del driver, libera la memoria |
Binding alla Coroutine
Le connessioni sono associate alle coroutine tramite una tabella hash pool_connections,
dove la chiave è l’identificatore della coroutine e il valore è un puntatore a pdo_dbh_t.
L’identificatore della coroutine viene calcolato dalla funzione pdo_pool_coro_key():
- Se la coroutine è un oggetto PHP – viene usato
zend_object.handle(uint32_t sequenziale) - Per le coroutine interne – l’indirizzo del puntatore spostato di
ZEND_MM_ALIGNMENT_LOG2
Pulizia al Completamento della Coroutine
Quando una connessione è associata a una coroutine, viene registrato un pdo_pool_cleanup_callback
tramite coro->event.add_callback(). Quando la coroutine si completa (normalmente o con un errore),
il callback restituisce automaticamente la connessione al pool. Questo garantisce nessuna perdita di connessioni
anche con eccezioni non gestite.
Pinning: Blocco della Connessione
Una connessione è bloccata su una coroutine e non tornerà al pool se almeno una condizione è soddisfatta:
conn->in_txn == true– una transazione attivaconn->pool_slot_refcount > 0– ci sono statement live (PDOStatement) che usano questa connessione
Il refcount viene incrementato quando uno statement viene creato e decrementato quando viene distrutto.
Quando entrambe le condizioni sono azzerate, pdo_pool_maybe_release() restituisce la connessione al pool.
Gestione delle Credenziali nella Factory
Quando si crea una nuova connessione, pdo_pool_factory() copia le
stringhe DSN, nome utente e password dal template tramite estrdup(). Questo è necessario perché
i driver possono mutare questi campi durante db_handle_factory():
- PostgreSQL – sostituisce
;con spazi indata_source - MySQL – alloca
username/passworddal DSN se non sono stati passati - ODBC – ricostruisce completamente
data_source, incorporando le credenziali
Dopo una chiamata db_handle_factory() riuscita, le copie vengono liberate tramite efree().
In caso di errore, la liberazione avviene attraverso pdo_pool_free_conn(),
che viene utilizzato anche dal distruttore del pool.
Incompatibilità con le Connessioni Persistenti
Le connessioni persistenti (PDO::ATTR_PERSISTENT) sono incompatibili con il pool.
Una connessione persistente è legata al processo e sopravvive alle richieste,
mentre il pool crea connessioni a livello di richiesta con gestione automatica del ciclo di vita.
Il tentativo di abilitare entrambi gli attributi contemporaneamente risulterà in un errore.
Prossimi Passi
- PDO Pool: Pool di Connessioni – guida all’uso
- Coroutine – come funzionano le coroutine
- Scope – gestione dei gruppi di coroutine