/** * Issue a commit on all masters who are currently in a transaction and have * made changes to the database. It also supports sometimes waiting for the * local wiki's replica DBs to catch up. See the documentation for * $wgJobSerialCommitThreshold for more. * * @param LBFactory $lbFactory * @param Job $job * @param string $fnameTrxOwner * @throws DBError */ private function commitMasterChanges(LBFactory $lbFactory, Job $job, $fnameTrxOwner) { global $wgJobSerialCommitThreshold; $time = false; $lb = $lbFactory->getMainLB(wfWikiID()); if ($wgJobSerialCommitThreshold !== false && $lb->getServerCount() > 1) { // Generally, there is one master connection to the local DB $dbwSerial = $lb->getAnyOpenConnection($lb->getWriterIndex()); // We need natively blocking fast locks if ($dbwSerial && $dbwSerial->namedLocksEnqueue()) { $time = $dbwSerial->pendingWriteQueryDuration($dbwSerial::ESTIMATE_DB_APPLY); if ($time < $wgJobSerialCommitThreshold) { $dbwSerial = false; } } else { $dbwSerial = false; } } else { // There are no replica DBs or writes are all to foreign DB (we don't handle that) $dbwSerial = false; } if (!$dbwSerial) { $lbFactory->commitMasterChanges($fnameTrxOwner); return; } $ms = intval(1000 * $time); $msg = $job->toString() . " COMMIT ENQUEUED [{$ms}ms of writes]"; $this->logger->info($msg); $this->debugCallback($msg); // Wait for an exclusive lock to commit if (!$dbwSerial->lock('jobrunner-serial-commit', __METHOD__, 30)) { // This will trigger a rollback in the main loop throw new DBError($dbwSerial, "Timed out waiting on commit queue."); } $unlocker = new ScopedCallback(function () use($dbwSerial) { $dbwSerial->unlock('jobrunner-serial-commit', __METHOD__); }); // Wait for the replica DBs to catch up $pos = $lb->getMasterPos(); if ($pos) { $lb->waitForAll($pos); } // Actually commit the DB master changes $lbFactory->commitMasterChanges($fnameTrxOwner); ScopedCallback::consume($unlocker); }