Skip to content
This repository was archived by the owner on Nov 4, 2021. It is now read-only.

Commit 7fae8c0

Browse files
committed
Merge branch 'zero_downtime' into dev
# Conflicts: # src/Console/Features/requiresModelArgument.php # src/ElasticEngine.php
2 parents f29e531 + 0de0b4e commit 7fae8c0

18 files changed

+795
-59
lines changed

README.md

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@
33
[![Packagist](https://img.shields.io/packagist/v/babenkoivan/scout-elasticsearch-driver.svg)](https://packagist.org/packages/babenkoivan/scout-elasticsearch-driver)
44
[![Packagist](https://img.shields.io/packagist/dt/babenkoivan/scout-elasticsearch-driver.svg)](https://packagist.org/packages/babenkoivan/scout-elasticsearch-driver)
55
[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/scout-elasticsearch-driver/Lobby)
6+
[![Donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://www.paypal.me/ivanbabenko)
67

7-
:exclamation: The project has a [chat room](https://gitter.im/scout-elasticsearch-driver/Lobby) on Gitter!
8+
:coffee: If you like my package, it'd be nice of you [to buy me a cup of coffee](https://www.paypal.me/ivanbabenko).
9+
10+
:octocat: The project has a [chat room on Gitter](https://gitter.im/scout-elasticsearch-driver/Lobby)!
11+
12+
---
813

914
This package offers advanced functionality for searching and filtering data in Elasticsearch.
1015
Check out its [features](#features)!
@@ -22,6 +27,7 @@ Check out its [features](#features)!
2227
* [Console commands](#console-commands)
2328
* [Search rules](#search-rules)
2429
* [Available filters](#available-filters)
30+
* [Zero downtime migration](#zero-downtime-migration)
2531
* [Debug](#debug)
2632

2733
## Tutorial
@@ -36,6 +42,7 @@ There are information about Elasticsearch installation and the package usage exa
3642
* A possibility to add a new field to an existing mapping [automatically](#configuration) or using [the artisan command](#console-commands).
3743
* Lots of different ways to implement your search algorithm: using [search rules](#search-rules) or a [raw search](#usage).
3844
* [Various filter types](#available-filters) to make a search query more specific.
45+
* [Zero downtime migration](#zero-downtime-migration) from an old index to a new index.
3946

4047
## Requirements
4148

@@ -257,6 +264,7 @@ elastic:create-index | `index-configurator` - The index configurator class | Cre
257264
elastic:update-index | `index-configurator` - The index configurator class | Updates settings and mappings of an Elasticsearch index.
258265
elastic:drop-index | `index-configurator` - The index configurator class | Drops an Elasticsearch index.
259266
elastic:update-mapping | `model` - The model class | Updates a model mapping.
267+
elastic:migrate | `model` - The model class, `target-index` - The index name to migrate | Migrates model to another index.
260268

261269
For detailed description and all available options run `php artisan help [command]` in the command line.
262270

@@ -371,6 +379,29 @@ whereRegexp($field, $value, $flags = 'ALL') | whereRegexp('name.raw', 'A.+') | F
371379

372380
In most cases it's better to use raw fields to filter records, i.e. not analyzed fields.
373381

382+
## Zero downtime migration
383+
384+
As you might know, you can't change the type of already created field in Elasticsearch.
385+
The only choice in such case is to create a new index with necessary mapping and import your models into the new index.
386+
A migration can take quite a long time, so to avoid downtime during the process the driver reads from the old index and writes to the new one.
387+
As soon as migration is over it starts reading from the new index and removes the old index.
388+
This is how the artisan `elastic:migrate` command works.
389+
390+
Before you run the command, make sure that your index configurator uses the `ScoutElastic\Migratable` trait.
391+
If it's not, add the trait and run the artisan `elastic:update-index` command using your index configurator class name as an argument:
392+
393+
```php
394+
php artisan elastic:update-index App\\MyIndexConfigurator
395+
```
396+
397+
When you are ready, make changes in the model mapping and run the `elastic:migrate` command using the model class as the first argument and desired index name as the second argument:
398+
399+
```php
400+
php artisan elastic:migrate App\\MyModel my_index_v2
401+
```
402+
403+
Note, that if you need just to add new fields in your mapping, use the `elastic:update-mapping` command.
404+
374405
## Debug
375406

376407
There are two methods that can help you to analyze results of a search query:

src/Console/ElasticIndexCreateCommand.php

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Illuminate\Console\Command;
66
use ScoutElastic\Console\Features\requiresIndexConfiguratorArgument;
77
use ScoutElastic\Facades\ElasticClient;
8+
use ScoutElastic\Migratable;
89
use ScoutElastic\Payloads\IndexPayload;
910

1011
class ElasticIndexCreateCommand extends Command
@@ -15,11 +16,9 @@ class ElasticIndexCreateCommand extends Command
1516

1617
protected $description = 'Create an Elasticsearch index';
1718

18-
public function handle()
19+
protected function createIndex()
1920
{
20-
if (!$configurator = $this->getIndexConfigurator()) {
21-
return;
22-
}
21+
$configurator = $this->getIndexConfigurator();
2322

2423
$payload = (new IndexPayload($configurator))
2524
->setIfNotEmpty('body.settings', $configurator->getSettings())
@@ -30,8 +29,37 @@ public function handle()
3029
->create($payload);
3130

3231
$this->info(sprintf(
33-
'The index %s was created!',
32+
'The %s index was created!',
3433
$configurator->getName()
3534
));
3635
}
36+
37+
protected function createWriteAlias()
38+
{
39+
$configurator = $this->getIndexConfigurator();
40+
41+
if (!in_array(Migratable::class, class_uses_recursive($configurator))) {
42+
return;
43+
}
44+
45+
$payload = (new IndexPayload($configurator))
46+
->set('name', $configurator->getWriteAlias())
47+
->get();
48+
49+
ElasticClient::indices()
50+
->putAlias($payload);
51+
52+
$this->info(sprintf(
53+
'The %s alias for the %s index was created!',
54+
$configurator->getWriteAlias(),
55+
$configurator->getName()
56+
));
57+
}
58+
59+
public function handle()
60+
{
61+
$this->createIndex();
62+
63+
$this->createWriteAlias();
64+
}
3765
}

src/Console/ElasticIndexDropCommand.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ class ElasticIndexDropCommand extends Command
1717

1818
public function handle()
1919
{
20-
if (!$configurator = $this->getIndexConfigurator()) {
21-
return;
22-
}
20+
$configurator = $this->getIndexConfigurator();
2321

2422
$payload = (new IndexPayload($configurator))
2523
->get();

src/Console/ElasticIndexUpdateCommand.php

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
namespace ScoutElastic\Console;
44

5+
use Exception;
6+
use LogicException;
57
use Illuminate\Console\Command;
68
use ScoutElastic\Console\Features\requiresIndexConfiguratorArgument;
79
use ScoutElastic\Facades\ElasticClient;
8-
use Exception;
10+
use ScoutElastic\Migratable;
911
use ScoutElastic\Payloads\IndexPayload;
12+
use ScoutElastic\Payloads\RawPayload;
1013

1114
class ElasticIndexUpdateCommand extends Command
1215
{
@@ -16,23 +19,19 @@ class ElasticIndexUpdateCommand extends Command
1619

1720
protected $description = 'Update settings and mappings of an Elasticsearch index';
1821

19-
public function handle()
22+
protected function updateIndex()
2023
{
21-
if (!$configurator = $this->getIndexConfigurator()) {
22-
return;
23-
}
24+
$configurator = $this->getIndexConfigurator();
2425

2526
$indexPayload = (new IndexPayload($configurator))->get();
2627

2728
$indices = ElasticClient::indices();
2829

2930
if (!$indices->exists($indexPayload)) {
30-
$this->error(sprintf(
31+
throw new LogicException(sprintf(
3132
'Index %s doesn\'t exist',
3233
$configurator->getName()
3334
));
34-
35-
return;
3635
}
3736

3837
try {
@@ -67,4 +66,42 @@ public function handle()
6766
$configurator->getName()
6867
));
6968
}
69+
70+
protected function createWriteAlias()
71+
{
72+
$configurator = $this->getIndexConfigurator();
73+
74+
if (!in_array(Migratable::class, class_uses_recursive($configurator))) {
75+
return;
76+
}
77+
78+
$indices = ElasticClient::indices();
79+
80+
$existsPayload = (new RawPayload())
81+
->set('name', $configurator->getWriteAlias())
82+
->get();
83+
84+
if ($indices->existsAlias($existsPayload)) {
85+
return;
86+
}
87+
88+
$putPayload = (new IndexPayload($configurator))
89+
->set('name', $configurator->getWriteAlias())
90+
->get();
91+
92+
$indices->putAlias($putPayload);
93+
94+
$this->info(sprintf(
95+
'The %s alias for the %s index was created!',
96+
$configurator->getWriteAlias(),
97+
$configurator->getName()
98+
));
99+
}
100+
101+
public function handle()
102+
{
103+
$this->updateIndex();
104+
105+
$this->createWriteAlias();
106+
}
70107
}

0 commit comments

Comments
 (0)