wikimedia/mediawiki-extensions-CirrusSearch

View on GitHub
includes/Maintenance/Validators/SpecificAliasValidator.php

Summary

Maintainability
B
5 hrs
Test Coverage
<?php

namespace CirrusSearch\Maintenance\Validators;

use CirrusSearch\Maintenance\Printer;
use CirrusSearch\Maintenance\Reindexer;
use Elastica\Client;
use MediaWiki\Language\RawMessage;
use MediaWiki\Status\Status;

class SpecificAliasValidator extends IndexAliasValidator {
    /**
     * @var Reindexer
     */
    private $reindexer;

    /**
     * @var array
     */
    private $reindexParams;

    /**
     * @var Validator[]
     */
    private $reindexValidators;

    /**
     * @var bool
     */
    private $reindexAndRemoveOk;

    /**
     * @param Client $client
     * @param string $aliasName
     * @param string $specificIndexName
     * @param bool $startOver
     * @param Reindexer $reindexer
     * @param array $reindexParams
     * @param Validator[] $reindexValidators
     * @param bool $reindexAndRemoveOk
     * @param Printer|null $out
     */
    public function __construct(
        Client $client,
        $aliasName,
        $specificIndexName,
        $startOver,
        Reindexer $reindexer,
        array $reindexParams,
        array $reindexValidators,
        $reindexAndRemoveOk,
        Printer $out = null
    ) {
        // @todo: this constructor takes too many arguments - refactor!

        parent::__construct( $client, $aliasName, $specificIndexName, $startOver, $out );

        $this->reindexer = $reindexer;
        $this->reindexParams = $reindexParams;
        $this->reindexValidators = $reindexValidators;
        $this->reindexAndRemoveOk = $reindexAndRemoveOk;
    }

    /**
     * @param string[] $add
     * @param string[] $remove
     * @return Status
     */
    protected function updateIndices( array $add, array $remove ) {
        if ( !$remove ) {
            return $this->updateFreeIndices( $add );
        }

        if ( !$this->reindexAndRemoveOk ) {
            $this->output( "cannot correct!\n" );
            return Status::newFatal( new RawMessage(
                "The alias is held by another index which means it might be actively serving\n" .
                "queries.  You can solve this problem by running this program again with\n" .
                "--reindexAndRemoveOk.  Make sure you understand the consequences of either\n" .
                "choice." ) );
        }

        try {
            $this->output( "is taken...\n" );
            $this->outputIndented( "\tReindexing...\n" );
            $this->reindexer->reindex( ...$this->reindexParams );
            # Expecting this to usually be a noop, index should have been created with
            # appropriate settings and reindexer should restore all the settings it
            # changed, but it couldn't hurt to check again.
            foreach ( $this->reindexValidators as $validator ) {
                $status = $validator->validate();
                if ( !$status->isOK() ) {
                    return $status;
                }
            }
            // Validators might have changed replica counts, wait for everything to
            // get to green.
            $this->reindexer->waitForGreen();
        } catch ( \Exception $e ) {
            return Status::newFatal( new RawMessage( $e->getMessage() ) );
        }

        // now add alias & remove indices for real
        $status = Status::newGood();
        $status->merge( $this->swapAliases( $add ) );
        $toRemove = [];
        foreach ( $remove as $indexToRemove ) {
            $resp = $this->client->request( $indexToRemove . '/_alias', 'GET' );
            if ( !$resp->isOk() ) {
                return Status::newFatal( "Cannot fetch aliases of the old index $indexToRemove" );
            }
            if ( count( $resp->getData()[$indexToRemove]['aliases'] ?? [] ) > 0 ) {
                $this->outputIndented( "\t$indexToRemove still have active aliases, cannot remove.\n" );
            } else {
                $toRemove[] = $indexToRemove;
            }
        }
        $status->merge( parent::updateIndices( [], $toRemove ) );
        return $status;
    }

    /**
     * @param string[] $add
     * @return Status
     */
    public function updateFreeIndices( array $add ) {
        $this->output( "alias is free..." );

        foreach ( $add as $indexName ) {
            $index = $this->client->getIndex( $indexName );
            $index->addAlias( $this->aliasName, false );
        }

        $this->output( "corrected\n" );

        return Status::newGood();
    }

    /**
     * @param array $add
     * @return Status
     */
    public function swapAliases( array $add ) {
        $this->outputIndented( "\tSwapping alias..." );

        foreach ( $add as $indexName ) {
            $index = $this->client->getIndex( $indexName );
            $index->addAlias( $this->aliasName, true );
        }

        $this->output( "done\n" );

        return Status::newGood();
    }

    /**
     * @param string $name
     * @return bool
     */
    protected function shouldRemoveFromAlias( $name ) {
        return true;
    }
}