|
10 | 10 | import org.elasticsearch.action.index.IndexRequest;
|
11 | 11 | import org.elasticsearch.action.support.PlainActionFuture;
|
12 | 12 | import org.elasticsearch.client.internal.OriginSettingClient;
|
| 13 | +import org.elasticsearch.common.settings.Settings; |
13 | 14 | import org.elasticsearch.common.unit.ByteSizeValue;
|
14 | 15 | import org.elasticsearch.indices.TestIndexNameExpressionResolver;
|
15 | 16 | import org.elasticsearch.tasks.TaskId;
|
@@ -63,91 +64,118 @@ public void createComponents() {
|
63 | 64 | }
|
64 | 65 |
|
65 | 66 | public void testRemoveUnusedStats() throws Exception {
|
| 67 | + String modelId = "model-with-stats"; |
| 68 | + putDFA(modelId); |
66 | 69 |
|
67 |
| - prepareIndex("foo").setId("some-empty-doc").setSource("{}", XContentType.JSON).get(); |
68 |
| - |
69 |
| - PutDataFrameAnalyticsAction.Request request = new PutDataFrameAnalyticsAction.Request( |
70 |
| - new DataFrameAnalyticsConfig.Builder().setId("analytics-with-stats") |
71 |
| - .setModelMemoryLimit(ByteSizeValue.ofGb(1)) |
72 |
| - .setSource(new DataFrameAnalyticsSource(new String[] { "foo" }, null, null, null)) |
73 |
| - .setDest(new DataFrameAnalyticsDest("bar", null)) |
74 |
| - .setAnalysis(new Regression("prediction")) |
75 |
| - .build() |
76 |
| - ); |
77 |
| - client.execute(PutDataFrameAnalyticsAction.INSTANCE, request).actionGet(); |
78 |
| - |
79 |
| - client.execute( |
80 |
| - PutTrainedModelAction.INSTANCE, |
81 |
| - new PutTrainedModelAction.Request( |
82 |
| - TrainedModelConfig.builder() |
83 |
| - .setModelId("model-with-stats") |
84 |
| - .setInferenceConfig(RegressionConfig.EMPTY_PARAMS) |
85 |
| - .setInput(new TrainedModelInput(Arrays.asList("foo", "bar"))) |
86 |
| - .setParsedDefinition( |
87 |
| - new TrainedModelDefinition.Builder().setPreProcessors(Collections.emptyList()) |
88 |
| - .setTrainedModel( |
89 |
| - Tree.builder() |
90 |
| - .setFeatureNames(Arrays.asList("foo", "bar")) |
91 |
| - .setRoot(TreeNode.builder(0).setLeafValue(42)) |
92 |
| - .build() |
93 |
| - ) |
94 |
| - ) |
95 |
| - .validate(true) |
96 |
| - .build(), |
97 |
| - false |
98 |
| - ) |
99 |
| - ).actionGet(); |
100 |
| - |
| 70 | + // Existing analytics and models |
101 | 71 | indexStatDocument(new DataCounts("analytics-with-stats", 1, 1, 1), DataCounts.documentId("analytics-with-stats"));
|
102 |
| - indexStatDocument(new DataCounts("missing-analytics-with-stats", 1, 1, 1), DataCounts.documentId("missing-analytics-with-stats")); |
| 72 | + indexStatDocument(new InferenceStats(1, 1, 1, 1, modelId, "test", Instant.now()), InferenceStats.docId(modelId, "test")); |
103 | 73 | indexStatDocument(
|
104 | 74 | new InferenceStats(1, 1, 1, 1, TrainedModelProvider.MODELS_STORED_AS_RESOURCE.iterator().next(), "test", Instant.now()),
|
105 | 75 | InferenceStats.docId(TrainedModelProvider.MODELS_STORED_AS_RESOURCE.iterator().next(), "test")
|
106 | 76 | );
|
| 77 | + |
| 78 | + // Unused analytics/model stats |
| 79 | + indexStatDocument(new DataCounts("missing-analytics-with-stats", 1, 1, 1), DataCounts.documentId("missing-analytics-with-stats")); |
107 | 80 | indexStatDocument(
|
108 | 81 | new InferenceStats(1, 1, 1, 1, "missing-model", "test", Instant.now()),
|
109 | 82 | InferenceStats.docId("missing-model", "test")
|
110 | 83 | );
|
| 84 | + |
| 85 | + refreshStatsIndex(); |
| 86 | + runUnusedStatsRemover(); |
| 87 | + |
| 88 | + final String index = MlStatsIndex.TEMPLATE_NAME + "-000001"; |
| 89 | + |
| 90 | + // Validate expected docs |
| 91 | + assertDocExists(index, InferenceStats.docId(modelId, "test")); |
| 92 | + assertDocExists(index, DataCounts.documentId("analytics-with-stats")); |
| 93 | + assertDocExists(index, InferenceStats.docId(TrainedModelProvider.MODELS_STORED_AS_RESOURCE.iterator().next(), "test")); |
| 94 | + |
| 95 | + // Validate removed docs |
| 96 | + assertDocDoesNotExist(index, InferenceStats.docId("missing-model", "test")); |
| 97 | + assertDocDoesNotExist(index, DataCounts.documentId("missing-analytics-with-stats")); |
| 98 | + } |
| 99 | + |
| 100 | + public void testRemovingUnusedStatsFromReadOnlyIndexShouldFailSilently() throws Exception { |
| 101 | + String modelId = "model-with-stats"; |
| 102 | + putDFA(modelId); |
| 103 | + |
111 | 104 | indexStatDocument(
|
112 |
| - new InferenceStats(1, 1, 1, 1, "model-with-stats", "test", Instant.now()), |
113 |
| - InferenceStats.docId("model-with-stats", "test") |
| 105 | + new InferenceStats(1, 1, 1, 1, "missing-model", "test", Instant.now()), |
| 106 | + InferenceStats.docId("missing-model", "test") |
114 | 107 | );
|
115 |
| - client().admin().indices().prepareRefresh(MlStatsIndex.indexPattern()).get(); |
| 108 | + makeIndexReadOnly(); |
| 109 | + refreshStatsIndex(); |
116 | 110 |
|
117 |
| - PlainActionFuture<Boolean> deletionListener = new PlainActionFuture<>(); |
118 |
| - UnusedStatsRemover statsRemover = new UnusedStatsRemover(client, new TaskId("test", 0L)); |
119 |
| - statsRemover.remove(10000.0f, deletionListener, () -> false); |
120 |
| - deletionListener.actionGet(); |
| 111 | + runUnusedStatsRemover(); |
| 112 | + refreshStatsIndex(); |
121 | 113 |
|
122 |
| - client().admin().indices().prepareRefresh(MlStatsIndex.indexPattern()).get(); |
| 114 | + final String index = MlStatsIndex.TEMPLATE_NAME + "-000001"; |
| 115 | + assertDocExists(index, InferenceStats.docId("missing-model", "test")); // should still exist |
| 116 | + } |
123 | 117 |
|
124 |
| - final String initialStateIndex = MlStatsIndex.TEMPLATE_NAME + "-000001"; |
| 118 | + private void putDFA(String modelId) { |
| 119 | + prepareIndex("foo").setId("some-empty-doc").setSource("{}", XContentType.JSON).get(); |
125 | 120 |
|
126 |
| - // Make sure that stats that should exist still exist |
127 |
| - assertTrue(client().prepareGet(initialStateIndex, InferenceStats.docId("model-with-stats", "test")).get().isExists()); |
128 |
| - assertTrue( |
129 |
| - client().prepareGet( |
130 |
| - initialStateIndex, |
131 |
| - InferenceStats.docId(TrainedModelProvider.MODELS_STORED_AS_RESOURCE.iterator().next(), "test") |
132 |
| - ).get().isExists() |
| 121 | + PutDataFrameAnalyticsAction.Request analyticsRequest = new PutDataFrameAnalyticsAction.Request( |
| 122 | + new DataFrameAnalyticsConfig.Builder().setId("analytics-with-stats") |
| 123 | + .setModelMemoryLimit(ByteSizeValue.ofGb(1)) |
| 124 | + .setSource(new DataFrameAnalyticsSource(new String[] { "foo" }, null, null, null)) |
| 125 | + .setDest(new DataFrameAnalyticsDest("bar", null)) |
| 126 | + .setAnalysis(new Regression("prediction")) |
| 127 | + .build() |
133 | 128 | );
|
134 |
| - assertTrue(client().prepareGet(initialStateIndex, DataCounts.documentId("analytics-with-stats")).get().isExists()); |
135 |
| - |
136 |
| - // make sure that unused stats were deleted |
137 |
| - assertFalse(client().prepareGet(initialStateIndex, DataCounts.documentId("missing-analytics-with-stats")).get().isExists()); |
138 |
| - assertFalse(client().prepareGet(initialStateIndex, InferenceStats.docId("missing-model", "test")).get().isExists()); |
| 129 | + client.execute(PutDataFrameAnalyticsAction.INSTANCE, analyticsRequest).actionGet(); |
| 130 | + |
| 131 | + TrainedModelDefinition.Builder definition = new TrainedModelDefinition.Builder().setPreProcessors(Collections.emptyList()) |
| 132 | + .setTrainedModel( |
| 133 | + Tree.builder().setFeatureNames(Arrays.asList("foo", "bar")).setRoot(TreeNode.builder(0).setLeafValue(42)).build() |
| 134 | + ); |
| 135 | + |
| 136 | + TrainedModelConfig modelConfig = TrainedModelConfig.builder() |
| 137 | + .setModelId(modelId) |
| 138 | + .setInferenceConfig(RegressionConfig.EMPTY_PARAMS) |
| 139 | + .setInput(new TrainedModelInput(Arrays.asList("foo", "bar"))) |
| 140 | + .setParsedDefinition(definition) |
| 141 | + .validate(true) |
| 142 | + .build(); |
| 143 | + |
| 144 | + client.execute(PutTrainedModelAction.INSTANCE, new PutTrainedModelAction.Request(modelConfig, false)).actionGet(); |
139 | 145 | }
|
140 | 146 |
|
141 | 147 | private void indexStatDocument(ToXContentObject object, String docId) throws Exception {
|
142 |
| - ToXContent.Params params = new ToXContent.MapParams( |
143 |
| - Collections.singletonMap(ToXContentParams.FOR_INTERNAL_STORAGE, Boolean.toString(true)) |
144 |
| - ); |
145 |
| - IndexRequest doc = new IndexRequest(MlStatsIndex.writeAlias()); |
146 |
| - doc.id(docId); |
| 148 | + IndexRequest doc = new IndexRequest(MlStatsIndex.writeAlias()).id(docId); |
147 | 149 | try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
|
148 |
| - object.toXContent(builder, params); |
| 150 | + object.toXContent(builder, new ToXContent.MapParams(Collections.singletonMap(ToXContentParams.FOR_INTERNAL_STORAGE, "true"))); |
149 | 151 | doc.source(builder);
|
150 | 152 | client.index(doc).actionGet();
|
151 | 153 | }
|
152 | 154 | }
|
| 155 | + |
| 156 | + private void refreshStatsIndex() { |
| 157 | + client().admin().indices().prepareRefresh(MlStatsIndex.indexPattern()).get(); |
| 158 | + } |
| 159 | + |
| 160 | + private void runUnusedStatsRemover() { |
| 161 | + PlainActionFuture<Boolean> deletionListener = new PlainActionFuture<>(); |
| 162 | + new UnusedStatsRemover(client, new TaskId("test", 0L)).remove(10000.0f, deletionListener, () -> false); |
| 163 | + deletionListener.actionGet(); |
| 164 | + } |
| 165 | + |
| 166 | + private void makeIndexReadOnly() { |
| 167 | + client().admin() |
| 168 | + .indices() |
| 169 | + .prepareUpdateSettings(MlStatsIndex.indexPattern()) |
| 170 | + .setSettings(Settings.builder().put("index.blocks.write", true)) |
| 171 | + .get(); |
| 172 | + } |
| 173 | + |
| 174 | + private void assertDocExists(String index, String docId) { |
| 175 | + assertTrue(client().prepareGet(index, docId).get().isExists()); |
| 176 | + } |
| 177 | + |
| 178 | + private void assertDocDoesNotExist(String index, String docId) { |
| 179 | + assertFalse(client().prepareGet(index, docId).get().isExists()); |
| 180 | + } |
153 | 181 | }
|
0 commit comments