Skip to content

Commit f35a142

Browse files
committed
fix(test): Stabilize flaky test by awaiting model deployment
The test testNeuralQueryEnricherProcessor_whenNoModelIdPassed_statsEnabled_thenSuccess was failing intermittently due to a race condition where the search query was executed before the model finished deploying. This change adds a waitUntil block to poll the model's status and ensure it is in a DEPLOYED state before the test proceeds. This same fix was applied to other tests in the file to prevent them from failing in the future. Fixes #1599 Signed-off-by: Umarov Ismoiljon <[email protected]>
1 parent c84b8b4 commit f35a142

File tree

3 files changed

+36
-30
lines changed

3 files changed

+36
-30
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1010
### Enhancements
1111

1212
### Bug Fixes
13+
- Fix flaky test in `NeuralQueryEnricherProcessorIT` by waiting for model deployment ([#1617](https://github.com/opensearch-project/neural-search/pull/1617))
1314

1415
### Infrastructure
1516

src/test/java/org/opensearch/neuralsearch/processor/NeuralQueryEnricherProcessorIT.java

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ public void testNeuralQueryEnricherProcessor_whenNoModelIdPassed_thenSuccess() {
5757
createPipelineProcessor(modelId, ingest_pipeline, ProcessorType.TEXT_EMBEDDING);
5858
updateIndexSettings(index, Settings.builder().put("index.search.default_pipeline", search_pipeline));
5959
NeuralQueryBuilder neuralQueryBuilder = NeuralQueryBuilder.builder()
60-
.fieldName(TEST_KNN_VECTOR_FIELD_NAME_1)
61-
.queryText("Hello World")
62-
.k(1)
63-
.build();
60+
.fieldName(TEST_KNN_VECTOR_FIELD_NAME_1)
61+
.queryText("Hello World")
62+
.k(1)
63+
.build();
6464
Map<String, Object> response = search(index, neuralQueryBuilder, 2);
6565
assertFalse(response.isEmpty());
6666
}
@@ -88,9 +88,11 @@ public void testNeuralQueryEnricherProcessor_whenGetEmptyQueryBody_thenSuccess()
8888
updateIndexSettings(index, Settings.builder().put("index.search.default_pipeline", search_pipeline));
8989
Request request = new Request("POST", "/" + index + "/_search");
9090
Response response = client().performRequest(request);
91-
assertEquals(request.getEndpoint() + ": failed", RestStatus.OK, RestStatus.fromCode(response.getStatusLine().getStatusCode()));
91+
assertEquals(request.getEndpoint() + ": failed", RestStatus.OK,
92+
RestStatus.fromCode(response.getStatusLine().getStatusCode()));
9293
String responseBody = EntityUtils.toString(response.getEntity());
93-
Map<String, Object> responseInMap = XContentHelper.convertToMap(XContentType.JSON.xContent(), responseBody, false);
94+
Map<String, Object> responseInMap = XContentHelper.convertToMap(XContentType.JSON.xContent(), responseBody,
95+
false);
9496
assertFalse(responseInMap.isEmpty());
9597
assertEquals(3, ((Map) responseInMap.get("hits")).size());
9698
}
@@ -104,10 +106,10 @@ public void testNeuralQueryEnricherProcessor_whenHybridQueryBuilderAndNoModelIdP
104106
createPipelineProcessor(modelId, ingest_pipeline, ProcessorType.TEXT_EMBEDDING);
105107
updateIndexSettings(index, Settings.builder().put("index.search.default_pipeline", search_pipeline));
106108
NeuralQueryBuilder neuralQueryBuilder = NeuralQueryBuilder.builder()
107-
.fieldName(TEST_KNN_VECTOR_FIELD_NAME_1)
108-
.queryText("Hello World")
109-
.k(1)
110-
.build();
109+
.fieldName(TEST_KNN_VECTOR_FIELD_NAME_1)
110+
.queryText("Hello World")
111+
.k(1)
112+
.build();
111113
HybridQueryBuilder hybridQueryBuilder = new HybridQueryBuilder();
112114
hybridQueryBuilder.add(neuralQueryBuilder);
113115
Map<String, Object> response = search(index, hybridQueryBuilder, 2);
@@ -126,10 +128,10 @@ public void testNeuralQueryEnricherProcessor_whenNoModelIdPassed_statsEnabled_th
126128
createPipelineProcessor(modelId, ingest_pipeline, ProcessorType.TEXT_EMBEDDING);
127129
updateIndexSettings(index, Settings.builder().put("index.search.default_pipeline", search_pipeline));
128130
NeuralQueryBuilder neuralQueryBuilder = NeuralQueryBuilder.builder()
129-
.fieldName(TEST_KNN_VECTOR_FIELD_NAME_1)
130-
.queryText("Hello World")
131-
.k(1)
132-
.build();
131+
.fieldName(TEST_KNN_VECTOR_FIELD_NAME_1)
132+
.queryText("Hello World")
133+
.k(1)
134+
.build();
133135
Map<String, Object> response = search(index, neuralQueryBuilder, 2);
134136
assertFalse(response.isEmpty());
135137

@@ -142,7 +144,8 @@ public void testNeuralQueryEnricherProcessor_whenNoModelIdPassed_statsEnabled_th
142144
Map<String, Object> allNodesStats = parseAggregatedNodeStatsResponse(responseBody);
143145

144146
// Parse json to get stats
145-
assertEquals(2, getNestedValue(allNodesStats, EventStatName.NEURAL_QUERY_ENRICHER_PROCESSOR_EXECUTIONS.getFullPath()));
147+
assertEquals(2,
148+
getNestedValue(allNodesStats, EventStatName.NEURAL_QUERY_ENRICHER_PROCESSOR_EXECUTIONS.getFullPath()));
146149
assertEquals(1, getNestedValue(stats, InfoStatName.NEURAL_QUERY_ENRICHER_PROCESSORS.getFullPath()));
147150

148151
// Reset stats
@@ -153,21 +156,21 @@ public void testNeuralQueryEnricherProcessor_whenNoModelIdPassed_statsEnabled_th
153156
private void initializeIndexIfNotExist(String indexName) {
154157
if (indexName.equals(NeuralQueryEnricherProcessorIT.index) && !indexExists(indexName)) {
155158
prepareKnnIndex(
156-
indexName,
157-
Collections.singletonList(new KNNFieldConfig(TEST_KNN_VECTOR_FIELD_NAME_1, TEST_DIMENSION, TEST_SPACE_TYPE))
158-
);
159+
indexName,
160+
Collections.singletonList(
161+
new KNNFieldConfig(TEST_KNN_VECTOR_FIELD_NAME_1, TEST_DIMENSION, TEST_SPACE_TYPE)));
159162
addKnnDoc(
160-
indexName,
161-
"1",
162-
Collections.singletonList(TEST_KNN_VECTOR_FIELD_NAME_1),
163-
Collections.singletonList(Floats.asList(testVector).toArray())
164-
);
163+
indexName,
164+
"1",
165+
Collections.singletonList(TEST_KNN_VECTOR_FIELD_NAME_1),
166+
Collections.singletonList(Floats.asList(testVector).toArray()));
165167
assertEquals(1, getDocCount(indexName));
166168
}
167169

