Skip to content

Commit 48dbdc2

Browse files
committed
fix(pool): keep capability answers per pool, not per handle
A handle is usually built for one request around a pool that lives as long as the process, so memoizing on the handle still checked a connection out for every distinct getter on every request. The answers now live in a WeakMap keyed by the pool, so each one is asked once per pool. Attribute support has a setter and stays per handle.
1 parent f829c7f commit 48dbdc2

1 file changed

Lines changed: 43 additions & 16 deletions

File tree

‎src/Database/Adapter/Pool.php‎

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,33 +40,47 @@ class Pool extends Adapter
4040

4141
/**
4242
* Answers to the getters that describe the adapter rather than a
43-
* connection, by method.
43+
* connection, by pool and method.
4444
*
4545
* Every connection in a pool is built by the same factory, so it is the
4646
* same adapter class on the same DSN, and these answers are the same
4747
* whichever connection gives them. Delegating them checked a connection
4848
* out and replayed the whole handle state onto it to read a constant, and
49-
* the hot path asks several per document. Getters that read connection or
50-
* handle state (the driver, the connection id, anything that varies with
51-
* shared tables or with how a connection was set up) still delegate, and
52-
* the one capability with a setter is updated by it.
49+
* the hot path asks several per document. They are kept per pool rather
50+
* than per handle because a handle is usually built for one request,
51+
* while its pool lives as long as the process. Getters that read
52+
* connection or handle state (the driver, the connection id, anything
53+
* that varies with shared tables or with how a connection was set up)
54+
* still delegate.
5355
*
54-
* @var array<string, mixed>
56+
* @var \WeakMap<UtopiaPool<covariant Adapter>, array<string, mixed>>
5557
*/
56-
private array $capabilities = [];
58+
private static \WeakMap $capabilities;
5759

5860
/**
5961
* Attribute support this handle asked for, replayed on every checkout so
60-
* each connection runs with the value the memoized getter reports.
62+
* each connection runs with the value the getter reports.
6163
*/
6264
private ?bool $supportForAttributes = null;
6365

66+
/**
67+
* Attribute support as the adapter reports it to this handle. It is the
68+
* one capability with a setter, so it is kept per handle, not per pool.
69+
*/
70+
private ?bool $reportedSupportForAttributes = null;
71+
6472
/**
6573
* @param UtopiaPool<covariant Adapter> $pool The pool to use for connections. Must contain instances of Adapter.
6674
*/
6775
public function __construct(UtopiaPool $pool)
6876
{
6977
$this->pool = $pool;
78+
79+
if (!isset(self::$capabilities)) {
80+
/** @var \WeakMap<UtopiaPool<covariant Adapter>, array<string, mixed>> $capabilities */
81+
$capabilities = new \WeakMap();
82+
self::$capabilities = $capabilities;
83+
}
7084
}
7185

7286
/**
@@ -129,11 +143,20 @@ public function delegate(string $method, array $args): mixed
129143
*/
130144
protected function capability(string $method): mixed
131145
{
132-
if (!\array_key_exists($method, $this->capabilities)) {
133-
$this->capabilities[$method] = $this->delegate($method, []);
146+
$answers = self::$capabilities[$this->pool] ?? [];
147+
148+
if (\array_key_exists($method, $answers)) {
149+
return $answers[$method];
134150
}
135151

136-
return $this->capabilities[$method];
152+
$answer = $this->delegate($method, []);
153+
154+
// The checkout can yield to a coroutine that kept another answer meanwhile
155+
$answers = self::$capabilities[$this->pool] ?? [];
156+
$answers[$method] = $answer;
157+
self::$capabilities[$this->pool] = $answers;
158+
159+
return $answer;
137160
}
138161

139162
public function getDriver(): mixed
@@ -287,14 +310,18 @@ public function rollbackTransaction(): bool
287310
*/
288311
public function getHostname(): string
289312
{
290-
if (!empty($this->capabilities[__FUNCTION__])) {
291-
return $this->capabilities[__FUNCTION__];
313+
$hostname = self::$capabilities[$this->pool][__FUNCTION__] ?? '';
314+
315+
if ($hostname !== '') {
316+
return $hostname;
292317
}
293318

294319
$hostname = $this->delegate(__FUNCTION__, \func_get_args());
295320

296321
if ($hostname !== '') {
297-
$this->capabilities[__FUNCTION__] = $hostname;
322+
$answers = self::$capabilities[$this->pool] ?? [];
323+
$answers[__FUNCTION__] = $hostname;
324+
self::$capabilities[$this->pool] = $answers;
298325
}
299326

300327
return $hostname;
@@ -579,7 +606,7 @@ public function getSupportForSchemas(): bool
579606

580607
public function getSupportForAttributes(): bool
581608
{
582-
return $this->capability(__FUNCTION__);
609+
return $this->reportedSupportForAttributes ??= $this->delegate(__FUNCTION__, \func_get_args());
583610
}
584611

585612
public function getSupportForSchemaAttributes(): bool
@@ -915,7 +942,7 @@ public function setSupportForAttributes(bool $support): bool
915942
{
916943
$this->supportForAttributes = $support;
917944

918-
return $this->capabilities['getSupportForAttributes'] = $this->delegate(__FUNCTION__, \func_get_args());
945+
return $this->reportedSupportForAttributes = $this->delegate(__FUNCTION__, \func_get_args());
919946
}
920947

921948
public function getSupportForIntegerBooleans(): bool

0 commit comments

Comments
 (0)