Skip to content

Commit c8053d4

Browse files
authored
[ML] Skip Usage stats update when ML is disabled (#121559)
Do not call ML's GetDeploymentStatsAction API when ML is disabled in the cluster, instead return the inference configurations as-is. Fix #121532
1 parent 885a551 commit c8053d4

File tree

3 files changed

+87
-1
lines changed

3 files changed

+87
-1
lines changed

docs/changelog/121559.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 121559
2+
summary: Skip Usage stats update when ML is disabled
3+
area: Machine Learning
4+
type: bug
5+
issues:
6+
- 121532

x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elasticsearch/ElasticsearchInternalService.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.elasticsearch.action.ActionListener;
1616
import org.elasticsearch.common.logging.DeprecationCategory;
1717
import org.elasticsearch.common.logging.DeprecationLogger;
18+
import org.elasticsearch.common.settings.Settings;
1819
import org.elasticsearch.common.util.LazyInitializable;
1920
import org.elasticsearch.core.Nullable;
2021
import org.elasticsearch.core.TimeValue;
@@ -33,6 +34,7 @@
3334
import org.elasticsearch.inference.UnifiedCompletionRequest;
3435
import org.elasticsearch.inference.configuration.SettingsConfigurationFieldType;
3536
import org.elasticsearch.rest.RestStatus;
37+
import org.elasticsearch.xpack.core.XPackSettings;
3638
import org.elasticsearch.xpack.core.inference.results.InferenceTextEmbeddingFloatResults;
3739
import org.elasticsearch.xpack.core.inference.results.RankedDocsResults;
3840
import org.elasticsearch.xpack.core.inference.results.SparseEmbeddingResults;
@@ -109,8 +111,11 @@ public class ElasticsearchInternalService extends BaseElasticsearchInternalServi
109111
private static final Logger logger = LogManager.getLogger(ElasticsearchInternalService.class);
110112
private static final DeprecationLogger DEPRECATION_LOGGER = DeprecationLogger.getLogger(ElasticsearchInternalService.class);
111113

114+
private final Settings settings;
115+
112116
public ElasticsearchInternalService(InferenceServiceExtension.InferenceServiceFactoryContext context) {
113117
super(context);
118+
this.settings = context.settings();
114119
}
115120

116121
// for testing
@@ -119,6 +124,7 @@ public ElasticsearchInternalService(InferenceServiceExtension.InferenceServiceFa
119124
Consumer<ActionListener<PreferredModelVariant>> platformArch
120125
) {
121126
super(context, platformArch);
127+
this.settings = context.settings();
122128
}
123129

124130
@Override
@@ -837,12 +843,17 @@ public List<DefaultConfigId> defaultConfigIds() {
837843

838844
@Override
839845
public void updateModelsWithDynamicFields(List<Model> models, ActionListener<List<Model>> listener) {
840-
841846
if (models.isEmpty()) {
842847
listener.onResponse(models);
843848
return;
844849
}
845850

851+
// if ML is disabled, do not update Deployment Stats (there won't be changes)
852+
if (XPackSettings.MACHINE_LEARNING_ENABLED.get(settings) == false) {
853+
listener.onResponse(models);
854+
return;
855+
}
856+
846857
var modelsByDeploymentIds = new HashMap<String, ElasticsearchInternalModel>();
847858
for (var model : models) {
848859
assert model instanceof ElasticsearchInternalModel;

x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/elasticsearch/ElasticsearchInternalServiceTests.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.elasticsearch.ElasticsearchStatusException;
1414
import org.elasticsearch.action.ActionListener;
1515
import org.elasticsearch.action.LatchedActionListener;
16+
import org.elasticsearch.action.support.ActionTestUtils;
1617
import org.elasticsearch.action.support.PlainActionFuture;
1718
import org.elasticsearch.client.internal.Client;
1819
import org.elasticsearch.cluster.service.ClusterService;
@@ -46,12 +47,14 @@
4647
import org.elasticsearch.xpack.core.inference.results.ChunkedInferenceEmbeddingSparse;
4748
import org.elasticsearch.xpack.core.inference.results.ChunkedInferenceError;
4849
import org.elasticsearch.xpack.core.ml.MachineLearningField;
50+
import org.elasticsearch.xpack.core.ml.action.GetDeploymentStatsAction;
4951
import org.elasticsearch.xpack.core.ml.action.GetTrainedModelsAction;
5052
import org.elasticsearch.xpack.core.ml.action.InferModelAction;
5153
import org.elasticsearch.xpack.core.ml.action.InferTrainedModelDeploymentAction;
5254
import org.elasticsearch.xpack.core.ml.action.PutTrainedModelAction;
5355
import org.elasticsearch.xpack.core.ml.inference.TrainedModelConfig;
5456
import org.elasticsearch.xpack.core.ml.inference.TrainedModelPrefixStrings;
57+
import org.elasticsearch.xpack.core.ml.inference.assignment.AssignmentStats;
5558
import org.elasticsearch.xpack.core.ml.inference.results.ErrorInferenceResults;
5659
import org.elasticsearch.xpack.core.ml.inference.results.MlTextEmbeddingResults;
5760
import org.elasticsearch.xpack.core.ml.inference.results.MlTextEmbeddingResultsTests;
@@ -67,11 +70,13 @@
6770
import org.elasticsearch.xpack.inference.chunking.EmbeddingRequestChunker;
6871
import org.elasticsearch.xpack.inference.chunking.WordBoundaryChunkingSettings;
6972
import org.elasticsearch.xpack.inference.services.ServiceFields;
73+
import org.hamcrest.Matchers;
7074
import org.junit.After;
7175
import org.junit.Before;
7276
import org.mockito.ArgumentCaptor;
7377
import org.mockito.Mockito;
7478

79+
import java.io.IOException;
7580
import java.util.ArrayList;
7681
import java.util.Arrays;
7782
import java.util.EnumSet;
@@ -81,12 +86,14 @@
8186
import java.util.Optional;
8287
import java.util.Set;
8388
import java.util.concurrent.CountDownLatch;
89+
import java.util.concurrent.TimeUnit;
8490
import java.util.concurrent.atomic.AtomicBoolean;
8591
import java.util.concurrent.atomic.AtomicInteger;
8692
import java.util.concurrent.atomic.AtomicReference;
8793

8894
import static org.elasticsearch.common.xcontent.XContentHelper.toXContent;
8995
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent;
96+
import static org.elasticsearch.xpack.core.ml.action.GetTrainedModelsStatsAction.Response.RESULTS_FIELD;
9097
import static org.elasticsearch.xpack.inference.chunking.ChunkingSettingsTests.createRandomChunkingSettingsMap;
9198
import static org.elasticsearch.xpack.inference.services.elasticsearch.ElasticsearchInternalService.MULTILINGUAL_E5_SMALL_MODEL_ID;
9299
import static org.elasticsearch.xpack.inference.services.elasticsearch.ElasticsearchInternalService.MULTILINGUAL_E5_SMALL_MODEL_ID_LINUX_X86;
@@ -101,6 +108,7 @@
101108
import static org.mockito.ArgumentMatchers.same;
102109
import static org.mockito.Mockito.doAnswer;
103110
import static org.mockito.Mockito.mock;
111+
import static org.mockito.Mockito.verify;
104112
import static org.mockito.Mockito.when;
105113

106114
public class ElasticsearchInternalServiceTests extends ESTestCase {
@@ -1632,6 +1640,67 @@ public void testGetConfiguration() throws Exception {
16321640
}
16331641
}
16341642

1643+
public void testUpdateWithoutMlEnabled() throws IOException, InterruptedException {
1644+
var cs = mock(ClusterService.class);
1645+
var cSettings = new ClusterSettings(Settings.EMPTY, Set.of(MachineLearningField.MAX_LAZY_ML_NODES));
1646+
when(cs.getClusterSettings()).thenReturn(cSettings);
1647+
var context = new InferenceServiceExtension.InferenceServiceFactoryContext(
1648+
mock(),
1649+
threadPool,
1650+
cs,
1651+
Settings.builder().put("xpack.ml.enabled", false).build()
1652+
);
1653+
try (var service = new ElasticsearchInternalService(context)) {
1654+
var models = List.of(mock(Model.class));
1655+
var latch = new CountDownLatch(1);
1656+
service.updateModelsWithDynamicFields(models, ActionTestUtils.assertNoFailureListener(r -> {
1657+
latch.countDown();
1658+
assertThat(r, Matchers.sameInstance(models));
1659+
}));
1660+
assertTrue(latch.await(30, TimeUnit.SECONDS));
1661+
}
1662+
}
1663+
1664+
public void testUpdateWithMlEnabled() throws IOException, InterruptedException {
1665+
var deploymentId = "deploymentId";
1666+
var model = mock(ElasticsearchInternalModel.class);
1667+
when(model.mlNodeDeploymentId()).thenReturn(deploymentId);
1668+
1669+
AssignmentStats stats = mock();
1670+
when(stats.getDeploymentId()).thenReturn(deploymentId);
1671+
when(stats.getNumberOfAllocations()).thenReturn(3);
1672+
1673+
var client = mock(Client.class);
1674+
doAnswer(ans -> {
1675+
QueryPage<AssignmentStats> queryPage = new QueryPage<>(List.of(stats), 1, RESULTS_FIELD);
1676+
1677+
GetDeploymentStatsAction.Response response = mock();
1678+
when(response.getStats()).thenReturn(queryPage);
1679+
1680+
ActionListener<GetDeploymentStatsAction.Response> listener = ans.getArgument(2);
1681+
listener.onResponse(response);
1682+
return null;
1683+
}).when(client).execute(eq(GetDeploymentStatsAction.INSTANCE), any(), any());
1684+
when(client.threadPool()).thenReturn(threadPool);
1685+
1686+
var cs = mock(ClusterService.class);
1687+
var cSettings = new ClusterSettings(Settings.EMPTY, Set.of(MachineLearningField.MAX_LAZY_ML_NODES));
1688+
when(cs.getClusterSettings()).thenReturn(cSettings);
1689+
var context = new InferenceServiceExtension.InferenceServiceFactoryContext(
1690+
client,
1691+
threadPool,
1692+
cs,
1693+
Settings.builder().put("xpack.ml.enabled", true).build()
1694+
);
1695+
try (var service = new ElasticsearchInternalService(context)) {
1696+
List<Model> models = List.of(model);
1697+
var latch = new CountDownLatch(1);
1698+
service.updateModelsWithDynamicFields(models, ActionTestUtils.assertNoFailureListener(r -> latch.countDown()));
1699+
assertTrue(latch.await(30, TimeUnit.SECONDS));
1700+
verify(model).updateNumAllocations(3);
1701+
}
1702+
}
1703+
16351704
private ElasticsearchInternalService createService(Client client) {
16361705
var cs = mock(ClusterService.class);
16371706
var cSettings = new ClusterSettings(Settings.EMPTY, Set.of(MachineLearningField.MAX_LAZY_ML_NODES));

0 commit comments

Comments
 (0)