+ $this->failures = 0;
+ $ex = new MWException( "Graceful storage failure" );
+ while (true) {
+ if ( $this->spawn ) {
+ if ($this->failures) {
+ // we don't know why it failed, could be the child process
+ // borked, could be db entry busted, could be db server out to lunch,
+ // so cover all bases
+ $this->closeSpawn();
+ $this->openSpawn();
+ }
+ $text = $this->getTextSpawned( $id );
+ } else {
+ $text = $this->getTextDbSafe( $id );
+ }
+ if ( $text === false ) {
+ $this->failures++;
+ if ( $this->failures > $this->maxFailures) {
+ $this->progress( "Failed to retrieve revision text for text id ".
+ "$id after $this->maxFailures tries, giving up" );
+ // were there so many bad retrievals in a row we want to bail?
+ // at some point we have to declare the dump irretrievably broken
+ $this->failedTextRetrievals++;
+ if ($this->failedTextRetrievals > $this->maxConsecutiveFailedTextRetrievals) {
+ throw $ex;
+ }
+ else {
+ // would be nice to return something better to the caller someday,
+ // log what we know about the failure and about the revision
+ return("");
+ }
+ } else {
+ $this->progress( "Error $this->failures " .
+ "of allowed $this->maxFailures retrieving revision text for text id $id! " .
+ "Pausing $this->failureTimeout seconds before retry..." );
+ sleep( $this->failureTimeout );
+ }
+ } else {
+ $this->failedTextRetrievals= 0;
+ return( $text );
+ }
+ }
+
+ }
+
+ /**
+ * Fetch a text revision from the database, retrying in case of failure.
+ * This may survive some transitory errors by reconnecting, but
+ * may not survive a long-term server outage.
+ */
+ private function getTextDbSafe( $id ) {
+ while ( true ) {
+ try {
+ $text = $this->getTextDb( $id );
+ } catch ( DBQueryError $ex ) {
+ $text = false;
+ }
+ return $text;
+ }
+ }
+
+ /**
+ * May throw a database error if, say, the server dies during query.
+ */
+ private function getTextDb( $id ) {
+ global $wgContLang;