/**
  * 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);
 }
コード例 #2
0
 /**
  * @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;
 }