Skip to content

Commit 722fe50

Browse files
committed
feat: add useRenameStrategyToRecreate option to reduce downtime
1 parent 457056b commit 722fe50

File tree

2 files changed

+31
-30
lines changed

2 files changed

+31
-30
lines changed

src/database/arangodb/schema-migration/arango-search-helpers.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ export interface ArangoSearchConfiguration {
6666
* and existing views will not be changed.
6767
*/
6868
readonly consolidationPolicy?: TierConsolidationPolicy;
69+
70+
/**
71+
* In some cases, arangosearch cannot be updated but need to be recreated. By default, the view
72+
* is deleted first, then recreated. If this is true, the view is first created using a
73+
* temporary name, then the actual one is deleted, and the temporary is renamed to the actual
74+
* one. This reduces the downtime during the migration.
75+
*
76+
* Cannot be used if ArangoDB is running as a cluster (renaming views is not supported in
77+
* ArangoDB cluster deployments, see
78+
* https://docs.arangodb.com/3.11/develop/http-api/views/arangosearch-views/#rename-a-view)
79+
*/
80+
readonly useRenameStrategyToRecreate?: boolean;
6981
}
7082

7183
export function getRequiredViewsFromModel(model: Model): ReadonlyArray<ArangoSearchDefinition> {

src/database/arangodb/schema-migration/performer.ts

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export class MigrationPerformer {
4343
case 'updateArangoSearchView':
4444
return this.updateArangoSearchView(migration);
4545
case 'dropArangoSearchView':
46-
return this.dropArangoSearchView(migration);
46+
return this.dropArangoSearchView(migration.viewName);
4747
case 'recreateArangoSearchView':
4848
return this.recreateArangoSearchView(migration);
4949
case 'createArangoSearchAnalyzer':
@@ -113,16 +113,21 @@ export class MigrationPerformer {
113113
}
114114
}
115115

116-
private async createArangoSearchView(migration: CreateArangoSearchViewMigration) {
116+
private async createArangoSearchView(
117+
migration: CreateArangoSearchViewMigration | RecreateArangoSearchViewMigration,
118+
{ viewNameOverride }: { viewNameOverride?: string } = {},
119+
) {
117120
try {
118121
await this.db.createView(
119-
migration.viewName,
122+
viewNameOverride ?? migration.viewName,
120123
configureForBackgroundCreation(migration.properties),
121124
);
122125
} catch (e: any) {
123126
// maybe the collection has been created in the meantime
124127
if (e.errorNum === ERROR_ARANGO_DUPLICATE_NAME) {
125-
const existingProperties = await this.db.view(migration.viewName).properties();
128+
const existingProperties = await this.db
129+
.view(viewNameOverride ?? migration.viewName)
130+
.properties();
126131
// if the properties do not equal, we might need to recreate; rather fail and let someone retry
127132
if (isEqualProperties(migration.properties, existingProperties)) {
128133
return;
@@ -138,9 +143,9 @@ export class MigrationPerformer {
138143
.replaceProperties(configureForBackgroundCreation(migration.properties));
139144
}
140145

141-
private async dropArangoSearchView(migration: DropArangoSearchViewMigration) {
146+
private async dropArangoSearchView(viewName: string) {
142147
try {
143-
await this.db.view(migration.config.viewName).drop();
148+
await this.db.view(viewName).drop();
144149
} catch (e: any) {
145150
// maybe the view has been dropped in the meantime
146151
if (e.errorNum === ERROR_ARANGO_DATA_SOURCE_NOT_FOUND) {
@@ -151,30 +156,14 @@ export class MigrationPerformer {
151156
}
152157

153158
private async recreateArangoSearchView(migration: RecreateArangoSearchViewMigration) {
154-
try {
155-
await this.db.view(migration.viewName).drop();
156-
} catch (e: any) {
157-
// maybe the view has been dropped in the meantime
158-
if (e.errorNum !== ERROR_ARANGO_DATA_SOURCE_NOT_FOUND) {
159-
throw e;
160-
}
161-
}
162-
163-
try {
164-
await this.db.createView(
165-
migration.viewName,
166-
configureForBackgroundCreation(migration.properties),
167-
);
168-
} catch (e: any) {
169-
// maybe the collection has been created in the meantime
170-
if (e.errorNum === ERROR_ARANGO_DUPLICATE_NAME) {
171-
const existingProperties = await this.db.view(migration.viewName).properties();
172-
// if the properties do not equal, we might need to recreate; rather fail and let someone retry
173-
if (isEqualProperties(migration.properties, existingProperties)) {
174-
return;
175-
}
176-
}
177-
throw e;
159+
if (this.config.arangoSearchConfiguration?.useRenameStrategyToRecreate) {
160+
const tempName = 'temp_' + migration.viewName;
161+
await this.createArangoSearchView(migration, { viewNameOverride: tempName });
162+
await this.dropArangoSearchView(migration.viewName);
163+
await this.db.view(tempName).rename(migration.viewName);
164+
} else {
165+
await this.dropArangoSearchView(migration.viewName);
166+
await this.createArangoSearchView(migration);
178167
}
179168
}
180169

0 commit comments

Comments
 (0)