/** * Returns a range of entity IDs to delete, based on this updater's batch size. * * @param array &$continuation * * @return bool|string[] list( $minId, $maxId, $count ), or false if there is nothing to delete */ private function getDeletionRange(array &$continuation = null) { $dbr = $this->repoConnectionManager->getReadConnection(); $conditions = array('cs_subscriber_id' => $this->subscriberWikiId); if (!empty($continuation)) { list($fromEntityId) = $continuation; $conditions[] = 'cs_entity_id > ' . $dbr->addQuotes($fromEntityId); } /** * @note Below, we query and iterate all rows we want to delete in the current batch. That * is rather ugly, but appears to be the best solution, because: * * - Deletions must be paged to avoid lock retention. * - DELETE does not support LIMIT, so we need to know a range (min/max) of IDs. * - GROUP BY does not support LIMIT, so we cannot use aggregate functions to get the * min/max IDs. * * Thus, using SELECT ... LIMIT seems to be the only reliable way to get the min/max range * needed for batched deletion. */ $res = $dbr->select('wb_changes_subscription', array('cs_entity_id'), $conditions, __METHOD__, array('ORDER BY' => 'cs_entity_id', 'LIMIT' => $this->batchSize)); $this->repoConnectionManager->releaseConnection($dbr); $subscriptions = $this->getEntityIdsFromRows($res, 'cs_entity_id', $continuation); if (empty($subscriptions)) { return false; } $minId = reset($subscriptions); $maxId = end($subscriptions); $count = count($subscriptions); return array($minId, $maxId, $count); }
/** * @see UsageLookup::getUnusedEntities * * @param EntityId[] $entityIds * * @return EntityId[] * @throws UsageTrackerException */ public function getUnusedEntities(array $entityIds) { if (empty($entityIds)) { return array(); } $db = $this->connectionManager->getReadConnection(); $usageTable = $this->newUsageTable($db); $unused = $usageTable->getUnusedEntities($entityIds); $this->connectionManager->releaseConnection($db); return $unused; }
public function testBeginAtomicSection() { $connection = $this->getConnectionMock(); $lb = $this->getLoadBalancerMock(); $lb->expects($this->exactly(2))->method('getConnection')->with(DB_MASTER)->will($this->returnValue($connection)); $connection->expects($this->once())->method('startAtomic')->will($this->returnValue(null)); $manager = new ConsistentReadConnectionManager($lb); $manager->beginAtomicSection('TEST'); // Should also ask for a DB_MASTER connection. // This is asserted by the $lb mock. $manager->getReadConnection(); }
/** * Checks if a recent change entry already exists in the recentchanges table * * @since 0.4 * * @param RecentChange $change * * @throws MWException * @return bool */ public function changeExists(RecentChange $change) { $attribs = $change->getAttributes(); //XXX: need to check master? $db = $this->connectionManager->getReadConnection(); $res = $db->select('recentchanges', array('rc_id', 'rc_timestamp', 'rc_type', 'rc_params'), array('rc_namespace' => $attribs['rc_namespace'], 'rc_title' => $attribs['rc_title'], 'rc_timestamp' => $attribs['rc_timestamp'], 'rc_type' => RC_EXTERNAL), __METHOD__); if ($res->numRows() === 0) { return false; } $changeMetadata = $this->getMetadata($attribs['rc_params']); $changeRevId = $changeMetadata['rev_id']; $changeParentId = $changeMetadata['parent_id']; foreach ($res as $rc) { $metadata = $this->getMetadata($rc->rc_params); $parent_id = $metadata['parent_id']; $rev_id = $metadata['rev_id']; if ($rev_id === $changeRevId && $parent_id === $changeParentId) { return true; } } $this->connectionManager->releaseConnection($db); return false; }