{"record":{"id":"75ef1b2365b24ac8","repo":"phacility/phabricator","slug":"1213","errorCode":"1213","errorMessage":"#%d: %s","messagePattern":"#%d: %s","errorType":"exception","errorClass":"AphrontDeadlockQueryException","httpStatus":null,"severity":"warning","filePath":"src/infrastructure/storage/connection/mysql/AphrontBaseMySQLDatabaseConnection.php","lineNumber":338,"sourceCode":"    }\n    $this->throwQueryCodeException($errno, $error);\n  }\n\n  private function throwCommonException($errno, $error) {\n    $message = pht('#%d: %s', $errno, $error);\n\n    switch ($errno) {\n      case 2013: // Connection Dropped\n        throw new AphrontConnectionLostQueryException($message);\n      case 2006: // Gone Away\n        $more = pht(\n          'This error may occur if your configured MySQL \"wait_timeout\" or '.\n          '\"max_allowed_packet\" values are too small. This may also indicate '.\n          'that something used the MySQL \"KILL <process>\" command to kill '.\n          'the connection running the query.');\n        throw new AphrontConnectionLostQueryException(\"{$message}\\n\\n{$more}\");\n      case 1213: // Deadlock\n        throw new AphrontDeadlockQueryException($message);\n      case 1205: // Lock wait timeout exceeded\n        throw new AphrontLockTimeoutQueryException($message);\n      case 1062: // Duplicate Key\n        // NOTE: In some versions of MySQL we get a key name back here, but\n        // older versions just give us a key index (\"key 2\") so it's not\n        // portable to parse the key out of the error and attach it to the\n        // exception.\n        throw new AphrontDuplicateKeyQueryException($message);\n      case 1044: // Access denied to database\n      case 1142: // Access denied to table\n      case 1143: // Access denied to column\n      case 1227: // Access denied (e.g., no SUPER for SHOW SLAVE STATUS).\n\n        // See T13622. Try to help users figure out that this is a GRANT\n        // problem.\n\n        $more = pht(\n          'This error usually indicates that you need to \"GRANT\" the '.","sourceCodeStart":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/infrastructure/storage/connection/mysql/AphrontBaseMySQLDatabaseConnection.php#L320-L356","documentation":"MySQL errno 1213 'Deadlock found when trying to get lock', translated into AphrontDeadlockQueryException. Two transactions each hold locks the other needs; InnoDB detects the cycle and sacrifices one with this error. Deadlocks are normal in concurrent OLTP systems — the documented remedy is to roll back and retry the whole transaction, ideally in a new transaction. Phabricator marks this exception class as retryable and its workers re-run such queries.","triggerScenarios":"Two workers updating overlapping rows in opposite orders (e.g. editing the same task's subpriority/transactions concurrently); batch scripts locking many rows in inconsistent order; transactions that read-then-write on the same keys racing each other.","commonSituations":"Bulk scripts adjusting task subpriorities or project memberships while users edit in the UI; heavy daemon activity on holidays/peak load; multiple queue workers processing tasks touching the same objects.","solutions":["Retry the transaction — this is the canonical fix; Phabricator daemons already retry deadlock exceptions automatically","In custom scripts, wrap the whole unit of work and re-run it on AphrontDeadlockQueryException (new transaction, small backoff)","Access rows in a consistent global order (e.g. ORDER BY id ... FOR UPDATE) to shrink deadlock windows","Keep transactions short: avoid holding locks across slow computation or external calls"],"exampleFix":"// before: single attempt\n$object->openTransaction(12); /* ...writes... */ $object->saveTransaction();\n\n// after: retry the whole transaction on deadlock\n$attempt = 0;\ndo {\n  try {\n    $object->openTransaction(12);\n    /* ...writes... */\n    $object->saveTransaction();\n    break;\n  } catch (AphrontDeadlockQueryException $ex) {\n    $object->killTransaction();\n    usleep(50000 * ++$attempt); // backoff 50ms, 100ms, ...\n  }\n} while ($attempt < 5);","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  $object->openTransaction(12);\n  /* ...writes... */\n  $object->saveTransaction();\n} catch (AphrontDeadlockQueryException $ex) {\n  $object->killTransaction();\n  usleep(100000); // small backoff, then retry the WHOLE transaction\n}","preventionTips":["Retry the entire transaction on deadlock — never resume a partial one","Touch rows in a consistent order (sorted by id) inside multi-row transactions","Keep transactions short: no external calls or heavy computation while holding locks"],"tags":["database","mysql","deadlock","concurrency","phabricator"],"backgroundTag":"mysql-deadlock","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}