Skip to content

Commit ede5ee0

Browse files
authored
fix(clients): WithTransformation methods retry logic (#5183)
1 parent 9ee13db commit ede5ee0

File tree

8 files changed

+102
-12
lines changed

8 files changed

+102
-12
lines changed

clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/utils/TaskUtils.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ private TaskUtils() {
1616

1717
public static <T> T retryUntil(Supplier<T> func, Predicate<T> validate, int maxRetries, IntUnaryOperator timeout)
1818
throws AlgoliaRuntimeException {
19+
if (timeout == null) {
20+
timeout = DEFAULT_TIMEOUT;
21+
}
22+
if (maxRetries == 0) {
23+
maxRetries = DEFAULT_MAX_RETRIES;
24+
}
1925
int retryCount = 0;
2026
while (retryCount < maxRetries) {
2127
T resp = func.get();

clients/algoliasearch-client-php/lib/Support/Helpers.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ public static function retryForApiKeyUntil(
193193
*
194194
* @return float|int
195195
*/
196-
private static function linearTimeout($defaultTimeout, $retries)
196+
public static function linearTimeout($defaultTimeout, $retries)
197197
{
198198
// minimum between timeout and 200 milliseconds * number of retries
199199
// Convert into microseconds for usleep (* 1000)

scripts/cts/testServer/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { benchmarkServer } from './benchmark.ts';
1414
import { chunkWrapperServer } from './chunkWrapper.ts';
1515
import { errorServer, errorServerRetriedOnce, errorServerRetriedTwice } from './error.ts';
1616
import { gzipServer } from './gzip.ts';
17-
import { pushMockServer } from './pushMock.ts';
17+
import { pushMockServer, pushMockServerRetriedOnce } from './pushMock.ts';
1818
import { replaceAllObjectsServer } from './replaceAllObjects.ts';
1919
import { replaceAllObjectsServerFailed } from './replaceAllObjectsFailed.ts';
2020
import { replaceAllObjectsScopesServer } from './replaceAllObjectsScopes.ts';
@@ -42,6 +42,7 @@ export async function startTestServer(suites: Record<CTSType, boolean>): Promise
4242
apiKeyServer(),
4343
algoliaMockServer(),
4444
pushMockServer(),
45+
pushMockServerRetriedOnce(),
4546
replaceAllObjectsWithTransformationServer(),
4647
);
4748
}

scripts/cts/testServer/pushMock.ts

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,15 @@ import express from 'express';
66

77
import { setupServer } from './index.ts';
88

9-
const pushMockState: Record<string, any> = {};
9+
const pushMockState: Record<
10+
string,
11+
{
12+
retriedEvents: boolean;
13+
retriedHosts: boolean;
14+
saveObjectsWithTransformation: number;
15+
partialUpdateObjectsWithTransformation: number;
16+
}
17+
> = {};
1018

1119
export function assertPushMockValid(expectedCount: number): void {
1220
if (Object.values(pushMockState).length !== expectedCount) {
@@ -20,6 +28,8 @@ export function assertPushMockValid(expectedCount: number): void {
2028
}
2129

2230
expect(state).to.deep.equal({
31+
retriedHosts: true,
32+
retriedEvents: true,
2333
saveObjectsWithTransformation: Number(numberOfTestSuites),
2434
partialUpdateObjectsWithTransformation: Number(numberOfTestSuites),
2535
});
@@ -40,7 +50,16 @@ function addRoutes(app: Express): void {
4050
const lang = match?.[2] as string;
4151

4252
if (!pushMockState[lang]) {
43-
pushMockState[lang] = {};
53+
// simulate a retry at the hosts level
54+
pushMockState[lang] = {
55+
retriedHosts: true,
56+
retriedEvents: false,
57+
saveObjectsWithTransformation: 0,
58+
partialUpdateObjectsWithTransformation: 0,
59+
};
60+
61+
res.status(500).json({ message: 'error test server response' });
62+
return;
4463
}
4564

4665
pushMockState[lang][helper] = (pushMockState[lang][helper] ?? 0) + 1;
@@ -55,7 +74,7 @@ function addRoutes(app: Express): void {
5574
});
5675

5776
res.json({
58-
runID: 'b1b7a982-524c-40d2-bb7f-48aab075abda',
77+
runID: `b1b7a982-524c-40d2-bb7f-48aab075abda_${lang}`,
5978
eventID: '113b2068-6337-4c85-b5c2-e7b213d82925',
6079
message: 'OK',
6180
createdAt: '2022-05-12T06:24:30.049Z',
@@ -72,7 +91,7 @@ function addRoutes(app: Express): void {
7291
});
7392

7493
res.json({
75-
runID: 'b1b7a982-524c-40d2-bb7f-48aab075abda',
94+
runID: `b1b7a982-524c-40d2-bb7f-48aab075abda_${lang}`,
7695
eventID: '113b2068-6337-4c85-b5c2-e7b213d82925',
7796
message: 'OK',
7897
createdAt: '2022-05-12T06:24:30.049Z',
@@ -82,8 +101,32 @@ function addRoutes(app: Express): void {
82101
throw new Error('unknown helper');
83102
}
84103
});
104+
105+
app.get('/1/runs/:runID/events/:eventID', (req, res) => {
106+
const lang = req.params.runID.match(/^b1b7a982-524c-40d2-bb7f-48aab075abda_(.*)$/)?.[1] as string;
107+
108+
if (pushMockState[lang] && !pushMockState[lang]?.retriedEvents) {
109+
pushMockState[lang].retriedEvents = true;
110+
res.status(404).json({ message: 'error test server response' });
111+
112+
return;
113+
}
114+
115+
res.json({
116+
status: 'succeeded',
117+
eventID: req.params.eventID,
118+
runID: req.params.runID,
119+
type: 'fetch',
120+
batchSize: 1,
121+
publishedAt: '2022-05-12T06:24:30.049Z',
122+
});
123+
});
124+
}
125+
126+
export function pushMockServerRetriedOnce(): Promise<Server> {
127+
return setupServer('pushMockRetriedOnce', 6689, addRoutes);
85128
}
86129

87130
export function pushMockServer(): Promise<Server> {
88-
return setupServer('pushMock', 6689, addRoutes);
131+
return setupServer('pushMock', 6688, addRoutes);
89132
}

templates/java/api_helpers.mustache

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,25 @@ public <T> List<WatchResponse> saveObjectsWithTransformation(String indexName, I
776776
return saveObjectsWithTransformation(indexName, objects, null);
777777
}
778778
779+
/**
780+
* Helper: Similar to the `saveObjects` method but requires a Push connector
781+
* (https://www.algolia.com/doc/guides/sending-and-managing-data/send-and-update-your-data/connectors/push/)
782+
* to be created first, in order to transform records before indexing them to Algolia. The
783+
* `region` must have been passed to the client instantiation method.
784+
*
785+
* @param indexName The `indexName` to replace `objects` in.
786+
* @param objects The array of `objects` to store in the given Algolia `indexName`.
787+
* @param waitForTasks - Whether or not we should wait until every `batch` tasks has been
788+
* processed, this operation may slow the total execution time of this method but is more
789+
* reliable.
790+
* @throws AlgoliaRetryException When the retry has failed on all hosts
791+
* @throws AlgoliaApiException When the API sends an http error code
792+
* @throws AlgoliaRuntimeException When an error occurred during the serialization
793+
*/
794+
public <T> List<WatchResponse> saveObjectsWithTransformation(String indexName, Iterable<T> objects, boolean waitForTasks) {
795+
return saveObjectsWithTransformation(indexName, objects, waitForTasks, null);
796+
}
797+
779798
/**
780799
* Helper: Similar to the `saveObjects` method but requires a Push connector
781800
* (https://www.algolia.com/doc/guides/sending-and-managing-data/send-and-update-your-data/connectors/push/)

templates/php/client_config.mustache

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,17 @@ class {{configClassname}} extends {{#hasRegionalHost}}ConfigWithRegion{{/hasRegi
3030
}
3131
{{/hasRegionalHost}}
3232

33-
{{#isSearchClient}}private $defaultWaitTaskTimeBeforeRetry = 5000; // 5 sec in milliseconds
33+
{{#isIngestionClient}}
34+
private $defaultWaitTaskTimeBeforeRetry = 5000; // 5 sec in milliseconds
35+
36+
public function getWaitTaskTimeBeforeRetry()
37+
{
38+
return $this->config['waitTaskTimeBeforeRetry'];
39+
}
40+
41+
{{/isIngestionClient}}
42+
{{#isSearchClient}}
43+
private $defaultWaitTaskTimeBeforeRetry = 5000; // 5 sec in milliseconds
3444
private $defaultMaxRetries = 50;
3545

3646
public static function create($appId, $apiKey)
@@ -98,6 +108,9 @@ class {{configClassname}} extends {{#hasRegionalHost}}ConfigWithRegion{{/hasRegi
98108
'writeTimeout' => {{#lambda.toSeconds}}{{x-timeouts.server.write}}{{/lambda.toSeconds}},
99109
'connectTimeout' => {{#lambda.toSeconds}}{{x-timeouts.server.connect}}{{/lambda.toSeconds}},
100110
'defaultHeaders' => [],
111+
{{#isIngestionClient}}
112+
'waitTaskTimeBeforeRetry' => $this->defaultWaitTaskTimeBeforeRetry,
113+
{{/isIngestionClient}}
101114
{{#isSearchClient}}
102115
'region' => null,
103116
'waitTaskTimeBeforeRetry' => $this->defaultWaitTaskTimeBeforeRetry,

tests/CTS/client/search/partialUpdateObjectsWithTransformation.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
"appId": "test-app-id",
1010
"apiKey": "test-api-key",
1111
"customHosts": [
12+
{
13+
"port": 6688
14+
},
1215
{
1316
"port": 6689
1417
}
@@ -31,12 +34,13 @@
3134
"name": "Benoit"
3235
}
3336
],
34-
"createIfNotExists": true
37+
"createIfNotExists": true,
38+
"waitForTasks": true
3539
},
3640
"expected": {
3741
"type": "response",
3842
"match": [{
39-
"runID": "b1b7a982-524c-40d2-bb7f-48aab075abda",
43+
"runID": "b1b7a982-524c-40d2-bb7f-48aab075abda_${{language}}",
4044
"eventID": "113b2068-6337-4c85-b5c2-e7b213d82925",
4145
"message": "OK",
4246
"createdAt": "2022-05-12T06:24:30.049Z"

tests/CTS/client/search/saveObjectsWithTransformation.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
"appId": "test-app-id",
1010
"apiKey": "test-api-key",
1111
"customHosts": [
12+
{
13+
"port": 6688
14+
},
1215
{
1316
"port": 6689
1417
}
@@ -30,12 +33,13 @@
3033
"objectID": "2",
3134
"name": "Benoit"
3235
}
33-
]
36+
],
37+
"waitForTasks": true
3438
},
3539
"expected": {
3640
"type": "response",
3741
"match": [{
38-
"runID": "b1b7a982-524c-40d2-bb7f-48aab075abda",
42+
"runID": "b1b7a982-524c-40d2-bb7f-48aab075abda_${{language}}",
3943
"eventID": "113b2068-6337-4c85-b5c2-e7b213d82925",
4044
"message": "OK",
4145
"createdAt": "2022-05-12T06:24:30.049Z"

0 commit comments

Comments
 (0)