PDO Pool Architektur
Dieser Artikel beschreibt das interne Design des PDO Pool. Wenn Sie eine Nutzungsanleitung suchen, siehe PDO Pool: Verbindungspool.
Zweistufige Architektur
PDO Pool besteht aus zwei Schichten:
1. PDO-Kern (pdo_pool.c) -- Logik fuer die Bindung von Verbindungen an Koroutinen, Transaktionsverwaltung, Statement-Referenzzaehlung.
2. Async Pool (zend_async_pool_t) -- der universelle Ressourcenpool der Async-Erweiterung. Verwaltet die Warteschlange freier Verbindungen, Limits und Healthchecks. Er weiss nichts ueber PDO -- er arbeitet mit abstrakten zval-Werten.
Diese Trennung ermoeglicht die Verwendung desselben Pooling-Mechanismus fuer beliebige Ressourcen, nicht nur Datenbanken.
Komponentendiagramm
Template-Verbindung
Beim Erstellen eines PDO mit einem Pool wird keine echte TCP-Verbindung geoeffnet. Stattdessen wird ein Template erstellt -- ein pdo_dbh_t-Objekt, das den DSN, Benutzernamen, das Passwort und eine Referenz auf den Treiber speichert. Alle echten Verbindungen werden spaeter bei Bedarf basierend auf diesem Template erstellt.
Fuer das Template wird db_handle_init_methods() anstelle von db_handle_factory() aufgerufen. Diese Methode setzt die Methodentabelle des Treibers (dbh->methods), erstellt aber keine TCP-Verbindung und alloziert kein driver_data.
Verbindungslebenszyklus
Erstellen einer Verbindung aus dem Pool (Sequenz)
Interne API
pdo_pool.c -- Oeffentliche Funktionen
| Funktion | Zweck |
|---|---|
pdo_pool_create() | Erstellt einen Pool fuer pdo_dbh_t basierend auf Konstruktorattributen |
pdo_pool_destroy() | Gibt alle Verbindungen frei, schliesst den Pool, leert die Hashtabelle |
pdo_pool_acquire_conn() | Gibt eine Verbindung fuer die aktuelle Koroutine zurueck (Wiederverwendung oder Acquire) |
pdo_pool_peek_conn() | Gibt die gebundene Verbindung ohne Acquire zurueck (NULL wenn keine) |
pdo_pool_maybe_release() | Gibt die Verbindung an den Pool zurueck, wenn keine Transaktion oder Statements |
pdo_pool_get_wrapper() | Gibt das Async\Pool PHP-Objekt fuer die getPool()-Methode zurueck |
pdo_pool.c -- Interne Callbacks
| Callback | Wann aufgerufen |
|---|---|
pdo_pool_factory() | Pool benoetigt eine neue Verbindung (Acquire bei leerem Pool) |
pdo_pool_destructor() | Pool zerstoert eine Verbindung (beim Schliessen oder Eviction) |
pdo_pool_healthcheck() | Periodische Pruefung -- lebt die Verbindung noch? |
pdo_pool_before_release() | Vor der Rueckgabe an den Pool -- uncommitted Transaktionen zurueckrollen |
pdo_pool_free_conn() | Schliesst die Treiberverbindung, gibt Speicher frei |
Bindung an eine Koroutine
Verbindungen werden ueber eine pool_connections-Hashtabelle an Koroutinen gebunden, wobei der Schluessel die Koroutinenkennung und der Wert ein Zeiger auf pdo_dbh_t ist.
Die Koroutinenkennung wird von der Funktion pdo_pool_coro_key() berechnet:
- Wenn die Koroutine ein PHP-Objekt ist -- wird
zend_object.handle(sequentielles uint32_t) verwendet - Fuer interne Koroutinen -- die Zeigeradresse verschoben um
ZEND_MM_ALIGNMENT_LOG2
Bereinigung bei Koroutinen-Abschluss
Wenn eine Verbindung an eine Koroutine gebunden wird, wird ein pdo_pool_cleanup_callback ueber coro->event.add_callback() registriert. Wenn die Koroutine abschliesst (normal oder mit einem Fehler), gibt der Callback die Verbindung automatisch an den Pool zurueck. Dies garantiert keine Verbindungslecks, selbst bei unbehandelten Ausnahmen.
Pinning: Verbindungssperrung
Eine Verbindung ist an eine Koroutine gepinnt und wird nicht an den Pool zurueckgegeben, wenn mindestens eine Bedingung erfuellt ist:
conn->in_txn == true-- eine aktive Transaktionconn->pool_slot_refcount > 0-- es gibt aktive Statements (PDOStatement), die diese Verbindung verwenden
Der Refcount wird beim Erstellen eines Statements inkrementiert und beim Zerstoeren dekrementiert. Wenn beide Bedingungen erfuellt sind, gibt pdo_pool_maybe_release() die Verbindung an den Pool zurueck.
Zugangsdatenverwaltung in der Factory
Beim Erstellen einer neuen Verbindung kopiert pdo_pool_factory() die DSN-, Benutzernamen- und Passwort-Strings vom Template ueber estrdup(). Dies ist notwendig, da Treiber diese Felder waehrend db_handle_factory() mutieren koennen:
- PostgreSQL -- ersetzt
;durch Leerzeichen indata_source - MySQL -- alloziert
username/passwordaus DSN, wenn sie nicht uebergeben wurden - ODBC -- baut
data_sourcekomplett um und bettet Zugangsdaten ein
Nach einem erfolgreichen db_handle_factory()-Aufruf werden die Kopien ueber efree() freigegeben. Bei Fehlern erfolgt die Freigabe ueber pdo_pool_free_conn(), die auch vom Destruktor des Pools verwendet wird.
Inkompatibilitaet mit persistenten Verbindungen
Persistente Verbindungen (PDO::ATTR_PERSISTENT) sind mit dem Pool inkompatibel. Eine persistente Verbindung ist an den Prozess gebunden und ueberlebt Anfragen, waehrend der Pool Verbindungen auf Anfrageebene mit automatischem Lebenszyklus-Management erstellt. Der Versuch, beide Attribute gleichzeitig zu aktivieren, fuehrt zu einem Fehler.
Was kommt als Naechstes?
- PDO Pool: Verbindungspool -- Nutzungsanleitung
- Koroutinen -- wie Koroutinen funktionieren
- Scope -- Verwaltung von Koroutinengruppen