From 40e8cb01036cf45da13e6ec13cf8b2676056fbf4 Mon Sep 17 00:00:00 2001 From: Matthias Neid Date: Tue, 10 Feb 2026 14:11:27 +0100 Subject: [PATCH 1/4] flexible mysql retry handling --- src/Driver/Mysqli/Mysqli.php | 70 +++++++++++++------------ src/Driver/Mysqli/RetryCollection.php | 50 ++++++++++++++++++ src/Driver/Mysqli/RetryGroup.php | 74 +++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 34 deletions(-) create mode 100644 src/Driver/Mysqli/RetryCollection.php create mode 100644 src/Driver/Mysqli/RetryGroup.php diff --git a/src/Driver/Mysqli/Mysqli.php b/src/Driver/Mysqli/Mysqli.php index 0d66a41..620e58b 100644 --- a/src/Driver/Mysqli/Mysqli.php +++ b/src/Driver/Mysqli/Mysqli.php @@ -28,6 +28,10 @@ class Mysqli extends Driver implements CRUDAbleInterface, CRUDQueryableInterface { public const string ID = "mysqli"; + + /** @var int[] */ + protected const array CONNECTION_ERROR_CODES = [2006, 2013]; + protected string $id = self::ID; /** @@ -77,8 +81,6 @@ class Mysqli extends Driver implements CRUDAbleInterface, CRUDQueryableInterface */ protected ?\mysqli $connection = null; - protected int $connectionRetries = 1; - /** * Mysqli constructor. * @@ -123,6 +125,17 @@ protected function reconnect(): void $this->connect(); } + /** + * @return RetryCollection + */ + protected function getRetryCollection(): RetryCollection + { + return new RetryCollection([ + new RetryGroup(static::CONNECTION_ERROR_CODES, 1), // connection + new RetryGroup([1213], 3) // deadlock + ]); + } + /** * Execute a mysql query * @@ -134,24 +147,19 @@ protected function reconnect(): void protected function rawQuery(string $query): mysqli_result|true { $this->connect(); - $retries = $this->connectionRetries; + $retries = $this->getRetryCollection(); while (true) { try { return $this->connection->query($query); } catch (mysqli_sql_exception $e) { - // no more retries left - if ($retries <= 0) { - throw MysqliException::fromException($e); - } - - // connection error, try to reconnect and retry - if ($e->getCode() === 2006 || $e->getCode() === 2013) { - $this->reconnect(); - $retries--; + if ($retries->canRetry($e->getCode())) { + if (in_array($e->getCode(), static::CONNECTION_ERROR_CODES)) { + $this->reconnect(); + } + $this->handleRetriedException($e); continue; } - // other error, throw exception throw MysqliException::fromException($e); } } @@ -166,29 +174,33 @@ protected function rawQuery(string $query): mysqli_result|true protected function escape(string $data): string { $this->connect(); - $retries = $this->connectionRetries; + $retries = $this->getRetryCollection(); while (true) { try { return $this->connection->real_escape_string($data); } catch (mysqli_sql_exception $e) { - // no more retries left - if ($retries <= 0) { - throw MysqliException::fromException($e); - } - - // connection error, try to reconnect and retry - if ($e->getCode() === 2006 || $e->getCode() === 2013) { - $this->reconnect(); - $retries--; + if ($retries->canRetry($e->getCode())) { + if (in_array($e->getCode(), static::CONNECTION_ERROR_CODES)) { + $this->reconnect(); + } + $this->handleRetriedException($e); continue; } - // other error, throw exception throw MysqliException::fromException($e); } } } + /** + * @param mysqli_sql_exception $exception + * @return void + */ + protected function handleRetriedException(mysqli_sql_exception $exception): void + { + // can be used to log retried exceptions + } + /** * Save the model * @@ -383,14 +395,4 @@ public function setDatabase(string $database): Mysqli $this->database = $database; return $this; } - - /** - * @param int $connectionRetries - * @return $this - */ - public function setConnectionRetries(int $connectionRetries): static - { - $this->connectionRetries = $connectionRetries; - return $this; - } } diff --git a/src/Driver/Mysqli/RetryCollection.php b/src/Driver/Mysqli/RetryCollection.php new file mode 100644 index 0000000..02808f0 --- /dev/null +++ b/src/Driver/Mysqli/RetryCollection.php @@ -0,0 +1,50 @@ +groups[] = $group; + return $this; + } + + /** + * @param array $statusCodes + * @return $this + */ + public function removeGroup(array $statusCodes): static + { + $this->groups = array_filter($this->groups, function (RetryGroup $group) use ($statusCodes) { + return !$group->matchesStatusCodes($statusCodes); + }); + return $this; + } + + /** + * @param int $statusCode + * @return bool + */ + public function canRetry(int $statusCode): bool + { + foreach ($this->groups as $group) { + if ($group->matchesStatusCode($statusCode) && $group->hasRetriesLeft()) { + $group->addRetry(); + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/src/Driver/Mysqli/RetryGroup.php b/src/Driver/Mysqli/RetryGroup.php new file mode 100644 index 0000000..9d2af75 --- /dev/null +++ b/src/Driver/Mysqli/RetryGroup.php @@ -0,0 +1,74 @@ +maxRetries; + } + + /** + * @return void + */ + public function addRetry(): void + { + $this->retries++; + } + + /** + * @return bool + */ + public function hasRetriesLeft(): bool + { + return $this->retries < $this->maxRetries; + } + + /** + * @return int[] + */ + public function getStatusCodes(): array + { + return $this->statusCodes; + } + + /** + * @param int $statusCode + * @return bool + */ + public function matchesStatusCode(int $statusCode): bool + { + return in_array($statusCode, $this->statusCodes); + } + + /** + * @param array $statusCodes + * @return bool + */ + public function matchesStatusCodes(array $statusCodes): bool + { + foreach ($statusCodes as $statusCode) { + if ($this->matchesStatusCode($statusCode)) { + return true; + } + } + return false; + } +} \ No newline at end of file From 12b49a0fe733191f7a3c9a3275043d3e1f6c40c0 Mon Sep 17 00:00:00 2001 From: Matthias Neid Date: Tue, 10 Feb 2026 14:15:50 +0100 Subject: [PATCH 2/4] strict array checks & array reindexing --- src/Driver/Mysqli/Mysqli.php | 4 ++-- src/Driver/Mysqli/RetryCollection.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Driver/Mysqli/Mysqli.php b/src/Driver/Mysqli/Mysqli.php index 620e58b..fc1a1c8 100644 --- a/src/Driver/Mysqli/Mysqli.php +++ b/src/Driver/Mysqli/Mysqli.php @@ -153,7 +153,7 @@ protected function rawQuery(string $query): mysqli_result|true return $this->connection->query($query); } catch (mysqli_sql_exception $e) { if ($retries->canRetry($e->getCode())) { - if (in_array($e->getCode(), static::CONNECTION_ERROR_CODES)) { + if (in_array($e->getCode(), static::CONNECTION_ERROR_CODES, true)) { $this->reconnect(); } $this->handleRetriedException($e); @@ -180,7 +180,7 @@ protected function escape(string $data): string return $this->connection->real_escape_string($data); } catch (mysqli_sql_exception $e) { if ($retries->canRetry($e->getCode())) { - if (in_array($e->getCode(), static::CONNECTION_ERROR_CODES)) { + if (in_array($e->getCode(), static::CONNECTION_ERROR_CODES, true)) { $this->reconnect(); } $this->handleRetriedException($e); diff --git a/src/Driver/Mysqli/RetryCollection.php b/src/Driver/Mysqli/RetryCollection.php index 02808f0..bded349 100644 --- a/src/Driver/Mysqli/RetryCollection.php +++ b/src/Driver/Mysqli/RetryCollection.php @@ -27,9 +27,9 @@ public function addGroup(RetryGroup $group): static */ public function removeGroup(array $statusCodes): static { - $this->groups = array_filter($this->groups, function (RetryGroup $group) use ($statusCodes) { + $this->groups = array_values(array_filter($this->groups, function (RetryGroup $group) use ($statusCodes) { return !$group->matchesStatusCodes($statusCodes); - }); + })); return $this; } From 2c8258299c00d6b15a3ec2c1ec507fe902dfe912 Mon Sep 17 00:00:00 2001 From: Matthias Neid Date: Tue, 10 Feb 2026 14:58:58 +0100 Subject: [PATCH 3/4] fix phpdoc --- src/Driver/Mysqli/RetryGroup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Driver/Mysqli/RetryGroup.php b/src/Driver/Mysqli/RetryGroup.php index 9d2af75..0f4310e 100644 --- a/src/Driver/Mysqli/RetryGroup.php +++ b/src/Driver/Mysqli/RetryGroup.php @@ -59,7 +59,7 @@ public function matchesStatusCode(int $statusCode): bool } /** - * @param array $statusCodes + * @param int[] $statusCodes * @return bool */ public function matchesStatusCodes(array $statusCodes): bool From 91ad392e3500691d7740538588732d000b05ae8f Mon Sep 17 00:00:00 2001 From: Matthias Neid Date: Wed, 11 Feb 2026 11:27:11 +0100 Subject: [PATCH 4/4] fix phpdoc --- src/Driver/Mysqli/RetryCollection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Driver/Mysqli/RetryCollection.php b/src/Driver/Mysqli/RetryCollection.php index bded349..4e9a60c 100644 --- a/src/Driver/Mysqli/RetryCollection.php +++ b/src/Driver/Mysqli/RetryCollection.php @@ -22,7 +22,7 @@ public function addGroup(RetryGroup $group): static } /** - * @param array $statusCodes + * @param int[] $statusCodes * @return $this */ public function removeGroup(array $statusCodes): static