Seal live dashboard event gaps

This commit is contained in:
Thanakorn S
2026-05-25 17:02:52 +07:00
parent 4733c78ac6
commit 4f884177a5
13 changed files with 147 additions and 17 deletions
+29 -2
View File
@@ -69,17 +69,43 @@ function db_check($sth, &$answer, $options = []) {
// Run a callback after the surrounding dbTransaction() commits. If no managed
// transaction is active, run immediately. Used for non-authoritative side effects
// such as live dashboard notifications.
function db_after_commit(callable $callback): void {
if (!isset($GLOBALS['_db_after_commit_stack']) || empty($GLOBALS['_db_after_commit_stack'])) {
$callback();
return;
}
$idx = count($GLOBALS['_db_after_commit_stack']) - 1;
$GLOBALS['_db_after_commit_stack'][$idx][] = $callback;
}
// transaction wrapper with retry logic for deadlocks
function dbTransaction($pdo, $callback, $maxRetries = 3) {
for ($attempt = 0; $attempt < $maxRetries; $attempt++) {
$GLOBALS['_db_after_commit_stack'][] = [];
try {
$pdo->beginTransaction();
$result = $callback($pdo);
$pdo->commit();
$callbacks = array_pop($GLOBALS['_db_after_commit_stack']);
foreach ($callbacks as $after_commit) {
try {
$after_commit();
} catch (Throwable $e) {
error_log("[AFTER COMMIT ERROR] " . $e->getMessage() . " | " . $e->getFile() . ":" . $e->getLine());
}
}
return $result;
} catch (PDOException $e) {
$pdo->rollBack();
if ($pdo->inTransaction()) $pdo->rollBack();
array_pop($GLOBALS['_db_after_commit_stack']);
if ($e->getCode() == '40001' && $attempt < $maxRetries - 1) {
usleep(600000 * ($attempt + 1));
@@ -90,7 +116,8 @@ function dbTransaction($pdo, $callback, $maxRetries = 3) {
throw $e;
} catch (Exception $e) {
$pdo->rollBack();
if ($pdo->inTransaction()) $pdo->rollBack();
array_pop($GLOBALS['_db_after_commit_stack']);
error_log("[ERROR] " . $e->getMessage() . " | " . $e->getFile() . ":" . $e->getLine());
throw $e;
}