Skip to content

Commit b7fa576

Browse files
committed
Fix simultaneous requests to prepare the same query
1 parent 01f9b5c commit b7fa576

File tree

1 file changed

+32
-13
lines changed

1 file changed

+32
-13
lines changed

src/Pool.php

+32-13
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
use Amp\Sql\ResultSet as SqlResultSet;
1414
use Amp\Sql\Statement as SqlStatement;
1515
use Amp\Sql\Transaction as SqlTransaction;
16-
use Amp\Success;
1716
use cash\LRUCache;
1817
use function Amp\call;
1918

@@ -64,14 +63,18 @@ public function getIterator(): \Iterator
6463
$this->statementWatcher = Loop::repeat(1000, static function () use (&$idleTimeout, $statements) {
6564
$now = \time();
6665

67-
foreach ($statements as $hash => $statement) {
66+
foreach ($statements as $sql => $statement) {
67+
if ($statement instanceof Promise) {
68+
continue;
69+
}
70+
6871
\assert($statement instanceof StatementPool);
6972

7073
if ($statement->getLastUsedAt() + $idleTimeout > $now) {
7174
return;
7275
}
7376

74-
$statements->remove($hash);
77+
$statements->remove($sql);
7578
}
7679
});
7780

@@ -143,18 +146,34 @@ public function prepare(string $sql): Promise
143146
throw new \Error("The pool has been closed");
144147
}
145148

146-
if ($this->statements->containsKey($sql)) {
147-
$statement = $this->statements->get($sql);
148-
\assert($statement instanceof SqlStatement);
149-
if ($statement->isAlive()) {
150-
return new Success($statement);
149+
return call(function () use ($sql) {
150+
if ($this->statements->containsKey($sql)) {
151+
$statement = $this->statements->get($sql);
152+
153+
if ($statement instanceof Promise) {
154+
$statement = yield $statement; // Wait for prior request to resolve.
155+
}
156+
157+
\assert($statement instanceof StatementPool);
158+
159+
if ($statement->isAlive()) {
160+
return $statement;
161+
}
151162
}
152-
}
153163

154-
return call(function () use ($sql) {
155-
$statement = yield parent::prepare($sql);
156-
\assert($statement instanceof SqlStatement);
157-
$this->statements->put($sql, $statement);
164+
$promise = parent::prepare($sql);
165+
$this->statements->put($sql, $promise); // Insert promise into queue so subsequent requests get promise.
166+
167+
try {
168+
$statement = yield $promise;
169+
\assert($statement instanceof StatementPool);
170+
} catch (\Throwable $exception) {
171+
$this->statements->remove($sql);
172+
throw $exception;
173+
}
174+
175+
$this->statements->put($sql, $statement); // Replace promise in queue with statement object.
176+
158177
return $statement;
159178
});
160179
}

0 commit comments

Comments
 (0)