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 you 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 change 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 the following example uses the same iterator as the earlier 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?