Skip to content

Commit de40907

Browse files
authored
fix(clients): safer replaceAllObjects + metis compliant (#3164)
1 parent 3f692ae commit de40907

File tree

14 files changed

+257
-110
lines changed

14 files changed

+257
-110
lines changed

clients/algoliasearch-client-csharp/algoliasearch/Utils/SearchClientExtensions.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ private static async Task<T> RetryUntil<T>(Func<Task<T>> func, Func<T, bool> val
410410
/// Push a new set of objects and remove all previous ones. Settings, synonyms and query rules are untouched.
411411
/// Replace all objects in an index without any downtime. Internally, this method copies the existing index settings, synonyms and query rules and indexes all passed objects.
412412
/// Finally, the temporary one replaces the existing index. (Synchronous version)
413+
/// See https://api-clients-automation.netlify.app/docs/contributing/add-new-api-client#5-helpers for implementation details.
413414
/// </summary>
414415
/// <param name="indexName">The index in which to perform the request.</param>
415416
/// <param name="objects">The list of `objects` to store in the given Algolia `indexName`.</param>
@@ -424,6 +425,7 @@ public ReplaceAllObjectsResponse ReplaceAllObjects<T>(string indexName, IEnumera
424425
/// Push a new set of objects and remove all previous ones. Settings, synonyms and query rules are untouched.
425426
/// Replace all objects in an index without any downtime. Internally, this method copies the existing index settings, synonyms and query rules and indexes all passed objects.
426427
/// Finally, the temporary one replaces the existing index.
428+
/// See https://api-clients-automation.netlify.app/docs/contributing/add-new-api-client#5-helpers for implementation details.
427429
/// </summary>
428430
/// <param name="indexName">The index in which to perform the request.</param>
429431
/// <param name="objects">The list of `objects` to store in the given Algolia `indexName`.</param>
@@ -441,20 +443,24 @@ public async Task<ReplaceAllObjectsResponse> ReplaceAllObjectsAsync<T>(string in
441443
var rnd = new Random();
442444
var tmpIndexName = $"{indexName}_tmp_{rnd.Next(100)}";
443445

444-
// Copy settings, synonyms and query rules into the temporary index
445446
var copyResponse = await OperationIndexAsync(indexName,
446447
new OperationIndexParams(OperationType.Copy, tmpIndexName)
447448
{ Scope = [ScopeType.Rules, ScopeType.Settings, ScopeType.Synonyms] }, options, cancellationToken)
448449
.ConfigureAwait(false);
449450

450-
await WaitForTaskAsync(indexName, copyResponse.TaskID, requestOptions: options, ct: cancellationToken)
451-
.ConfigureAwait(false);
452-
453-
// Add objects to the temporary index
454451
var batchResponse = await ChunkedBatchAsync(tmpIndexName, objects, Action.AddObject, batchSize,
455452
options, cancellationToken).ConfigureAwait(false);
456453

457-
// Move the temporary index to the main one
454+
await WaitForTaskAsync(tmpIndexName, copyResponse.TaskID, requestOptions: options, ct: cancellationToken)
455+
.ConfigureAwait(false);
456+
457+
copyResponse = await OperationIndexAsync(indexName,
458+
new OperationIndexParams(OperationType.Copy, tmpIndexName)
459+
{ Scope = [ScopeType.Rules, ScopeType.Settings, ScopeType.Synonyms] }, options, cancellationToken)
460+
.ConfigureAwait(false);
461+
await WaitForTaskAsync(tmpIndexName, copyResponse.TaskID, requestOptions: options, ct: cancellationToken)
462+
.ConfigureAwait(false);
463+
458464
var moveResponse = await OperationIndexAsync(tmpIndexName,
459465
new OperationIndexParams(OperationType.Move, indexName), options, cancellationToken)
460466
.ConfigureAwait(false);

clients/algoliasearch-client-go/.golangci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ linters:
2222
- wsl
2323
- varnamelen
2424
- nlreturn
25-
- goerr113
25+
- err113
2626
- gochecknoglobals
2727
- exhaustruct
2828
- exhaustive

clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/SearchClient.kt

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,8 @@ public suspend fun SearchClient.searchForFacets(
282282
* Internally, this method copies the existing index settings, synonyms and query rules and indexes all
283283
* passed objects. Finally, the temporary one replaces the existing index.
284284
*
285+
* See https://api-clients-automation.netlify.app/docs/contributing/add-new-api-client#5-helpers for implementation details.
286+
*
285287
* @param serializer [KSerializer] of type [T] for serialization.
286288
* @param records The list of records to replace.
287289
* @return intermediate operations (index name to task ID).
@@ -298,37 +300,44 @@ public suspend fun <T> SearchClient.replaceAllObjects(
298300
val body = options.json.encodeToJsonElement(serializer, record).jsonObject
299301
BatchRequest(action = Action.AddObject, body = body)
300302
}
301-
val destinationIndex = "${indexName}_tmp_${Random.nextInt(from = 0, until = 100)}"
303+
val tmpIndexName = "${indexName}_tmp_${Random.nextInt(from = 0, until = 100)}"
302304

303-
// 1. Copy index resources
304-
val copy = operationIndex(
305+
var copy = operationIndex(
305306
indexName = indexName,
306307
operationIndexParams = OperationIndexParams(
307308
operation = OperationType.Copy,
308-
destination = destinationIndex,
309+
destination = tmpIndexName,
309310
scope = listOf(ScopeType.Settings, ScopeType.Rules, ScopeType.Synonyms),
310311
),
311312
requestOptions = requestOptions,
312313
)
313-
waitTask(indexName = indexName, taskID = copy.taskID)
314314

315-
// 2. Save new objects
316315
val batch = batch(
317-
indexName = destinationIndex,
316+
indexName = tmpIndexName,
318317
batchWriteParams = BatchWriteParams(requests),
319318
requestOptions = requestOptions,
320319
)
321-
waitTask(indexName = destinationIndex, taskID = batch.taskID)
320+
waitTask(indexName = tmpIndexName, taskID = batch.taskID)
321+
waitTask(indexName = tmpIndexName, taskID = copy.taskID)
322+
323+
copy = operationIndex(
324+
indexName = indexName,
325+
operationIndexParams = OperationIndexParams(
326+
operation = OperationType.Copy,
327+
destination = tmpIndexName,
328+
scope = listOf(ScopeType.Settings, ScopeType.Rules, ScopeType.Synonyms),
329+
),
330+
requestOptions = requestOptions,
331+
)
332+
waitTask(indexName = tmpIndexName, taskID = copy.taskID)
322333

323-
// 3. Move temporary index to source index
324334
val move = operationIndex(
325-
indexName = destinationIndex,
335+
indexName = tmpIndexName,
326336
operationIndexParams = OperationIndexParams(operation = OperationType.Move, destination = indexName),
327337
requestOptions = requestOptions,
328338
)
329-
waitTask(indexName = destinationIndex, taskID = move.taskID)
339+
waitTask(indexName = tmpIndexName, taskID = move.taskID)
330340

331-
// 4. Return the list of operations
332341
return listOf(copy.taskID, batch.taskID, move.taskID)
333342
}
334343

clients/algoliasearch-client-scala/src/main/scala/algoliasearch/extension/package.scala

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,9 @@ package object extension {
198198
* settings, synonyms and query rules and indexes all passed objects. Finally, the temporary one replaces the
199199
* existing index.
200200
*
201+
* See https://api-clients-automation.netlify.app/docs/contributing/add-new-api-client#5-helpers for implementation
202+
* details.
203+
*
201204
* @param indexName
202205
* The index in which to perform the request.
203206
* @param records
@@ -215,31 +218,44 @@ package object extension {
215218
val requests = records.map { record =>
216219
BatchRequest(action = Action.AddObject, body = record)
217220
}
218-
val destinationIndex = s"${indexName}_tmp_${scala.util.Random.nextInt(100)}"
221+
val tmpIndexName = s"${indexName}_tmp_${scala.util.Random.nextInt(100)}"
219222

220223
for {
221224
copy <- client.operationIndex(
222225
indexName = indexName,
223226
operationIndexParams = OperationIndexParams(
224227
operation = OperationType.Copy,
225-
destination = destinationIndex,
228+
destination = tmpIndexName,
226229
scope = Some(Seq(ScopeType.Settings, ScopeType.Rules, ScopeType.Synonyms))
227230
),
228231
requestOptions = requestOptions
229232
)
230-
_ <- client.waitTask(indexName = destinationIndex, taskID = copy.taskID, requestOptions = requestOptions)
233+
231234
batch <- client.batch(
232-
indexName = destinationIndex,
235+
indexName = tmpIndexName,
233236
batchWriteParams = BatchWriteParams(requests),
234237
requestOptions = requestOptions
235-
) // 3. update the copy
236-
_ <- client.waitTask(indexName = destinationIndex, taskID = batch.taskID, requestOptions = requestOptions)
238+
)
239+
_ <- client.waitTask(indexName = tmpIndexName, taskID = batch.taskID, requestOptions = requestOptions)
240+
_ <- client.waitTask(indexName = tmpIndexName, taskID = copy.taskID, requestOptions = requestOptions)
241+
242+
copy <- client.operationIndex(
243+
indexName = indexName,
244+
operationIndexParams = OperationIndexParams(
245+
operation = OperationType.Copy,
246+
destination = tmpIndexName,
247+
scope = Some(Seq(ScopeType.Settings, ScopeType.Rules, ScopeType.Synonyms))
248+
),
249+
requestOptions = requestOptions
250+
)
251+
_ <- client.waitTask(indexName = tmpIndexName, taskID = copy.taskID, requestOptions = requestOptions)
252+
237253
replace <- client.operationIndex(
238-
indexName = destinationIndex,
254+
indexName = tmpIndexName,
239255
operationIndexParams = OperationIndexParams(operation = OperationType.Move, destination = indexName),
240256
requestOptions = requestOptions
241257
)
242-
_ <- client.waitTask(indexName = indexName, taskID = replace.taskID, requestOptions = requestOptions)
258+
_ <- client.waitTask(indexName = tmpIndexName, taskID = replace.taskID, requestOptions = requestOptions)
243259
} yield Seq(copy.taskID, batch.taskID, replace.taskID)
244260
}
245261
}

clients/algoliasearch-client-swift/Sources/Search/Extra/SearchClientExtension.swift

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,8 @@ public extension SearchClient {
457457
}
458458

459459
/// Replace all objects in an index
460+
///
461+
/// See https://api-clients-automation.netlify.app/docs/contributing/add-new-api-client#5-helpers for implementation details.
460462
/// - parameter objects: The new objects
461463
/// - parameter indexName: The name of the index where to replace the objects
462464
/// - parameter requestOptions: The request options
@@ -470,8 +472,7 @@ public extension SearchClient {
470472
) async throws -> ReplaceAllObjectsResponse {
471473
let tmpIndexName = try "\(indexName)_tmp_\(randomString())"
472474

473-
// Copy all index resources from production index
474-
let copyOperationResponse = try await operationIndex(
475+
var copyOperationResponse = try await operationIndex(
475476
indexName: indexName,
476477
operationIndexParams: OperationIndexParams(
477478
operation: .copy,
@@ -481,18 +482,26 @@ public extension SearchClient {
481482
requestOptions: requestOptions
482483
)
483484

484-
try await self.waitForTask(with: copyOperationResponse.taskID, in: indexName)
485-
486-
// Send records to the tmp index (batched)
487485
let batchResponses = try await self.chunkedBatch(
488486
indexName: tmpIndexName,
489487
objects: objects,
490488
waitForTasks: true,
491489
batchSize: batchSize,
492490
requestOptions: requestOptions
493491
)
492+
try await self.waitForTask(with: copyOperationResponse.taskID, in: tmpIndexName)
493+
494+
copyOperationResponse = try await operationIndex(
495+
indexName: indexName,
496+
operationIndexParams: OperationIndexParams(
497+
operation: .copy,
498+
destination: tmpIndexName,
499+
scope: [.rules, .settings, .synonyms]
500+
),
501+
requestOptions: requestOptions
502+
)
503+
try await self.waitForTask(with: copyOperationResponse.taskID, in: tmpIndexName)
494504

495-
// Move the temporary index to replace the main one
496505
let moveOperationResponse = try await self.operationIndex(
497506
indexName: tmpIndexName,
498507
operationIndexParams: OperationIndexParams(
@@ -501,7 +510,6 @@ public extension SearchClient {
501510
),
502511
requestOptions: requestOptions
503512
)
504-
505513
try await self.waitForTask(with: moveOperationResponse.taskID, in: tmpIndexName)
506514

507515
return ReplaceAllObjectsResponse(

playground/python/app/search.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ async def main():
1616

1717
try:
1818
resp = await client.replace_all_objects(
19-
index_name="test_replace_all_objects",
19+
index_name="newoneeverytime",
2020
objects=[{"name": f"John Doe{i}", "objectID": f"fff2bd4d-bb17-4e21-a0c4-0a8ea5e363f2{i}" } for i in range(33)],
2121
batch_size=10
2222
)

templates/go/search_helpers.mustache

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -516,32 +516,43 @@ func (c *APIClient) ChunkedBatch(indexName string, objects []map[string]any, act
516516
}
517517

518518
// ReplaceAllObjects replaces all objects (records) in the given `indexName` with the given `objects`. A temporary index is created during this process in order to backup your data.
519+
// See https://api-clients-automation.netlify.app/docs/contributing/add-new-api-client#5-helpers for implementation details.
519520
func (c *APIClient) ReplaceAllObjects(indexName string, objects []map[string]any, batchSize *int) (*ReplaceAllObjectsResponse, error) {
520-
tmpIndex := fmt.Sprintf("%s_tmp_%d", indexName, time.Now().UnixNano())
521+
tmpIndexName := fmt.Sprintf("%s_tmp_%d", indexName, time.Now().UnixNano())
521522
522-
copyResp, err := c.OperationIndex(c.NewApiOperationIndexRequest(indexName, NewOperationIndexParams(OPERATIONTYPE_COPY, tmpIndex, WithOperationIndexParamsScope([]ScopeType{SCOPETYPE_RULES, SCOPETYPE_SETTINGS, SCOPETYPE_SYNONYMS}))))
523+
copyResp, err := c.OperationIndex(c.NewApiOperationIndexRequest(indexName, NewOperationIndexParams(OPERATIONTYPE_COPY, tmpIndexName, WithOperationIndexParamsScope([]ScopeType{SCOPETYPE_RULES, SCOPETYPE_SETTINGS, SCOPETYPE_SYNONYMS}))))
523524
if err != nil {
524525
return nil, err
525526
}
526527

527-
_, err = c.WaitForTask(indexName, copyResp.TaskID, nil, nil, nil)
528+
waitForTask := true
529+
530+
batchResp, err := c.ChunkedBatch(tmpIndexName, objects, nil, &waitForTask, batchSize)
528531
if err != nil {
529532
return nil, err
530533
}
531534

532-
waitForTask := true
535+
_, err = c.WaitForTask(tmpIndexName, copyResp.TaskID, nil, nil, nil)
536+
if err != nil {
537+
return nil, err
538+
}
539+
540+
copyResp, err = c.OperationIndex(c.NewApiOperationIndexRequest(indexName, NewOperationIndexParams(OPERATIONTYPE_COPY, tmpIndexName, WithOperationIndexParamsScope([]ScopeType{SCOPETYPE_RULES, SCOPETYPE_SETTINGS, SCOPETYPE_SYNONYMS}))))
541+
if err != nil {
542+
return nil, err
543+
}
533544

534-
batchResp, err := c.ChunkedBatch(tmpIndex, objects, nil, &waitForTask, batchSize)
545+
_, err = c.WaitForTask(tmpIndexName, copyResp.TaskID, nil, nil, nil)
535546
if err != nil {
536547
return nil, err
537548
}
538549

539-
moveResp, err := c.OperationIndex(c.NewApiOperationIndexRequest(tmpIndex, NewOperationIndexParams(OPERATIONTYPE_MOVE, indexName)))
550+
moveResp, err := c.OperationIndex(c.NewApiOperationIndexRequest(tmpIndexName, NewOperationIndexParams(OPERATIONTYPE_MOVE, indexName)))
540551
if err != nil {
541552
return nil, err
542553
}
543554

544-
_, err = c.WaitForTask(indexName, moveResp.TaskID, nil, nil, nil)
555+
_, err = c.WaitForTask(tmpIndexName, moveResp.TaskID, nil, nil, nil)
545556
if err != nil {
546557
return nil, err
547558
}

templates/java/api_helpers.mustache

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,7 @@ return responses;
579579
/**
580580
* Push a new set of objects and remove all previous ones. Settings, synonyms and query rules are
581581
* untouched. Replace all records in an index without any downtime.
582+
* See https://api-clients-automation.netlify.app/docs/contributing/add-new-api-client#5-helpers for implementation details.
582583
*
583584
* @param indexName The `indexName` to replace `objects` in.
584585
* @param objects The array of `objects` to store in the given Algolia `indexName`.
@@ -608,18 +609,31 @@ UpdatedAtResponse copyOperationResponse = operationIndex(
608609
.addScope(ScopeType.SYNONYMS),
609610
requestOptions
610611
);
611-
waitForTask(indexName, copyOperationResponse.getTaskID(), requestOptions);
612612
613613
// Save new objects
614614
List<BatchResponse> batchResponses = chunkedBatch(tmpIndexName, objects, Action.ADD_OBJECT, true, batchSize, requestOptions);
615615
616+
waitForTask(tmpIndexName, copyOperationResponse.getTaskID(), requestOptions);
617+
618+
copyOperationResponse = operationIndex(
619+
indexName,
620+
new OperationIndexParams()
621+
.setOperation(OperationType.COPY)
622+
.setDestination(tmpIndexName)
623+
.addScope(ScopeType.SETTINGS)
624+
.addScope(ScopeType.RULES)
625+
.addScope(ScopeType.SYNONYMS),
626+
requestOptions
627+
);
628+
waitForTask(tmpIndexName, copyOperationResponse.getTaskID(), requestOptions);
629+
616630
// Move temporary index to source index
617631
UpdatedAtResponse moveOperationResponse = operationIndex(
618632
tmpIndexName,
619633
new OperationIndexParams().setOperation(OperationType.MOVE).setDestination(indexName),
620634
requestOptions
621635
);
622-
waitForTask(indexName, moveOperationResponse.getTaskID(), requestOptions);
636+
waitForTask(tmpIndexName, moveOperationResponse.getTaskID(), requestOptions);
623637
624638
return new ReplaceAllObjectsResponse()
625639
.setCopyOperationResponse(copyOperationResponse)

0 commit comments

Comments
 (0)