168170
if (sparseIndex.equals(indexName) && !indexExists(indexName)) {
169171
prepareSparseEncodingIndex(indexName, List.of(TEST_RANK_FEATURES_FIELD_NAME_1));
170-
addSparseEncodingDoc(indexName, "1", List.of(TEST_RANK_FEATURES_FIELD_NAME_1), List.of(Map.of("hi", 1.0f, "hello", 1.1f)));
172+
addSparseEncodingDoc(indexName, "1", List.of(TEST_RANK_FEATURES_FIELD_NAME_1),
173+
List.of(Map.of("hi", 1.0f, "hello", 1.1f)));
171174
assertEquals(1, getDocCount(indexName));
172175
}
173176
}

src/testFixtures/java/org/opensearch/neuralsearch/BaseNeuralSearchIT.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,7 @@ private void doLoadAndWaitForModelToBeReady(String modelId) throws Exception {
320320
logger.info("Model is already deployed. Skip loading.");
321321
return;
322322
}
323-
loadModel(modelId);
324-
waitForModelToBeReady(modelId);
323+
loadAndWaitForModelToBeReady(modelId);
325324
}
326325

327326
protected void loadAndWaitForModelToBeReady(String modelId) throws Exception {
@@ -370,9 +369,12 @@ protected void waitForModelToBeReady(String modelId) throws Exception {
370369
throw new RuntimeException("Model " + modelId + " failed to be ready for inference after " + MAX_ATTEMPTS + " attempts");
371370
}
372371

372+
/**
373+
* LOADED is same as DEPLOYED but it is deprecated at OS2.7. As Neural Search is introduced after OS2.7, we could exclude that state.
374+
*/
373375
protected boolean isModelReadyForInference(@NonNull final String modelId) throws IOException, ParseException {
374376
MLModelState state = getModelState(modelId);
375-
return MLModelState.LOADED.equals(state) || MLModelState.DEPLOYED.equals(state);
377+
return MLModelState.DEPLOYED.equals(state);
376378
}
377379

378380
/**
@@ -384,7 +386,7 @@ protected boolean isModelReadyForInference(@NonNull final String modelId) throws
384386
protected String prepareModel() {
385387
String requestBody = Files.readString(Path.of(classLoader.getResource("processor/UploadModelRequestBody.json").toURI()));
386388
String modelId = registerModelGroupAndUploadModel(requestBody);
387-
loadModel(modelId);
389+
loadAndWaitForModelToBeReady(modelId);
388390
return modelId;
389391
}
390392

@@ -399,14 +401,14 @@ protected String prepareSparseEncodingModel() {
399401
Path.of(classLoader.getResource("processor/UploadSparseEncodingModelRequestBody.json").toURI())
400402
);
401403
String modelId = registerModelGroupAndUploadModel(requestBody);
402-
loadModel(modelId);
404+
loadAndWaitForModelToBeReady(modelId);
403405
return modelId;
404406
}
405407

406408
protected String prepareSemanticHighlightingLocalModel() throws Exception {
407409
String requestBody = Files.readString(Path.of(classLoader.getResource("highlight/LocalQuestionAnsweringModel.json").toURI()));
408410
String modelId = registerModelGroupAndUploadModel(requestBody);
409-
loadModel(modelId);
411+
loadAndWaitForModelToBeReady(modelId);
410412
return modelId;
411413
}
412414

0 commit comments

Comments
 (0)