Skip to content

Commit 8d81d07

Browse files
committed
Add zero-downtime index deployments
1 parent f6afad2 commit 8d81d07

19 files changed

Lines changed: 1251 additions & 6 deletions

README.md

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ Publish the OpenSearch client configuration:
2525
php artisan vendor:publish --provider="DirectoryTree\OpenSearchClient\OpenSearchClientServiceProvider"
2626
```
2727

28-
Publish the migration configuration:
28+
Publish the migration and deployment configuration:
2929

3030
```bash
3131
php artisan vendor:publish --provider="DirectoryTree\OpenSearchMigrations\OpenSearchMigrationsServiceProvider"
3232
```
3333

3434
## Configuration
3535

36-
The migration configuration is published to `config/opensearch-migrations.php`:
36+
Migration history and index naming are configured in `config/opensearch-migrations.php`:
3737

3838
```php
3939
'table' => env('OPENSEARCH_MIGRATIONS_TABLE', 'opensearch_migrations'),
@@ -47,6 +47,17 @@ The migration configuration is published to `config/opensearch-migrations.php`:
4747
'alias_name_prefix' => env('OPENSEARCH_MIGRATIONS_ALIAS_NAME_PREFIX', env('SCOUT_PREFIX', '')),
4848
```
4949

50+
Deployment storage is configured separately in `config/opensearch-deployments.php`:
51+
52+
```php
53+
'table' => env('OPENSEARCH_DEPLOYMENTS_TABLE', 'opensearch_deployments'),
54+
55+
'connection' => env(
56+
'OPENSEARCH_DEPLOYMENTS_CONNECTION',
57+
env('OPENSEARCH_MIGRATIONS_CONNECTION')
58+
),
59+
```
60+
5061
## Creating Migrations
5162

5263
Create a migration:
@@ -124,6 +135,74 @@ Show migration status:
124135
php artisan opensearch:migrate:status
125136
```
126137

138+
## Zero-Downtime Index Deployments
139+
140+
The deployer creates versioned physical indexes and tracks their lifecycle in the `opensearch_deployments` table. Applications remain responsible for backfilling and validating candidate documents.
141+
142+
First, create a stable alias for the index in a migration:
143+
144+
```php
145+
Index::putAlias('posts', 'posts_search');
146+
```
147+
148+
Start a deployment with the latest mapping and settings:
149+
150+
```php
151+
use DirectoryTree\OpenSearchAdapter\Indices\Mapping;
152+
use DirectoryTree\OpenSearchAdapter\Indices\Settings;
153+
use DirectoryTree\OpenSearchMigrations\Deployer;
154+
155+
$deployer = app(Deployer::class);
156+
157+
$deployment = $deployer->start(
158+
name: 'posts',
159+
alias: 'posts_search',
160+
configure: function (Mapping $mapping, Settings $settings) {
161+
$mapping->text('title');
162+
$mapping->text('body');
163+
},
164+
);
165+
```
166+
167+
The returned deployment exposes the physical candidate index for backfilling:
168+
169+
```php
170+
$deployment->candidateIndex;
171+
```
172+
173+
While backfilling, send live writes and deletions to every deployment write index:
174+
175+
```php
176+
$deployment->writeIndexes();
177+
```
178+
179+
After application-specific validation succeeds, mark the candidate ready and atomically move the alias:
180+
181+
```php
182+
$deployer->markReady('posts');
183+
$deployer->cutover('posts');
184+
```
185+
186+
Cancel and delete a candidate that should not be promoted:
187+
188+
```php
189+
$deployer->cancel('posts');
190+
```
191+
192+
The previous index remains in the deployment's write indexes during the rollback window:
193+
194+
```php
195+
$deployer->rollback('posts');
196+
```
197+
198+
Once the new index is verified in production, delete the previous physical index and complete the deployment:
199+
200+
```php
201+
$deployer->retire('posts');
202+
```
203+
204+
`opensearch:migrate:fresh` deletes all deployment records because it drops all physical indexes. Migration reset and refresh commands refuse to run while managed deployments exist.
205+
127206
## Credits
128207

