/** * Commit the blob. * Does nothing if no text items have been added. * May skip the move if --copy-only is set. */ function commit() { $originalCount = count($this->texts); if (!$originalCount) { return; } /* Check to see if the target text_ids have been moved already. * * We originally read from the slave, so this can happen when a single * text_id is shared between multiple pages. It's rare, but possible * if a delete/move/undelete cycle splits up a null edit. * * We do a locking read to prevent closer-run race conditions. */ $dbw = wfGetDB(DB_MASTER); $dbw->begin(__METHOD__); $res = $dbw->select('blob_tracking', ['bt_text_id', 'bt_moved'], ['bt_text_id' => array_keys($this->referrers)], __METHOD__, ['FOR UPDATE']); $dirty = false; foreach ($res as $row) { if ($row->bt_moved) { # This row has already been moved, remove it $this->parent->debug("TRX: conflict detected in old_id={$row->bt_text_id}"); unset($this->texts[$row->bt_text_id]); $dirty = true; } } // Recompress the blob if necessary if ($dirty) { if (!count($this->texts)) { // All have been moved already if ($originalCount > 1) { // This is suspcious, make noise $this->parent->critical("Warning: concurrent operation detected, are there two conflicting " . "processes running, doing the same job?"); } return; } $this->recompress(); } // Insert the data into the destination cluster $targetCluster = $this->parent->getTargetCluster(); $store = $this->parent->store; $targetDB = $store->getMaster($targetCluster); $targetDB->clearFlag(DBO_TRX); // we manage the transactions $targetDB->begin(__METHOD__); $baseUrl = $this->parent->store->store($targetCluster, serialize($this->cgz)); // Write the new URLs to the blob_tracking table foreach ($this->referrers as $textId => $hash) { $url = $baseUrl . '/' . $hash; $dbw->update('blob_tracking', ['bt_new_url' => $url], ['bt_text_id' => $textId, 'bt_moved' => 0], __METHOD__); } $targetDB->commit(__METHOD__); // Critical section here: interruption at this point causes blob duplication // Reversing the order of the commits would cause data loss instead $dbw->commit(__METHOD__); // Write the new URLs to the text table and set the moved flag if (!$this->parent->copyOnly) { foreach ($this->referrers as $textId => $hash) { $url = $baseUrl . '/' . $hash; $this->parent->moveTextRow($textId, $url); } } }