Skip to content

Commit d569ff5

Browse files
authored
[ML] Skip Usage stats update when ML is disabled (#121559) (#121768)
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 cbb9b15 commit d569ff5

File tree

3 files changed

+87
-0
lines changed

3 files changed

+87
-0
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 & 0 deletions
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;
@@ -36,6 +37,7 @@
3637
import org.elasticsearch.inference.configuration.SettingsConfigurationFieldType;
3738
import org.elasticsearch.inference.configuration.SettingsConfigurationSelectOption;
3839
import org.elasticsearch.rest.RestStatus;
40+
import org.elasticsearch.xpack.core.XPackSettings;
3941
import org.elasticsearch.xpack.core.inference.results.InferenceTextEmbeddingFloatResults;
4042
import org.elasticsearch.xpack.core.inference.results.RankedDocsResults;
4143
import org.elasticsearch.xpack.core.inference.results.SparseEmbeddingResults;
@@ -111,8 +113,11 @@ public class ElasticsearchInternalService extends BaseElasticsearchInternalServi
111113
private static final Logger logger = LogManager.getLogger(ElasticsearchInternalService.class);
112114
private static final DeprecationLogger DEPRECATION_LOGGER = DeprecationLogger.getLogger(ElasticsearchInternalService.class);
113115

116+
private final Settings settings;
117+
114118
public ElasticsearchInternalService(InferenceServiceExtension.InferenceServiceFactoryContext context) {
115119
super(context);
120+
this.settings = context.settings();
116121
}
117122

118123
// for testing
@@ -121,6 +126,7 @@ public ElasticsearchInternalService(InferenceServiceExtension.InferenceServiceFa
121126
Consumer<ActionListener<PreferredModelVariant>> platformArch
122127
) {
123128
super(context, platformArch);
129+
this.settings = context.settings();
124130
}
125131

126132
@Override
@@ -825,6 +831,12 @@ public void updateModelsWithDynamicFields(List<Model> models, ActionListener<Lis
825831
return;
826832
}
827833

834+
// if ML is disabled, do not update Deployment Stats (there won't be changes)
835+
if (XPackSettings.MACHINE_LEARNING_ENABLED.get(settings) == false) {
836+
listener.onResponse(models);
837+
return;
838+
}
839+
828840
var modelsByDeploymentIds = new HashMap<String, ElasticsearchInternalModel>();
829841
for (var model : models) {
830842
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;
@@ -47,12 +48,14 @@
4748
import org.elasticsearch.xpack.core.inference.results.InferenceChunkedSparseEmbeddingResults;
4849
import org.elasticsearch.xpack.core.inference.results.InferenceChunkedTextEmbeddingFloatResults;
4950
import org.elasticsearch.xpack.core.ml.MachineLearningField;
51+
import org.elasticsearch.xpack.core.ml.action.GetDeploymentStatsAction;
5052
import org.elasticsearch.xpack.core.ml.action.GetTrainedModelsAction;
5153
import org.elasticsearch.xpack.core.ml.action.InferModelAction;
5254
import org.elasticsearch.xpack.core.ml.action.InferTrainedModelDeploymentAction;
5355
import org.elasticsearch.xpack.core.ml.action.PutTrainedModelAction;
5456
import org.elasticsearch.xpack.core.ml.inference.TrainedModelConfig;
5557
import org.elasticsearch.xpack.core.ml.inference.TrainedModelPrefixStrings;
58+
import org.elasticsearch.xpack.core.ml.inference.assignment.AssignmentStats;
5659
import org.elasticsearch.xpack.core.ml.inference.results.ErrorInferenceResults;
5760
import org.elasticsearch.xpack.core.ml.inference.results.MlTextEmbeddingResults;
5861
import org.elasticsearch.xpack.core.ml.inference.results.MlTextEmbeddingResultsTests;
@@ -68,11 +71,13 @@
6871
import org.elasticsearch.xpack.inference.chunking.EmbeddingRequestChunker;
6972
import org.elasticsearch.xpack.inference.chunking.WordBoundaryChunkingSettings;
7073
import org.elasticsearch.xpack.inference.services.ServiceFields;
74+
import org.hamcrest.Matchers;
7175
import org.junit.After;
7276
import org.junit.Before;
7377
import org.mockito.ArgumentCaptor;
7478
import org.mockito.Mockito;
7579

80+
import java.io.IOException;
7681
import java.util.ArrayList;
7782
import java.util.Arrays;
7883
import java.util.EnumSet;
@@ -82,12 +87,14 @@
8287
import java.util.Optional;
8388
import java.util.Set;
8489
import java.util.concurrent.CountDownLatch;
90+
import java.util.concurrent.TimeUnit;
8591
import java.util.concurrent.atomic.AtomicBoolean;
8692
import java.util.concurrent.atomic.AtomicInteger;
8793
import java.util.concurrent.atomic.AtomicReference;
8894

8995
import static org.elasticsearch.common.xcontent.XContentHelper.toXContent;
9096
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent;
97+
import static org.elasticsearch.xpack.core.ml.action.GetTrainedModelsStatsAction.Response.RESULTS_FIELD;
9198
import static org.elasticsearch.xpack.inference.chunking.ChunkingSettingsTests.createRandomChunkingSettingsMap;
9299
import static org.elasticsearch.xpack.inference.services.elasticsearch.ElasticsearchInternalService.MULTILINGUAL_E5_SMALL_MODEL_ID;
93100
import static org.elasticsearch.xpack.inference.services.elasticsearch.ElasticsearchInternalService.MULTILINGUAL_E5_SMALL_MODEL_ID_LINUX_X86;
@@ -102,6 +109,7 @@
102109
import static org.mockito.ArgumentMatchers.same;
103110
import static org.mockito.Mockito.doAnswer;
104111
import static org.mockito.Mockito.mock;
112+
import static org.mockito.Mockito.verify;
105113
import static org.mockito.Mockito.when;
106114

107115
public class ElasticsearchInternalServiceTests extends ESTestCase {
@@ -1698,6 +1706,67 @@ public void testGetConfiguration() throws Exception {
16981706
}
16991707
}
17001708

1709+
public void testUpdateWithoutMlEnabled() throws IOException, InterruptedException {
1710+
var cs = mock(ClusterService.class);
1711+
var cSettings = new ClusterSettings(Settings.EMPTY, Set.of(MachineLearningField.MAX_LAZY_ML_NODES));
1712+
when(cs.getClusterSettings()).thenReturn(cSettings);
1713+
var context = new InferenceServiceExtension.InferenceServiceFactoryContext(
1714+
mock(),
1715+
threadPool,
1716+
cs,
1717+
Settings.builder().put("xpack.ml.enabled", false).build()
1718+
);
1719+
try (var service = new ElasticsearchInternalService(context)) {
1720+
var models = List.of(mock(Model.class));
1721+
var latch = new CountDownLatch(1);
1722+
service.updateModelsWithDynamicFields(models, ActionTestUtils.assertNoFailureListener(r -> {
1723+
latch.countDown();
1724+
assertThat(r, Matchers.sameInstance(models));
1725+
}));
1726+
assertTrue(latch.await(30, TimeUnit.SECONDS));
1727+
}
1728+
}
1729+
1730+
public void testUpdateWithMlEnabled() throws IOException, InterruptedException {
1731+
var deploymentId = "deploymentId";
1732+
var model = mock(ElasticsearchInternalModel.class);
1733+
when(model.mlNodeDeploymentId()).thenReturn(deploymentId);
1734+
1735+
AssignmentStats stats = mock();
1736+
when(stats.getDeploymentId()).thenReturn(deploymentId);
1737+
when(stats.getNumberOfAllocations()).thenReturn(3);
1738+
1739+
var client = mock(Client.class);
1740+
doAnswer(ans -> {
1741+
QueryPage<AssignmentStats> queryPage = new QueryPage<>(List.of(stats), 1, RESULTS_FIELD);
1742+
1743+
GetDeploymentStatsAction.Response response = mock();
1744+
when(response.getStats()).thenReturn(queryPage);
1745+
1746+
ActionListener<GetDeploymentStatsAction.Response> listener = ans.getArgument(2);
1747+
listener.onResponse(response);
1748+
return null;
1749+
}).when(client).execute(eq(GetDeploymentStatsAction.INSTANCE), any(), any());
1750+
when(client.threadPool()).thenReturn(threadPool);
1751+
1752+
var cs = mock(ClusterService.class);
1753+
var cSettings = new ClusterSettings(Settings.EMPTY, Set.of(MachineLearningField.MAX_LAZY_ML_NODES));
1754+
when(cs.getClusterSettings()).thenReturn(cSettings);
1755+
var context = new InferenceServiceExtension.InferenceServiceFactoryContext(
1756+
client,
1757+
threadPool,
1758+
cs,
1759+
Settings.builder().put("xpack.ml.enabled", true).build()
1760+
);
1761+
try (var service = new ElasticsearchInternalService(context)) {
1762+
List<Model> models = List.of(model);
1763+
var latch = new CountDownLatch(1);
1764+
service.updateModelsWithDynamicFields(models, ActionTestUtils.assertNoFailureListener(r -> latch.countDown()));
1765+
assertTrue(latch.await(30, TimeUnit.SECONDS));
1766+
verify(model).updateNumAllocations(3);
1767+
}
1768+
}
1769+
17011770
private ElasticsearchInternalService createService(Client client) {
17021771
var cs = mock(ClusterService.class);
17031772
var cSettings = new ClusterSettings(Settings.EMPTY, Set.of(MachineLearningField.MAX_LAZY_ML_NODES));

0 commit comments

Comments
 (0)