129208
This package builds on a lot of the foundation and prior work from [Ivan Babenko](https://github.com/babenkoivan) and his Elasticsearch Laravel ecosystem packages.

config/opensearch-deployments.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
return [
4+
/*
5+
|--------------------------------------------------------------------------
6+
| Deployment Table
7+
|--------------------------------------------------------------------------
8+
|
9+
| This table stores the state of versioned OpenSearch index deployments.
10+
|
11+
*/
12+
13+
'table' => env('OPENSEARCH_DEPLOYMENTS_TABLE', 'opensearch_deployments'),
14+
15+
/*
16+
|--------------------------------------------------------------------------
17+
| Database Connection
18+
|--------------------------------------------------------------------------
19+
|
20+
| This connection stores OpenSearch deployment state. When null, the
21+
| application's default database connection will be used.
22+
|
23+
*/
24+
25+
'connection' => env(
26+
'OPENSEARCH_DEPLOYMENTS_CONNECTION',
27+
env('OPENSEARCH_MIGRATIONS_CONNECTION')
28+
),
29+
];

src/Console/FreshCommand.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use DirectoryTree\OpenSearchMigrations\IndexManagerInterface;
66
use DirectoryTree\OpenSearchMigrations\Migrator;
7+
use DirectoryTree\OpenSearchMigrations\Repositories\DeploymentRepository;
78
use DirectoryTree\OpenSearchMigrations\Repositories\MigrationRepository;
89
use Illuminate\Console\Command;
910
use Illuminate\Console\ConfirmableTrait;
@@ -33,6 +34,7 @@ public function handle(
3334
Migrator $migrator,
3435
IndexManagerInterface $index,
3536
MigrationRepository $migrations,
37+
DeploymentRepository $deployments,
3638
): int {
3739
$migrator->setOutput($this->output);
3840

@@ -41,10 +43,12 @@ public function handle(
4143
}
4244

4345
$migrator->prepare();
46+
$deployments->prepare();
4447

4548
$index->drop('*');
4649

4750
$migrations->deleteAll();
51+
$deployments->deleteAll();
4852

4953
$migrator->migrateAll();
5054

src/Console/RefreshCommand.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace DirectoryTree\OpenSearchMigrations\Console;
44

55
use DirectoryTree\OpenSearchMigrations\Migrator;
6+
use DirectoryTree\OpenSearchMigrations\Repositories\DeploymentRepository;
67
use Illuminate\Console\Command;
78
use Illuminate\Console\ConfirmableTrait;
89

@@ -31,7 +32,7 @@ class RefreshCommand extends Command
3132
/**
3233
* Execute the console command.
3334
*/
34-
public function handle(Migrator $migrator): int
35+
public function handle(Migrator $migrator, DeploymentRepository $deployments): int
3536
{
3637
$migrator->setOutput($this->output);
3738

@@ -40,6 +41,13 @@ public function handle(Migrator $migrator): int
4041
}
4142

4243
$migrator->prepare();
44+
$deployments->prepare();
45+
46+
if ($deployments->exists()) {
47+
$this->components->error('Managed indexes cannot be refreshed. Use opensearch:migrate:fresh to discard them.');
48+
49+
return static::FAILURE;
50+
}
4351

4452
$migrator->rollbackAll();
4553
$migrator->migrateAll();

src/Console/ResetCommand.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace DirectoryTree\OpenSearchMigrations\Console;
44

55
use DirectoryTree\OpenSearchMigrations\Migrator;
6+
use DirectoryTree\OpenSearchMigrations\Repositories\DeploymentRepository;
67
use Illuminate\Console\Command;
78
use Illuminate\Console\ConfirmableTrait;
89

@@ -31,7 +32,7 @@ class ResetCommand extends Command
3132
/**
3233
* Execute the console command.
3334
*/
34-
public function handle(Migrator $migrator): int
35+
public function handle(Migrator $migrator, DeploymentRepository $deployments): int
3536
{
3637
$migrator->setOutput($this->output);
3738

@@ -40,6 +41,13 @@ public function handle(Migrator $migrator): int
4041
}
4142

4243
$migrator->prepare();
44+
$deployments->prepare();
45+
46+
if ($deployments->exists()) {
47+
$this->components->error('Managed indexes cannot be reset. Use opensearch:migrate:fresh to discard them.');
48+
49+
return static::FAILURE;
50+
}
4351

4452
$migrator->rollbackAll();
4553

0 commit comments

Comments
 (0)