{"record":{"id":"57aefad202355bd1","repo":"phacility/phabricator","slug":"failed-to-acquire-read-lock-after-waiting-s-secon","errorCode":null,"errorMessage":"Failed to acquire read lock after waiting %s second(s). You may be able to retry later. (%s)","messagePattern":"Failed to acquire read lock after waiting (.+?) second\\(s\\)\\. You may be able to retry later\\. \\((.+?)\\)","errorType":"exception","errorClass":"PhutilProxyException","httpStatus":null,"severity":"error","filePath":"src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php","lineNumber":192,"sourceCode":"        $device->getName()));\n\n    try {\n      $start = PhabricatorTime::getNow();\n      $read_lock->lock($lock_wait);\n      $waited = (PhabricatorTime::getNow() - $start);\n\n      if ($waited) {\n        $this->logLine(\n          pht(\n            'Acquired read lock after %s second(s).',\n            new PhutilNumber($waited)));\n      } else {\n        $this->logLine(\n          pht(\n            'Acquired read lock immediately.'));\n      }\n    } catch (PhutilLockException $ex) {\n      throw new PhutilProxyException(\n        pht(\n          'Failed to acquire read lock after waiting %s second(s). You '.\n          'may be able to retry later. (%s)',\n          new PhutilNumber($lock_wait),\n          $ex->getHint()),\n        $ex);\n    }\n\n    $versions = PhabricatorRepositoryWorkingCopyVersion::loadVersions(\n      $repository_phid);\n    $versions = mpull($versions, null, 'getDevicePHID');\n\n    $this_version = idx($versions, $device_phid);\n    if ($this_version) {\n      $this_version = (int)$this_version->getRepositoryVersion();\n    } else {\n      $this_version = null;\n    }","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php#L174-L210","documentation":"Before reading a clustered working copy, the engine acquires a per-repository global read lock with a bounded wait; PhutilLockException on timeout is re-thrown as a PhutilProxyException carrying both the wait budget and the underlying lock hint. Another process or device held the repository's read lock longer than lock_wait seconds, so this reader gave up before starting.","triggerScenarios":"Two or more actors contend for the same repository lock: pull daemons, discovery, a push on another cluster device, or a manual 'bin/repository update' run while a big clone/sync holds the lock past the wait budget.","commonSituations":"Initial imports or very large repositories where sync takes hours; overlapping daemon jobs; stale locks left by killed daemons; operators running maintenance commands during busy periods.","solutions":["Retry once the current lock holder finishes; check the daemon logs to confirm the holder is actually progressing (usually transient contention)","Reduce overlap: pause the repository's daemons or scheduled jobs before running manual bin/repository update or indexing","If contention never clears, find the stuck holder (bin/phd status, daemon logs) and restart that daemon to release a leaked lock"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// reduce contention before dispatch: skip if a sync job for this repo is already running\n$running = id(new PhabricatorWorkerActiveTaskQuery())\n  ->setViewer(PhabricatorUser::getOmnipotentUser())\n  ->withClass('RepositoryUpdateWorker')\n  ->execute();","typeGuard":null,"tryCatchPattern":"$attempts = 3;\nfor ($i = 0; $i < $attempts; $i++) {\n  try {\n    return $engine->synchronizeWorkingCopyBeforeRead();\n  } catch (PhutilProxyException $ex) {\n    if (!($ex->getPrevious() instanceof PhutilLockException)) {\n      throw $ex;\n    }\n    sleep(5 * (2 ** $i)); // exponential backoff around lock contention\n  }\n}\nthrow $ex;","preventionTips":["Stagger daemon schedules so discovery and pull for one repository do not overlap","Run manual bin/repository maintenance with that repository's daemons paused","Monitor lock wait durations: sustained waits indicate a stuck holder, not normal contention"],"tags":["locking","concurrency","cluster","timeout","phabricator"],"backgroundTag":"lock-acquisition-timeout","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}