|
22 | 22 | import org.opensearch.action.update.UpdateRequest;
|
23 | 23 | import org.opensearch.action.update.UpdateResponse;
|
24 | 24 | import org.opensearch.cluster.ClusterState;
|
| 25 | +import org.opensearch.common.xcontent.XContentFactory; |
| 26 | +import org.opensearch.common.xcontent.XContentHelper; |
| 27 | +import org.opensearch.core.common.bytes.BytesReference; |
| 28 | +import org.opensearch.core.xcontent.ToXContent; |
| 29 | +import org.opensearch.core.xcontent.XContentBuilder; |
25 | 30 | import org.opensearch.index.IndexNotFoundException;
|
26 | 31 | import org.opensearch.index.engine.DocumentMissingException;
|
27 | 32 | import org.opensearch.index.engine.VersionConflictEngineException;
|
|
31 | 36 | import org.opensearch.test.OpenSearchIntegTestCase.Scope;
|
32 | 37 | import org.hamcrest.MatcherAssert;
|
33 | 38 |
|
| 39 | +import java.io.IOException; |
| 40 | +import java.util.ArrayList; |
34 | 41 | import java.util.Arrays;
|
| 42 | +import java.util.Collections; |
35 | 43 | import java.util.Comparator;
|
| 44 | +import java.util.LinkedHashMap; |
36 | 45 | import java.util.Map;
|
37 |
| -import java.util.ArrayList; |
38 | 46 | import java.util.concurrent.atomic.AtomicLong;
|
39 | 47 |
|
40 | 48 | import static java.util.Collections.singletonMap;
|
@@ -246,47 +254,140 @@ public void testNodeIndicesStatsDocStatusStatsCreateDeleteUpdate() {
|
246 | 254 | }
|
247 | 255 | }
|
248 | 256 | }
|
249 |
| - public void testNodeIndicesStatsOptimisedResponse() { |
| 257 | + |
| 258 | + /** |
| 259 | + * Default behavior - without consideration of request level param on level, the NodeStatsRequest always |
| 260 | + * returns ShardStats which is aggregated on the coordinator node when creating the XContent. |
| 261 | + */ |
| 262 | + public void testNodeIndicesStatsDefaultResponse() { |
| 263 | + String testLevel = randomFrom("null", "node", "indices", "shards", "unknown"); |
250 | 264 | internalCluster().startNode();
|
251 | 265 | ensureGreen();
|
252 | 266 | String indexName = "test1";
|
253 | 267 | index(indexName, "type", "1", "f", "f");
|
254 | 268 | refresh();
|
255 | 269 | ClusterState clusterState = client().admin().cluster().prepareState().get().getState();
|
256 | 270 |
|
257 |
| - NodesStatsResponse response = client().admin().cluster().prepareNodesStats().get(); |
| 271 | + NodesStatsResponse response; |
| 272 | + if (!testLevel.equals("null")) { |
| 273 | + ArrayList<String> level_arg = new ArrayList<>(); |
| 274 | + level_arg.add(testLevel); |
| 275 | + |
| 276 | + CommonStatsFlags commonStatsFlags = new CommonStatsFlags(); |
| 277 | + commonStatsFlags.setLevels(level_arg.toArray(new String[0])); |
| 278 | + response = client().admin().cluster().prepareNodesStats().setIndices(commonStatsFlags).get(); |
| 279 | + } else { |
| 280 | + response = client().admin().cluster().prepareNodesStats().get(); |
| 281 | + } |
| 282 | + |
258 | 283 | response.getNodes().forEach(nodeStats -> {
|
259 | 284 | assertNotNull(nodeStats.getIndices().getShardStats(clusterState.metadata().index(indexName).getIndex()));
|
260 |
| - assertNull(nodeStats.getIndices().getIndexStats(clusterState.metadata().index(indexName).getIndex())); |
| 285 | + try { |
| 286 | + // Without any param - default is level = nodes |
| 287 | + XContentBuilder builder = XContentFactory.jsonBuilder(); |
| 288 | + builder.startObject(); |
| 289 | + builder = nodeStats.getIndices().toXContent(builder, ToXContent.EMPTY_PARAMS); |
| 290 | + builder.endObject(); |
| 291 | + |
| 292 | + Map<String, Object> xContentMap = xContentBuilderToMap(builder); |
| 293 | + LinkedHashMap indicesStatsMap = (LinkedHashMap) xContentMap.get("indices"); |
| 294 | + assertFalse(indicesStatsMap.containsKey("indices")); |
| 295 | + assertFalse(indicesStatsMap.containsKey("shards")); |
| 296 | + |
| 297 | + // With param containing level as 'indices', the indices stats are returned |
| 298 | + builder = XContentFactory.jsonBuilder(); |
| 299 | + builder.startObject(); |
| 300 | + builder = nodeStats.getIndices() |
| 301 | + .toXContent(builder, new ToXContent.MapParams(Collections.singletonMap("level", "indices"))); |
| 302 | + builder.endObject(); |
| 303 | + |
| 304 | + xContentMap = xContentBuilderToMap(builder); |
| 305 | + indicesStatsMap = (LinkedHashMap) xContentMap.get("indices"); |
| 306 | + assertTrue(indicesStatsMap.containsKey("indices")); |
| 307 | + assertFalse(indicesStatsMap.containsKey("shards")); |
| 308 | + |
| 309 | + LinkedHashMap indexLevelStats = (LinkedHashMap) indicesStatsMap.get("indices"); |
| 310 | + assertTrue(indexLevelStats.containsKey(indexName)); |
| 311 | + |
| 312 | + // With param containing level as 'shards', the shard stats are returned |
| 313 | + builder = XContentFactory.jsonBuilder(); |
| 314 | + builder.startObject(); |
| 315 | + builder = nodeStats.getIndices().toXContent(builder, new ToXContent.MapParams(Collections.singletonMap("level", "shards"))); |
| 316 | + builder.endObject(); |
| 317 | + |
| 318 | + xContentMap = xContentBuilderToMap(builder); |
| 319 | + indicesStatsMap = (LinkedHashMap) xContentMap.get("indices"); |
| 320 | + assertFalse(indicesStatsMap.containsKey("indices")); |
| 321 | + assertTrue(indicesStatsMap.containsKey("shards")); |
| 322 | + |
| 323 | + LinkedHashMap shardLevelStats = (LinkedHashMap) indicesStatsMap.get("shards"); |
| 324 | + assertTrue(shardLevelStats.containsKey(indexName)); |
| 325 | + } catch (IOException e) { |
| 326 | + throw new RuntimeException(e); |
| 327 | + } |
261 | 328 | });
|
| 329 | + } |
| 330 | + |
| 331 | + /** |
| 332 | + * Optimized behavior - to avoid unnecessary IO in the form of shard-stats when not required, we not honor the levels on the |
| 333 | + * individual data nodes instead and pre-compute information as required. |
| 334 | + */ |
| 335 | + public void testNodeIndicesStatsOptimizedResponse() { |
| 336 | + String testLevel = randomFrom("null", "node", "indices", "shards", "unknown"); |
| 337 | + internalCluster().startNode(); |
| 338 | + ensureGreen(); |
| 339 | + String indexName = "test1"; |
| 340 | + index(indexName, "type", "1", "f", "f"); |
| 341 | + refresh(); |
| 342 | + |
| 343 | + NodesStatsResponse response; |
262 | 344 | CommonStatsFlags commonStatsFlags = new CommonStatsFlags();
|
263 | 345 | commonStatsFlags.optimizeNodeIndicesStatsOnLevel(true);
|
264 |
| - response = client().admin().cluster().prepareNodesStats().setIndices(commonStatsFlags).get(); |
265 |
| - response.getNodes().forEach(nodeStats -> { |
266 |
| - assertNull(nodeStats.getIndices().getShardStats(clusterState.metadata().index(indexName).getIndex())); |
267 |
| - assertNull(nodeStats.getIndices().getIndexStats(clusterState.metadata().index(indexName).getIndex())); |
| 346 | + if (!testLevel.equals("null")) { |
| 347 | + ArrayList<String> level_arg = new ArrayList<>(); |
| 348 | + level_arg.add(testLevel); |
268 | 349 |
|
269 |
| - }); |
270 |
| - ArrayList<String> level_arg = new ArrayList<>(); |
271 |
| - level_arg.add("indices"); |
272 |
| - commonStatsFlags.setLevels(level_arg.toArray(new String[0])); |
| 350 | + commonStatsFlags.setLevels(level_arg.toArray(new String[0])); |
| 351 | + } |
273 | 352 | response = client().admin().cluster().prepareNodesStats().setIndices(commonStatsFlags).get();
|
274 |
| - response.getNodes().forEach(nodeStats -> { |
275 |
| - assertNotNull(nodeStats.getIndices().getIndexStats(clusterState.metadata().index(indexName).getIndex())); |
276 |
| - assertNull(nodeStats.getIndices().getShardStats(clusterState.metadata().index(indexName).getIndex())); |
277 |
| - }); |
278 | 353 |
|
279 |
| - level_arg.clear(); |
280 |
| - level_arg.add("shards"); |
281 |
| - commonStatsFlags.setLevels(level_arg.toArray(new String[0])); |
282 |
| - response = client().admin().cluster().prepareNodesStats().setIndices(commonStatsFlags).get(); |
283 | 354 | response.getNodes().forEach(nodeStats -> {
|
284 |
| - assertNotNull(nodeStats.getIndices().getShardStats(clusterState.metadata().index(indexName).getIndex())); |
285 |
| - assertNull(nodeStats.getIndices().getIndexStats(clusterState.metadata().index(indexName).getIndex())); |
| 355 | + try { |
| 356 | + XContentBuilder builder = XContentFactory.jsonBuilder(); |
| 357 | + builder.startObject(); |
| 358 | + builder = nodeStats.getIndices().toXContent(builder, new ToXContent.MapParams(Collections.singletonMap("level", "shards"))); |
| 359 | + builder.endObject(); |
| 360 | + |
| 361 | + Map<String, Object> xContentMap = xContentBuilderToMap(builder); |
| 362 | + LinkedHashMap indicesStatsMap = (LinkedHashMap) xContentMap.get("indices"); |
| 363 | + LinkedHashMap indicesStats = (LinkedHashMap) indicesStatsMap.get("indices"); |
| 364 | + LinkedHashMap shardStats = (LinkedHashMap) indicesStatsMap.get("shards"); |
| 365 | + |
| 366 | + switch (testLevel) { |
| 367 | + case "shards": |
| 368 | + assertFalse(shardStats.isEmpty()); |
| 369 | + assertFalse(indicesStats.isEmpty()); |
| 370 | + break; |
| 371 | + case "indices": |
| 372 | + assertTrue(shardStats.isEmpty()); |
| 373 | + assertFalse(indicesStats.isEmpty()); |
| 374 | + break; |
| 375 | + case "node": |
| 376 | + case "null": |
| 377 | + case "unknown": |
| 378 | + assertTrue(shardStats.isEmpty()); |
| 379 | + assertTrue(indicesStats.isEmpty()); |
| 380 | + break; |
| 381 | + } |
| 382 | + } catch (IOException e) { |
| 383 | + throw new RuntimeException(e); |
| 384 | + } |
286 | 385 | });
|
287 | 386 | }
|
288 | 387 |
|
289 |
| - |
| 388 | + private Map<String, Object> xContentBuilderToMap(XContentBuilder xContentBuilder) { |
| 389 | + return XContentHelper.convertToMap(BytesReference.bytes(xContentBuilder), true, xContentBuilder.contentType()).v2(); |
| 390 | + } |
290 | 391 |
|
291 | 392 | private void assertDocStatusStats() {
|
292 | 393 | DocStatusStats docStatusStats = client().admin()
|
|
0 commit comments