Integrations / Platforms / WordPress / Zero-Downtime Reindexing

Zero-Downtime Reindexing

Before going live, you want to refactor your reindexing strategy to ensure that your reindexing process creates no downtime.

To do so in your WordPress setup, you can use iterators. It lets us create a temporary index, index your data, then rename the temporary index to replace the production one. The renaming operation is atomic, making it transparent for your end users.

Reindexing records

To reindex atomically, you need to modify the reindex_posts command. You first want to extract the query loop to an iterator, then use the replaceAllObjects method instead of saveObjects.

First, you need to create a new Algolia_Post_Iterator class in your wp-cli.php file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
class Algolia_Post_Iterator implements Iterator {
    /**
     * @var array
     */
    private $queryArgs;

    private $key;

    private $paged;

    private $posts;
    private $type;

    public function __construct($type, array $queryArgs = []) {
        $this->type = $type;
        $this->queryArgs = ['post_type' => $type] + $queryArgs;
    }

    public function current() {
        return $this->serialize($this->posts[$this->key]);
    }

    public function next() {
        $this->key++;
    }

    public function key() {
        $this->key;
    }

    public function valid() {
        if (isset($this->posts[$this->key])) {
            return true;
        }

        $this->paged++;
        $query = new WP_Query(['paged' => $this->paged] + $this->queryArgs);

        if (!$query->have_posts()) {
            return false;
        }

        $this->posts = $query->posts;
        $this->key = 0;

        return true;
    }

    public function rewind() {
        $this->key = 0;
        $this->paged = 0;
        $this->posts = [];
    }

    private function serialize( WP_Post $post ) {
        $record = (array) apply_filters($this->type.'_to_record', $post);

        if (!isset($record['objectID'])) {
            $record['objectID'] = implode('#', [$post->post_type, $post->ID]);
        }

        return $record;
    }
}

Now you can simplify your indexing command. You can specify the query parameters to use, pass them to the iterator, then pass the iterator to replaceAllObjects.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public function reindex_post_atomic($args, $assoc_args) {
    global $algolia;

    $type = isset($assoc_args['type']) ? $assoc_args['type'] : 'post';

    $index = $algolia->initIndex(
        apply_filters('algolia_index_name', $type)
    );

    $queryArgs = [
        'posts_per_page' => 100,
        'post_status' => 'publish',
    ];

    $iterator = new Algolia_Post_Iterator($type, $queryArgs);

    $index->replaceAllObjects($iterator);

    WP_CLI::success("Reindexed $type posts in Algolia");
}

Reindexing records and configuration

If you want to send settings, synonyms, and Rules when reindexing, you can’t use replaceAllObjects directly, and need to perform each step explicitly.

Note that we’re using the same iterator as with the previous example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public function reindex_post_atomic_with_config($args, $assoc_args) {
    global $algolia;

    $type = isset($assoc_args['type']) ? $assoc_args['type'] : 'post';

    $temporaryName = sha1($type.time().mt_rand(0, 100));
    $finalIndexName = apply_filters('algolia_index_name', $type);
    $index = $algolia->initIndex($temporaryName);
    $settings = (array) apply_filters('get_'.$type.'_settings', []);

    unset($settings['replicas']);

    if ($settings) {
        $index->setSettings($settings);
    }

    $synonyms = (array) apply_filters('get_'.$type.'_synonyms', []);

    if ($synonyms) {
        $index->saveSynonyms($synonyms);
    }

    $rules = (array) apply_filters('get_'.$type.'$rules', []);

    if ($rules) {
        $index->saveRules($rules);
    }

    $queryArgs = [
        'posts_per_page' => 100,
        'post_status' => 'publish',
    ];

    $iterator = new Algolia_Post_Iterator($type, $queryArgs);
    $index->saveObjects($iterator);

    $algolia->moveIndex($temporaryName, $finalIndexName);

    WP_CLI::success("Reindexed $type posts in Algolia");
}

Did you find this page helpful?