/** * Wait for the slave to catch up to a given master position. * @todo Return values for this and base class are rubbish * * @param DBMasterPos|MySQLMasterPos $pos * @param int $timeout The maximum number of seconds to wait for synchronisation * @return int Zero if the slave was past that position already, * greater than zero if we waited for some period of time, less than * zero if we timed out. */ function masterPosWait(DBMasterPos $pos, $timeout) { if ($this->lastKnownSlavePos && $this->lastKnownSlavePos->hasReached($pos)) { return '0'; // http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html } # Commit any open transactions $this->commit(__METHOD__, 'flush'); # Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set $encFile = $this->addQuotes($pos->file); $encPos = intval($pos->pos); $sql = "SELECT MASTER_POS_WAIT({$encFile}, {$encPos}, {$timeout})"; $res = $this->doQuery($sql); $status = false; if ($res) { $row = $this->fetchRow($res); if ($row) { $status = $row[0]; // can be NULL, -1, or 0+ per the MySQL manual if (ctype_digit($status)) { // success $this->lastKnownSlavePos = $pos; } } } return $status; }
function masterPosWait(DBMasterPos $pos, $timeout) { if (!$pos instanceof MySQLMasterPos) { throw new InvalidArgumentException("Position not an instance of MySQLMasterPos"); } if ($this->getLBInfo('is static') === true) { return 0; // this is a copy of a read-only dataset with no master DB } elseif ($this->lastKnownReplicaPos && $this->lastKnownReplicaPos->hasReached($pos)) { return 0; // already reached this point for sure } // Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set if ($this->useGTIDs && $pos->gtids) { // Wait on the GTID set (MariaDB only) $gtidArg = $this->addQuotes(implode(',', $pos->gtids)); $res = $this->doQuery("SELECT MASTER_GTID_WAIT({$gtidArg}, {$timeout})"); } else { // Wait on the binlog coordinates $encFile = $this->addQuotes($pos->file); $encPos = intval($pos->pos); $res = $this->doQuery("SELECT MASTER_POS_WAIT({$encFile}, {$encPos}, {$timeout})"); } $row = $res ? $this->fetchRow($res) : false; if (!$row) { throw new DBExpectedError($this, "Failed to query MASTER_POS_WAIT()"); } // Result can be NULL (error), -1 (timeout), or 0+ per the MySQL manual $status = $row[0] !== null ? intval($row[0]) : null; if ($status === null) { // T126436: jobs programmed to wait on master positions might be referencing binlogs // with an old master hostname. Such calls make MASTER_POS_WAIT() return null. Try // to detect this and treat the replica DB as having reached the position; a proper master // switchover already requires that the new master be caught up before the switch. $replicationPos = $this->getReplicaPos(); if ($replicationPos && !$replicationPos->channelsMatch($pos)) { $this->lastKnownReplicaPos = $replicationPos; $status = 0; } } elseif ($status >= 0) { // Remember that this position was reached to save queries next time $this->lastKnownReplicaPos = $pos; } return $status; }
/** * Wait for the slave to catch up to a given master position. * @todo Return values for this and base class are rubbish * * @param DBMasterPos|MySQLMasterPos $pos * @param int $timeout The maximum number of seconds to wait for synchronisation * @return int Zero if the slave was past that position already, * greater than zero if we waited for some period of time, less than * zero if we timed out. */ function masterPosWait(DBMasterPos $pos, $timeout) { if ($this->lastKnownSlavePos && $this->lastKnownSlavePos->hasReached($pos)) { return '0'; // http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html } wfProfileIn(__METHOD__); # Commit any open transactions $this->commit(__METHOD__, 'flush'); if (!is_null($this->mFakeSlaveLag)) { $wait = intval(($pos->pos - microtime(true) + $this->mFakeSlaveLag) * 1000000.0); if ($wait > $timeout * 1000000.0) { wfDebug("Fake slave timed out waiting for {$pos} ({$wait} us)\n"); wfProfileOut(__METHOD__); return -1; } elseif ($wait > 0) { wfDebug("Fake slave waiting {$wait} us\n"); usleep($wait); wfProfileOut(__METHOD__); return 1; } else { wfDebug("Fake slave up to date ({$wait} us)\n"); wfProfileOut(__METHOD__); return 0; } } # Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set $encFile = $this->addQuotes($pos->file); $encPos = intval($pos->pos); $sql = "SELECT MASTER_POS_WAIT({$encFile}, {$encPos}, {$timeout})"; $res = $this->doQuery($sql); $status = false; if ($res && ($row = $this->fetchRow($res))) { $status = $row[0]; // can be NULL, -1, or 0+ per the MySQL manual if (ctype_digit($status)) { // success $this->lastKnownSlavePos = $pos; } } wfProfileOut(__METHOD__); return $status; }