32
32
33
33
package org .opensearch .indices ;
34
34
35
+ import org .opensearch .Version ;
35
36
import org .opensearch .action .admin .indices .stats .CommonStats ;
36
37
import org .opensearch .action .admin .indices .stats .IndexShardStats ;
37
38
import org .opensearch .action .admin .indices .stats .ShardStats ;
63
64
64
65
import java .io .IOException ;
65
66
import java .util .ArrayList ;
67
+ import java .util .Arrays ;
66
68
import java .util .HashMap ;
67
69
import java .util .List ;
68
70
import java .util .Map ;
71
+ import java .util .Optional ;
69
72
70
73
/**
71
74
* Global information on indices stats running on a specific node.
74
77
*/
75
78
@ PublicApi (since = "1.0.0" )
76
79
public class NodeIndicesStats implements Writeable , ToXContentFragment {
77
- private CommonStats stats ;
78
- private Map <Index , List <IndexShardStats >> statsByShard ;
80
+ protected CommonStats stats ;
81
+ protected Map <Index , CommonStats > statsByIndex ;
82
+ protected Map <Index , List <IndexShardStats >> statsByShard ;
79
83
80
84
public NodeIndicesStats (StreamInput in ) throws IOException {
81
85
stats = new CommonStats (in );
82
- if (in .readBoolean ()) {
83
- int entries = in .readVInt ();
84
- statsByShard = new HashMap <>();
85
- for (int i = 0 ; i < entries ; i ++) {
86
- Index index = new Index (in );
87
- int indexShardListSize = in .readVInt ();
88
- List <IndexShardStats > indexShardStats = new ArrayList <>(indexShardListSize );
89
- for (int j = 0 ; j < indexShardListSize ; j ++) {
90
- indexShardStats .add (new IndexShardStats (in ));
91
- }
92
- statsByShard .put (index , indexShardStats );
86
+ if (in .getVersion ().onOrAfter (Version .V_3_0_0 )) {
87
+ // contains statsByIndex
88
+ if (in .readBoolean ()) {
89
+ statsByIndex = readStatsByIndex (in );
93
90
}
94
91
}
92
+ if (in .readBoolean ()) {
93
+ statsByShard = readStatsByShard (in );
94
+ }
95
95
}
96
96
97
+ /**
98
+ * Without passing the information of the levels to the constructor, we return the Node-level aggregated stats as
99
+ * {@link CommonStats} along with a hash-map containing Index to List of Shard Stats.
100
+ */
97
101
public NodeIndicesStats (CommonStats oldStats , Map <Index , List <IndexShardStats >> statsByShard , SearchRequestStats searchRequestStats ) {
98
102
// this.stats = stats;
99
103
this .statsByShard = statsByShard ;
@@ -112,6 +116,90 @@ public NodeIndicesStats(CommonStats oldStats, Map<Index, List<IndexShardStats>>
112
116
}
113
117
}
114
118
119
+ /**
120
+ * Passing the level information to the nodes allows us to aggregate the stats based on the level passed. This
121
+ * allows us to aggregate based on NodeLevel (default - if no level is passed) or Index level if `indices` level is
122
+ * passed and finally return the statsByShards map if `shards` level is passed. This allows us to reduce ser/de of
123
+ * stats and return only the information that is required while returning to the client.
124
+ */
125
+ public NodeIndicesStats (
126
+ CommonStats oldStats ,
127
+ Map <Index , List <IndexShardStats >> statsByShard ,
128
+ SearchRequestStats searchRequestStats ,
129
+ StatsLevel level
130
+ ) {
131
+ // make a total common stats from old ones and current ones
132
+ this .stats = oldStats ;
133
+ for (List <IndexShardStats > shardStatsList : statsByShard .values ()) {
134
+ for (IndexShardStats indexShardStats : shardStatsList ) {
135
+ for (ShardStats shardStats : indexShardStats .getShards ()) {
136
+ stats .add (shardStats .getStats ());
137
+ }
138
+ }
139
+ }
140
+
141
+ if (this .stats .search != null ) {
142
+ this .stats .search .setSearchRequestStats (searchRequestStats );
143
+ }
144
+
145
+ if (level != null ) {
146
+ switch (level ) {
147
+ case INDICES :
148
+ this .statsByIndex = createStatsByIndex (statsByShard );
149
+ break ;
150
+ case SHARDS :
151
+ this .statsByShard = statsByShard ;
152
+ break ;
153
+ }
154
+ }
155
+ }
156
+
157
+ /**
158
+ * By default, the levels passed from the transport action will be a list of strings, since NodeIndicesStats can
159
+ * only aggregate on one level, we pick the first accepted level else we ignore if no known level is passed. Level is
160
+ * selected based on enum defined in {@link StatsLevel}
161
+ *
162
+ * Note - we are picking the first level as multiple levels are not supported in the previous versions.
163
+ * @param levels - levels sent in the request.
164
+ *
165
+ * @return Corresponding identified enum {@link StatsLevel}
166
+ */
167
+ public static StatsLevel getAcceptedLevel (String [] levels ) {
168
+ if (levels != null && levels .length > 0 ) {
169
+ Optional <StatsLevel > level = Arrays .stream (StatsLevel .values ())
170
+ .filter (field -> field .getRestName ().equals (levels [0 ]))
171
+ .findFirst ();
172
+ return level .orElseThrow (() -> new IllegalArgumentException ("Level provided is not supported by NodeIndicesStats" ));
173
+ }
174
+ return null ;
175
+ }
176
+
177
+ private Map <Index , CommonStats > readStatsByIndex (StreamInput in ) throws IOException {
178
+ Map <Index , CommonStats > statsByIndex = new HashMap <>();
179
+ int indexEntries = in .readVInt ();
180
+ for (int i = 0 ; i < indexEntries ; i ++) {
181
+ Index index = new Index (in );
182
+ CommonStats commonStats = new CommonStats (in );
183
+ statsByIndex .put (index , commonStats );
184
+ }
185
+ return statsByIndex ;
186
+ }
187
+
188
+ private Map <Index , List <IndexShardStats >> readStatsByShard (StreamInput in ) throws IOException {
189
+ Map <Index , List <IndexShardStats >> statsByShard = new HashMap <>();
190
+ int entries = in .readVInt ();
191
+ for (int i = 0 ; i < entries ; i ++) {
192
+ Index index = new Index (in );
193
+ int indexShardListSize = in .readVInt ();
194
+ List <IndexShardStats > indexShardStats = new ArrayList <>(indexShardListSize );
195
+ for (int j = 0 ; j < indexShardListSize ; j ++) {
196
+ indexShardStats .add (new IndexShardStats (in ));
197
+ }
198
+ statsByShard .put (index , indexShardStats );
199
+ }
200
+ return statsByShard ;
201
+ }
202
+
115
203
@ Nullable
116
204
public StoreStats getStore () {
117
205
return stats .getStore ();
@@ -195,7 +283,31 @@ public RecoveryStats getRecoveryStats() {
195
283
@ Override
196
284
public void writeTo (StreamOutput out ) throws IOException {
197
285
stats .writeTo (out );
286
+
287
+ if (out .getVersion ().onOrAfter (Version .V_3_0_0 )) {
288
+ out .writeBoolean (statsByIndex != null );
289
+ if (statsByIndex != null ) {
290
+ writeStatsByIndex (out );
291
+ }
292
+ }
293
+
198
294
out .writeBoolean (statsByShard != null );
295
+ if (statsByShard != null ) {
296
+ writeStatsByShards (out );
297
+ }
298
+ }
299
+
300
+ private void writeStatsByIndex (StreamOutput out ) throws IOException {
301
+ if (statsByIndex != null ) {
302
+ out .writeVInt (statsByIndex .size ());
303
+ for (Map .Entry <Index , CommonStats > entry : statsByIndex .entrySet ()) {
304
+ entry .getKey ().writeTo (out );
305
+ entry .getValue ().writeTo (out );
306
+ }
307
+ }
308
+ }
309
+
310
+ private void writeStatsByShards (StreamOutput out ) throws IOException {
199
311
if (statsByShard != null ) {
200
312
out .writeVInt (statsByShard .size ());
201
313
for (Map .Entry <Index , List <IndexShardStats >> entry : statsByShard .entrySet ()) {
@@ -210,29 +322,46 @@ public void writeTo(StreamOutput out) throws IOException {
210
322
211
323
@ Override
212
324
public XContentBuilder toXContent (XContentBuilder builder , Params params ) throws IOException {
213
- final String level = params .param ("level" , "node" );
214
- final boolean isLevelValid = "indices" .equalsIgnoreCase (level )
215
- || "node" .equalsIgnoreCase (level )
216
- || "shards" .equalsIgnoreCase (level );
325
+ final String level = params .param ("level" , StatsLevel . NODE . getRestName () );
326
+ final boolean isLevelValid = StatsLevel . NODE . getRestName () .equalsIgnoreCase (level )
327
+ || StatsLevel . INDICES . getRestName () .equalsIgnoreCase (level )
328
+ || StatsLevel . SHARDS . getRestName () .equalsIgnoreCase (level );
217
329
if (!isLevelValid ) {
218
- throw new IllegalArgumentException ("level parameter must be one of [indices] or [node] or [shards] but was [" + level + "]" );
330
+ throw new IllegalArgumentException (
331
+ "level parameter must be one of ["
332
+ + StatsLevel .INDICES .getRestName ()
333
+ + "] or ["
334
+ + StatsLevel .NODE .getRestName ()
335
+ + "] or ["
336
+ + StatsLevel .SHARDS .getRestName ()
337
+ + "] but was ["
338
+ + level
339
+ + "]"
340
+ );
219
341
}
220
342
221
343
// "node" level
222
- builder .startObject (Fields .INDICES );
344
+ builder .startObject (StatsLevel .INDICES . getRestName () );
223
345
stats .toXContent (builder , params );
224
346
225
- if ("indices" .equals (level )) {
226
- Map <Index , CommonStats > indexStats = createStatsByIndex ();
227
- builder .startObject (Fields .INDICES );
228
- for (Map .Entry <Index , CommonStats > entry : indexStats .entrySet ()) {
347
+ if (StatsLevel .INDICES .getRestName ().equals (level )) {
348
+ assert statsByIndex != null || statsByShard != null : "Expected shard stats or index stats in response for generating ["
349
+ + StatsLevel .INDICES
350
+ + "] field" ;
351
+ if (statsByIndex == null ) {
352
+ statsByIndex = createStatsByIndex (statsByShard );
353
+ }
354
+
355
+ builder .startObject (StatsLevel .INDICES .getRestName ());
356
+ for (Map .Entry <Index , CommonStats > entry : statsByIndex .entrySet ()) {
229
357
builder .startObject (entry .getKey ().getName ());
230
358
entry .getValue ().toXContent (builder , params );
231
359
builder .endObject ();
232
360
}
233
361
builder .endObject ();
234
- } else if ("shards" .equals (level )) {
235
- builder .startObject ("shards" );
362
+ } else if (StatsLevel .SHARDS .getRestName ().equals (level )) {
363
+ builder .startObject (StatsLevel .SHARDS .getRestName ());
364
+ assert statsByShard != null : "Expected shard stats in response for generating [" + StatsLevel .SHARDS + "] field" ;
236
365
for (Map .Entry <Index , List <IndexShardStats >> entry : statsByShard .entrySet ()) {
237
366
builder .startArray (entry .getKey ().getName ());
238
367
for (IndexShardStats indexShardStats : entry .getValue ()) {
@@ -251,7 +380,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
251
380
return builder ;
252
381
}
253
382
254
- private Map <Index , CommonStats > createStatsByIndex () {
383
+ private Map <Index , CommonStats > createStatsByIndex (Map < Index , List < IndexShardStats >> statsByShard ) {
255
384
Map <Index , CommonStats > statsMap = new HashMap <>();
256
385
for (Map .Entry <Index , List <IndexShardStats >> entry : statsByShard .entrySet ()) {
257
386
if (!statsMap .containsKey (entry .getKey ())) {
@@ -281,7 +410,21 @@ public List<IndexShardStats> getShardStats(Index index) {
281
410
*
282
411
* @opensearch.internal
283
412
*/
284
- static final class Fields {
285
- static final String INDICES = "indices" ;
413
+ @ PublicApi (since = "3.0.0" )
414
+ public enum StatsLevel {
415
+ INDICES ("indices" ),
416
+ SHARDS ("shards" ),
417
+ NODE ("node" );
418
+
419
+ private final String restName ;
420
+
421
+ StatsLevel (String restName ) {
422
+ this .restName = restName ;
423
+ }
424
+
425
+ public String getRestName () {
426
+ return restName ;
427
+ }
428
+
286
429
}
287
430
}
0 commit comments