Skip to content

Commit 1fe3b77

Browse files
PeteGillinElasticnielsbaumanelasticsearchmachine
authored
ES-10063 Add multi-project support for more stats APIs (#127650)
* Add multi-project support for more stats APIs This affects the following APIs: - `GET _nodes/stats`: - For `indices`, it now prefixes the index name with the project ID (for non-default projects). Previously, it didn't tell you which project an index was in, and it failed if two projects had the same index name. - For `ingest`, it now gets the pipeline and processor stats for all projects, and prefixes the pipeline ID with the project ID. Previously, it only got them for the default project. - `GET /_cluster/stats`: - For `ingest`, it now aggregates the pipeline and processor stats for all projects. Previously, it only got them for the default project. - `GET /_info`: - For `ingest`, same as for `GET /_nodes/stats`. This is done by making `IndicesService.stats()` and `IngestService.stats()` include project IDs in the `NodeIndicesStats` and `IngestStats` objects they return, and making those stats objects incorporate the project IDs when converting to XContent. The transitive callers of these two methods are rather extensive (including all callers to `NodeService.stats()`, all callers of `TransportNodesStatsAction`, and so on). To ensure the change is safe, the callers were all checked out, and they fall into the following cases: - The behaviour change is one of the desired enhancements described above. - There is no behaviour change because it was getting node stats but neither `indices` nor `ingest` stats were requested. - There is no behaviour change because it was getting `indices` and/or `ingest` stats but only using aggregate values. - In `MachineLearningUsageTransportAction` and `TransportGetTrainedModelsStatsAction`, the `IngestStats` returned will return stats from all projects instead of just the default with this change, but they have been changed to filter the non-default project stats out, so this change is a noop there. (These actions are not MP-ready yet.) - `MonitoringService` will be affected, but this is the legacy monitoring module which is not in use anywhere that MP is going to be enabled. (If anything, the behaviour is probably improved by this change, as it will now include project IDs, rather than producing ambiguous unqualified results and failing in the case of duplicates.) * Update test/external-modules/multi-project/build.gradle Change suggested by Niels. Co-authored-by: Niels Bauman <[email protected]> * Respond to review comments * fix merge weirdness * [CI] Auto commit changes from spotless * Fix test compilation following upstream change to base class * Update x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/datatiers/DataTierUsageFixtures.java Co-authored-by: Niels Bauman <[email protected]> * Make projects-by-index map nullable and omit in single-project; always include project prefix in XContent in multip-project, even if default; also incorporate one other review comment * Add a TODO * update IT to reflect changed behaviour * Switch to using XContent.Params to indicate whether it is multi-project or not * Refactor NodesStatsMultiProjectIT to common up repeated assertions * Defer use of ProjectIdResolver in REST handlers to keep tests happy * Include index UUID in "unknown project" case * Make the index-to-project map empty rather than null in the BWC deserialization case. This works out fine, for the reasons given in the comment. As it happens, I'd already forgotten to do the null check in the one place it's actively used. * remove a TODO that is done, and add a comment * fix typo * Get REST YAML tests working with project ID prefix TODO finish this * As a drive-by, fix and un-suppress one of the health REST tests * [CI] Auto commit changes from spotless * TODO ugh * Experiment with different stashing behaviour * [CI] Auto commit changes from spotless * Try a more sensible stash behaviour for assertions * clarify comment * Make checkstyle happy * Make the way `Assertion` works more consistent, and simplify implementation * [CI] Auto commit changes from spotless * In RestNodesStatsAction, make the XContent params to channel.request(), which is the value it would have had before this change --------- Co-authored-by: Niels Bauman <[email protected]> Co-authored-by: elasticsearchmachine <[email protected]>
1 parent e230c01 commit 1fe3b77

File tree

41 files changed

+1065
-356
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1065
-356
lines changed

modules/ingest-common/src/internalClusterTest/java/org/elasticsearch/ingest/common/IngestRestartIT.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.elasticsearch.action.support.WriteRequest;
2121
import org.elasticsearch.client.internal.Requests;
2222
import org.elasticsearch.cluster.block.ClusterBlockException;
23+
import org.elasticsearch.cluster.metadata.ProjectId;
2324
import org.elasticsearch.cluster.node.DiscoveryNodeRole;
2425
import org.elasticsearch.common.settings.Settings;
2526
import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException;
@@ -112,7 +113,12 @@ public void testFailureInConditionalProcessor() {
112113
NodesStatsResponse r = clusterAdmin().prepareNodesStats(internalCluster().getNodeNames()).setIngest(true).get();
113114
int nodeCount = r.getNodes().size();
114115
for (int k = 0; k < nodeCount; k++) {
115-
List<IngestStats.ProcessorStat> stats = r.getNodes().get(k).getIngestStats().processorStats().get(pipelineId);
116+
List<IngestStats.ProcessorStat> stats = r.getNodes()
117+
.get(k)
118+
.getIngestStats()
119+
.processorStats()
120+
.get(ProjectId.DEFAULT)
121+
.get(pipelineId);
116122
for (IngestStats.ProcessorStat st : stats) {
117123
assertThat(st.stats().ingestCurrent(), greaterThanOrEqualTo(0L));
118124
}

modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/15_info_ingest.yml

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -85,22 +85,22 @@ teardown:
8585
- gte: { ingest.total.failed: 0 }
8686

8787
# Pipelines section
88-
- is_true: ingest.pipelines.ingest_info_pipeline
89-
- gte: { ingest.pipelines.ingest_info_pipeline.count: 2 }
90-
- gte: { ingest.pipelines.ingest_info_pipeline.time_in_millis: 0 }
91-
- match: { ingest.pipelines.ingest_info_pipeline.current: 0 }
92-
- match: { ingest.pipelines.ingest_info_pipeline.failed: 0 }
93-
- gt: { ingest.pipelines.ingest_info_pipeline.ingested_as_first_pipeline_in_bytes: 0 }
94-
- gt: { ingest.pipelines.ingest_info_pipeline.produced_as_first_pipeline_in_bytes: 0 }
88+
- is_true: "ingest.pipelines.${_project_id_prefix_}ingest_info_pipeline"
89+
- gte: { "ingest.pipelines.${_project_id_prefix_}ingest_info_pipeline.count": 2 }
90+
- gte: { "ingest.pipelines.${_project_id_prefix_}ingest_info_pipeline.time_in_millis": 0 }
91+
- match: { "ingest.pipelines.${_project_id_prefix_}ingest_info_pipeline.current": 0 }
92+
- match: { "ingest.pipelines.${_project_id_prefix_}ingest_info_pipeline.failed": 0 }
93+
- gt: { "ingest.pipelines.${_project_id_prefix_}ingest_info_pipeline.ingested_as_first_pipeline_in_bytes": 0 }
94+
- gt: { "ingest.pipelines.${_project_id_prefix_}ingest_info_pipeline.produced_as_first_pipeline_in_bytes": 0 }
9595

9696
# Processors section
97-
- is_true: ingest.pipelines.ingest_info_pipeline.processors.0.set
98-
- match: { ingest.pipelines.ingest_info_pipeline.processors.0.set.type: "set" }
99-
- is_true: ingest.pipelines.ingest_info_pipeline.processors.0.set.stats
100-
- gte: { ingest.pipelines.ingest_info_pipeline.processors.0.set.stats.count: 2 }
101-
- gte: { ingest.pipelines.ingest_info_pipeline.processors.0.set.stats.time_in_millis: 0 }
102-
- match: { ingest.pipelines.ingest_info_pipeline.processors.0.set.stats.current: 0 }
103-
- match: { ingest.pipelines.ingest_info_pipeline.processors.0.set.stats.failed: 0 }
97+
- is_true: "ingest.pipelines.${_project_id_prefix_}ingest_info_pipeline.processors.0.set"
98+
- match: { "ingest.pipelines.${_project_id_prefix_}ingest_info_pipeline.processors.0.set.type": "set" }
99+
- is_true: "ingest.pipelines.${_project_id_prefix_}ingest_info_pipeline.processors.0.set.stats"
100+
- gte: { "ingest.pipelines.${_project_id_prefix_}ingest_info_pipeline.processors.0.set.stats.count": 2 }
101+
- gte: { "ingest.pipelines.${_project_id_prefix_}ingest_info_pipeline.processors.0.set.stats.time_in_millis": 0 }
102+
- match: { "ingest.pipelines.${_project_id_prefix_}ingest_info_pipeline.processors.0.set.stats.current": 0 }
103+
- match: { "ingest.pipelines.${_project_id_prefix_}ingest_info_pipeline.processors.0.set.stats.failed": 0 }
104104

105105
---
106106
"Test bytes_produced not increased when pipeline fails":
@@ -128,9 +128,9 @@ teardown:
128128
- do:
129129
cluster.info:
130130
target: [ ingest ]
131-
- match: { ingest.pipelines.pipeline-1.failed: 1 }
132-
- gt: { ingest.pipelines.pipeline-1.ingested_as_first_pipeline_in_bytes: 0 }
133-
- match: { ingest.pipelines.pipeline-1.produced_as_first_pipeline_in_bytes: 0 }
131+
- match: { "ingest.pipelines.${_project_id_prefix_}pipeline-1.failed": 1 }
132+
- gt: { "ingest.pipelines.${_project_id_prefix_}pipeline-1.ingested_as_first_pipeline_in_bytes": 0 }
133+
- match: { "ingest.pipelines.${_project_id_prefix_}pipeline-1.produced_as_first_pipeline_in_bytes": 0 }
134134

135135
---
136136
"Test drop processor":
@@ -156,8 +156,8 @@ teardown:
156156
- do:
157157
cluster.info:
158158
target: [ ingest ]
159-
- gt: { ingest.pipelines.pipeline-1.ingested_as_first_pipeline_in_bytes: 0 }
160-
- match: { ingest.pipelines.pipeline-1.produced_as_first_pipeline_in_bytes: 0 }
159+
- gt: { "ingest.pipelines.${_project_id_prefix_}pipeline-1.ingested_as_first_pipeline_in_bytes": 0 }
160+
- match: { "ingest.pipelines.${_project_id_prefix_}pipeline-1.produced_as_first_pipeline_in_bytes": 0 }
161161

162162
---
163163
"Test that pipeline processor has byte stats recorded in first pipeline":
@@ -210,11 +210,11 @@ teardown:
210210
- do:
211211
cluster.info:
212212
target: [ ingest ]
213-
- gt: { ingest.pipelines.pipeline-1.ingested_as_first_pipeline_in_bytes: 0 }
214-
- set: { ingest.pipelines.pipeline-1.ingested_as_first_pipeline_in_bytes: ingest_bytes }
215-
- gt: { ingest.pipelines.pipeline-1.produced_as_first_pipeline_in_bytes: $ingest_bytes }
216-
- match: { ingest.pipelines.pipeline-2.ingested_as_first_pipeline_in_bytes: 0 }
217-
- match: { ingest.pipelines.pipeline-2.produced_as_first_pipeline_in_bytes: 0 }
213+
- gt: { "ingest.pipelines.${_project_id_prefix_}pipeline-1.ingested_as_first_pipeline_in_bytes": 0 }
214+
- set: { "ingest.pipelines.${_project_id_prefix_}pipeline-1.ingested_as_first_pipeline_in_bytes": ingest_bytes }
215+
- gt: { "ingest.pipelines.${_project_id_prefix_}pipeline-1.produced_as_first_pipeline_in_bytes": $ingest_bytes }
216+
- match: { "ingest.pipelines.${_project_id_prefix_}pipeline-2.ingested_as_first_pipeline_in_bytes": 0 }
217+
- match: { "ingest.pipelines.${_project_id_prefix_}pipeline-2.produced_as_first_pipeline_in_bytes": 0 }
218218

219219
---
220220
"Test that final pipeline has byte stats recorded in first pipeline":
@@ -262,11 +262,11 @@ teardown:
262262
- do:
263263
cluster.info:
264264
target: [ ingest ]
265-
- gt: { ingest.pipelines.pipeline-1.ingested_as_first_pipeline_in_bytes: 0 }
266-
- set: { ingest.pipelines.pipeline-1.ingested_as_first_pipeline_in_bytes: ingest_bytes }
267-
- gt: { ingest.pipelines.pipeline-1.produced_as_first_pipeline_in_bytes: $ingest_bytes }
268-
- match: { ingest.pipelines.pipeline-2.ingested_as_first_pipeline_in_bytes: 0 }
269-
- match: { ingest.pipelines.pipeline-2.produced_as_first_pipeline_in_bytes: 0 }
265+
- gt: { "ingest.pipelines.${_project_id_prefix_}pipeline-1.ingested_as_first_pipeline_in_bytes": 0 }
266+
- set: { "ingest.pipelines.${_project_id_prefix_}pipeline-1.ingested_as_first_pipeline_in_bytes": ingest_bytes }
267+
- gt: { "ingest.pipelines.${_project_id_prefix_}pipeline-1.produced_as_first_pipeline_in_bytes": $ingest_bytes }
268+
- match: { "ingest.pipelines.${_project_id_prefix_}pipeline-2.ingested_as_first_pipeline_in_bytes": 0 }
269+
- match: { "ingest.pipelines.${_project_id_prefix_}pipeline-2.produced_as_first_pipeline_in_bytes": 0 }
270270

271271
---
272272
"Test that reroute processor has byte stats recorded in first pipeline":
@@ -327,11 +327,11 @@ teardown:
327327
- do:
328328
cluster.info:
329329
target: [ ingest ]
330-
- gt: { ingest.pipelines.pipeline-1.ingested_as_first_pipeline_in_bytes: 0 }
331-
- set: { ingest.pipelines.pipeline-1.ingested_as_first_pipeline_in_bytes: ingest_bytes }
332-
- gt: { ingest.pipelines.pipeline-1.produced_as_first_pipeline_in_bytes: $ingest_bytes }
333-
- match: { ingest.pipelines.pipeline-2.ingested_as_first_pipeline_in_bytes: 0 }
334-
- match: { ingest.pipelines.pipeline-2.produced_as_first_pipeline_in_bytes: 0 }
330+
- gt: { "ingest.pipelines.${_project_id_prefix_}pipeline-1.ingested_as_first_pipeline_in_bytes": 0 }
331+
- set: { "ingest.pipelines.${_project_id_prefix_}pipeline-1.ingested_as_first_pipeline_in_bytes": ingest_bytes }
332+
- gt: { "ingest.pipelines.${_project_id_prefix_}pipeline-1.produced_as_first_pipeline_in_bytes": $ingest_bytes }
333+
- match: { "ingest.pipelines.${_project_id_prefix_}pipeline-2.ingested_as_first_pipeline_in_bytes": 0 }
334+
- match: { "ingest.pipelines.${_project_id_prefix_}pipeline-2.produced_as_first_pipeline_in_bytes": 0 }
335335

336336
---
337337
"Test human readable byte stat fields":
@@ -360,8 +360,8 @@ teardown:
360360
target: [ ingest ]
361361
human: true
362362

363-
- match: { ingest.pipelines.pipeline-1.count: 1 }
364-
- gt: { ingest.pipelines.pipeline-1.ingested_as_first_pipeline_in_bytes: 0 }
365-
- gt: { ingest.pipelines.pipeline-1.produced_as_first_pipeline_in_bytes: 0 }
366-
- is_true: ingest.pipelines.pipeline-1.ingested_as_first_pipeline
367-
- is_true: ingest.pipelines.pipeline-1.produced_as_first_pipeline
363+
- match: { "ingest.pipelines.${_project_id_prefix_}pipeline-1.count": 1 }
364+
- gt: { "ingest.pipelines.${_project_id_prefix_}pipeline-1.ingested_as_first_pipeline_in_bytes": 0 }
365+
- gt: { "ingest.pipelines.${_project_id_prefix_}pipeline-1.produced_as_first_pipeline_in_bytes": 0 }
366+
- is_true: "ingest.pipelines.${_project_id_prefix_}pipeline-1.ingested_as_first_pipeline"
367+
- is_true: "ingest.pipelines.${_project_id_prefix_}pipeline-1.produced_as_first_pipeline"

modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/70_bulk.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,10 @@ teardown:
8888
- gte: {nodes.$master.ingest.total.failed: 0}
8989
- gte: {nodes.$master.ingest.total.time_in_millis: 0}
9090
- match: {nodes.$master.ingest.total.current: 0}
91-
- gte: {nodes.$master.ingest.pipelines.pipeline1.count: 0}
92-
- match: {nodes.$master.ingest.pipelines.pipeline1.failed: 0}
93-
- gte: {nodes.$master.ingest.pipelines.pipeline1.time_in_millis: 0}
94-
- match: {nodes.$master.ingest.pipelines.pipeline1.current: 0}
91+
- gte: { "nodes.$master.ingest.pipelines.${_project_id_prefix_}pipeline1.count": 0}
92+
- match: { "nodes.$master.ingest.pipelines.${_project_id_prefix_}pipeline1.failed": 0}
93+
- gte: { "nodes.$master.ingest.pipelines.${_project_id_prefix_}pipeline1.time_in_millis": 0}
94+
- match: { "nodes.$master.ingest.pipelines.${_project_id_prefix_}pipeline1.current": 0}
9595

9696
---
9797
"Test bulk request with default pipeline":
@@ -124,10 +124,10 @@ teardown:
124124
- gte: {nodes.$master.ingest.total.failed: 0}
125125
- gte: {nodes.$master.ingest.total.time_in_millis: 0}
126126
- match: {nodes.$master.ingest.total.current: 0}
127-
- gte: {nodes.$master.ingest.pipelines.pipeline2.count: 0}
128-
- match: {nodes.$master.ingest.pipelines.pipeline2.failed: 0}
129-
- gte: {nodes.$master.ingest.pipelines.pipeline2.time_in_millis: 0}
130-
- match: {nodes.$master.ingest.pipelines.pipeline2.current: 0}
127+
- gte: { "nodes.$master.ingest.pipelines.${_project_id_prefix_}pipeline2.count": 0}
128+
- match: { "nodes.$master.ingest.pipelines.${_project_id_prefix_}pipeline2.failed": 0}
129+
- gte: { "nodes.$master.ingest.pipelines.${_project_id_prefix_}pipeline2.time_in_millis": 0}
130+
- match: { "nodes.$master.ingest.pipelines.${_project_id_prefix_}pipeline2.current": 0}
131131

132132
- do:
133133
get:

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/health/40_diagnosis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@
2626
- length: { indicators.shards_availability.diagnosis: 1 }
2727
- is_true: indicators.shards_availability.diagnosis.0.affected_resources
2828
- length: { indicators.shards_availability.diagnosis.0.affected_resources: 1 }
29-
- match: { indicators.shards_availability.diagnosis.0.affected_resources.indices.0: "red_index" }
29+
- match: { indicators.shards_availability.diagnosis.0.affected_resources.indices.0: "${_project_id_prefix_}red_index" }

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/nodes.stats/11_indices_metrics.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -503,9 +503,9 @@
503503
- gte: { nodes.$node_id.indices.mappings.total_count: 28 }
504504
- is_true: nodes.$node_id.indices.mappings.total_estimated_overhead
505505
- gte: { nodes.$node_id.indices.mappings.total_estimated_overhead_in_bytes: 26624 }
506-
- match: { nodes.$node_id.indices.indices.index1.mappings.total_count: 28 }
507-
- is_true: nodes.$node_id.indices.indices.index1.mappings.total_estimated_overhead
508-
- match: { nodes.$node_id.indices.indices.index1.mappings.total_estimated_overhead_in_bytes: 28672 }
506+
- match: { "nodes.$node_id.indices.indices.${_project_id_prefix_}index1.mappings.total_count": 28 }
507+
- is_true: "nodes.$node_id.indices.indices.${_project_id_prefix_}index1.mappings.total_estimated_overhead"
508+
- match: { "nodes.$node_id.indices.indices.${_project_id_prefix_}index1.mappings.total_estimated_overhead_in_bytes": 28672 }
509509

510510
---
511511
"Lucene segment level fields stats":

server/src/internalClusterTest/java/org/elasticsearch/ingest/IngestStatsNamesAndTypesIT.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsResponse;
1616
import org.elasticsearch.action.bulk.BulkRequest;
1717
import org.elasticsearch.action.index.IndexRequest;
18+
import org.elasticsearch.cluster.metadata.ProjectId;
1819
import org.elasticsearch.common.Strings;
1920
import org.elasticsearch.plugins.Plugin;
2021
import org.elasticsearch.script.MockScriptEngine;
@@ -109,7 +110,10 @@ public void testIngestStatsNamesAndTypes() throws IOException {
109110
assertThat(pipelineStat.pipelineId(), equalTo("pipeline1"));
110111
assertThat(pipelineStat.stats().ingestCount(), equalTo(1L));
111112

112-
List<IngestStats.ProcessorStat> processorStats = stats.getIngestStats().processorStats().get("pipeline1");
113+
List<IngestStats.ProcessorStat> processorStats = stats.getIngestStats()
114+
.processorStats()
115+
.get(ProjectId.DEFAULT)
116+
.get("pipeline1");
113117
assertThat(processorStats.size(), equalTo(4));
114118

115119
IngestStats.ProcessorStat setA = processorStats.get(0);

server/src/main/java/org/elasticsearch/TransportVersions.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ static TransportVersion def(int id) {
260260
public static final TransportVersion ESQL_TIME_SERIES_SOURCE_STATUS = def(9_076_0_00);
261261
public static final TransportVersion ESQL_HASH_OPERATOR_STATUS_OUTPUT_TIME = def(9_077_0_00);
262262
public static final TransportVersion ML_INFERENCE_HUGGING_FACE_CHAT_COMPLETION_ADDED = def(9_078_0_00);
263+
public static final TransportVersion NODES_STATS_SUPPORTS_MULTI_PROJECT = def(9_079_0_00);
263264

264265
/*
265266
* STOP! READ THIS FIRST! No, really,

server/src/main/java/org/elasticsearch/action/ActionModule.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,7 @@ public void initRestHandlers(Supplier<DiscoveryNodes> nodesInCluster, Predicate<
846846
registerHandler.accept(new RestNodesInfoAction(settingsFilter));
847847
registerHandler.accept(new RestRemoteClusterInfoAction());
848848
registerHandler.accept(new RestNodesCapabilitiesAction());
849-
registerHandler.accept(new RestNodesStatsAction());
849+
registerHandler.accept(new RestNodesStatsAction(projectIdResolver));
850850
registerHandler.accept(new RestNodesUsageAction());
851851
registerHandler.accept(new RestNodesHotThreadsAction());
852852
registerHandler.accept(new RestClusterAllocationExplainAction());
@@ -981,7 +981,7 @@ public void initRestHandlers(Supplier<DiscoveryNodes> nodesInCluster, Predicate<
981981
registerHandler.accept(new RestShardsAction());
982982
registerHandler.accept(new RestMasterAction());
983983
registerHandler.accept(new RestNodesAction());
984-
registerHandler.accept(new RestClusterInfoAction());
984+
registerHandler.accept(new RestClusterInfoAction(projectIdResolver));
985985
registerHandler.accept(new RestTasksAction(nodesInCluster));
986986
registerHandler.accept(new RestIndicesAction(projectIdResolver));
987987
registerHandler.accept(new RestSegmentsAction());

server/src/main/java/org/elasticsearch/action/admin/cluster/node/stats/NodeStats.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
*/
5353
public class NodeStats extends BaseNodeResponse implements ChunkedToXContent {
5454

55+
public static final String MULTI_PROJECT_ENABLED_XCONTENT_PARAM_KEY = "multi_project_enabled_node_stats";
56+
5557
private final long timestamp;
5658

5759
@Nullable

0 commit comments

Comments
 (0)