From 5e97b60ebf3f30c373b70c17456c2e376308f760 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Sat, 14 Sep 2024 17:49:40 +0200 Subject: [PATCH] database.php: add retry_on_deadlock utility function --- inc/database.php | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/inc/database.php b/inc/database.php index d6dd21c2..c2612493 100644 --- a/inc/database.php +++ b/inc/database.php @@ -6,6 +6,9 @@ defined('TINYBOARD') or exit; +// https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html +const MYSQL_ER_LOCK_DEADLOCK = 1213; + class PreparedQueryDebug { protected $query, $explain_query = false; @@ -147,6 +150,32 @@ function query($query) { return $pdo->query($query); } +/** + * @param int $max_retries The maximum number of times the callable may be re-executed on deadlock detection. + * @param callable $query_func A callable that executes a query. Must throw a PDOException on deadlocks. + * @return mixed The value returned by $query_func. + * @throws PDOException Throws once the maximum number of retries have been attempted. + */ +function retry_on_deadlock(int $max_retries, callable $query_func) { + // Add the first original attempt to the max count. + $max_retries += 1; + $attempt = 0; + + while (true) { + $attempt++; + + try { + return $query_func(); + } catch (\PDOException $e) { + if ($e->errorInfo === null || $e->errorInfo[1] != MYSQL_ER_LOCK_DEADLOCK || $attempt >= $max_retries) { + throw $e; + } + // Wait for 0.5s before retrying... + usleep(500000); + } + } +} + function db_error($PDOStatement = null) { global $pdo, $db_error;