Skip to content

Commit c0553d4

Browse files
Remove ChunkedToXContentBuilder (#119310)
Reverts the introduction of the ChunkedToXContentBuilder to fix the various performance regressions it introduced and the theoretical impossibility of fixing its performance to rival that of the iterator based solution. With the exception of a few minor adjustments that came out of changes already made on top of the builder migration this simply returns to the previous implementations (and some of the stuff in that code could be done better with the utilities available now). I also verified that this solves the performance issues that we've been running into with the builder. closes #118647 This reverts commit 918a9cc This reverts commit 8c37875 This reverts commit 11c2eb2 This reverts commit c311515
1 parent ec35dc2 commit c0553d4

File tree

64 files changed

+787
-1100
lines changed

Some content is hidden

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

64 files changed

+787
-1100
lines changed

docs/changelog/119310.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 119310
2+
summary: Remove ChunkedToXContentBuilder
3+
area: "Network"
4+
type: bug
5+
issues:
6+
- 118647

modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/IngestGeoIpMetadata.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
import org.elasticsearch.cluster.DiffableUtils;
1616
import org.elasticsearch.cluster.NamedDiff;
1717
import org.elasticsearch.cluster.metadata.Metadata;
18+
import org.elasticsearch.common.collect.Iterators;
1819
import org.elasticsearch.common.io.stream.StreamInput;
1920
import org.elasticsearch.common.io.stream.StreamOutput;
20-
import org.elasticsearch.common.xcontent.ChunkedToXContent;
21+
import org.elasticsearch.common.xcontent.ChunkedToXContentHelper;
2122
import org.elasticsearch.ingest.geoip.direct.DatabaseConfigurationMetadata;
2223
import org.elasticsearch.xcontent.ConstructingObjectParser;
2324
import org.elasticsearch.xcontent.ParseField;
@@ -90,8 +91,8 @@ public static IngestGeoIpMetadata fromXContent(XContentParser parser) throws IOE
9091
}
9192

9293
@Override
93-
public Iterator<? extends ToXContent> toXContentChunked(ToXContent.Params params) {
94-
return ChunkedToXContent.builder(params).xContentObjectFields(DATABASES_FIELD.getPreferredName(), databases);
94+
public Iterator<? extends ToXContent> toXContentChunked(ToXContent.Params ignored) {
95+
return Iterators.concat(ChunkedToXContentHelper.xContentValuesMap(DATABASES_FIELD.getPreferredName(), databases));
9596
}
9697

9798
@Override

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

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
import org.elasticsearch.cluster.node.DiscoveryNodeRole;
1616
import org.elasticsearch.cluster.routing.allocation.DiskThresholdSettings;
1717
import org.elasticsearch.cluster.routing.allocation.NodeAllocationStats;
18+
import org.elasticsearch.common.collect.Iterators;
1819
import org.elasticsearch.common.io.stream.StreamInput;
1920
import org.elasticsearch.common.io.stream.StreamOutput;
2021
import org.elasticsearch.common.xcontent.ChunkedToXContent;
22+
import org.elasticsearch.common.xcontent.ChunkedToXContentHelper;
2123
import org.elasticsearch.core.Nullable;
2224
import org.elasticsearch.discovery.DiscoveryStats;
2325
import org.elasticsearch.http.HttpStats;
@@ -38,10 +40,13 @@
3840
import org.elasticsearch.xcontent.ToXContent;
3941

4042
import java.io.IOException;
43+
import java.util.Collections;
4144
import java.util.Iterator;
4245
import java.util.Map;
4346
import java.util.Objects;
4447

48+
import static org.elasticsearch.common.xcontent.ChunkedToXContentHelper.singleChunk;
49+
4550
/**
4651
* Node statistics (dynamic, changes depending on when created).
4752
*/
@@ -342,7 +347,7 @@ public void writeTo(StreamOutput out) throws IOException {
342347

343348
@Override
344349
public Iterator<? extends ToXContent> toXContentChunked(ToXContent.Params outerParams) {
345-
return ChunkedToXContent.builder(outerParams).append((builder, params) -> {
350+
return Iterators.concat(singleChunk((builder, params) -> {
346351
builder.field("name", getNode().getName());
347352
builder.field("transport_address", getNode().getAddress().toString());
348353
builder.field("host", getNode().getHostName());
@@ -353,38 +358,47 @@ public Iterator<? extends ToXContent> toXContentChunked(ToXContent.Params outerP
353358
builder.value(role.roleName());
354359
}
355360
builder.endArray();
356-
357361
if (getNode().getAttributes().isEmpty() == false) {
358362
builder.startObject("attributes");
359363
for (Map.Entry<String, String> attrEntry : getNode().getAttributes().entrySet()) {
360364
builder.field(attrEntry.getKey(), attrEntry.getValue());
361365
}
362366
builder.endObject();
363367
}
368+
364369
return builder;
365-
})
366-
367-
.appendIfPresent(getIndices())
368-
.append((builder, p) -> builder.value(ifPresent(getOs()), p).value(ifPresent(getProcess()), p).value(ifPresent(getJvm()), p))
369-
370-
.appendIfPresent(getThreadPool())
371-
.appendIfPresent(getFs())
372-
.appendIfPresent(getTransport())
373-
.appendIfPresent(getHttp())
374-
.appendIfPresent(getBreaker())
375-
.appendIfPresent(getScriptStats())
376-
.appendIfPresent(getDiscoveryStats())
377-
.appendIfPresent(getIngestStats())
378-
.appendIfPresent(getAdaptiveSelectionStats())
379-
.appendIfPresent(getScriptCacheStats())
380-
.append(
370+
}),
371+
ifPresent(getIndices()).toXContentChunked(outerParams),
372+
singleChunk(
373+
(builder, p) -> builder.value(ifPresent(getOs()), p).value(ifPresent(getProcess()), p).value(ifPresent(getJvm()), p)
374+
),
375+
ifPresent(getThreadPool()).toXContentChunked(outerParams),
376+
singleChunkIfPresent(getFs()),
377+
ifPresent(getTransport()).toXContentChunked(outerParams),
378+
ifPresent(getHttp()).toXContentChunked(outerParams),
379+
singleChunkIfPresent(getBreaker()),
380+
ifPresent(getScriptStats()).toXContentChunked(outerParams),
381+
singleChunkIfPresent(getDiscoveryStats()),
382+
ifPresent(getIngestStats()).toXContentChunked(outerParams),
383+
singleChunkIfPresent(getAdaptiveSelectionStats()),
384+
singleChunkIfPresent(getScriptCacheStats()),
385+
singleChunk(
381386
(builder, p) -> builder.value(ifPresent(getIndexingPressureStats()), p)
382387
.value(ifPresent(getRepositoriesStats()), p)
383388
.value(ifPresent(getNodeAllocationStats()), p)
384-
);
389+
)
390+
);
391+
}
392+
393+
private static ChunkedToXContent ifPresent(@Nullable ChunkedToXContent chunkedToXContent) {
394+
return Objects.requireNonNullElse(chunkedToXContent, ChunkedToXContent.EMPTY);
385395
}
386396

387397
private static ToXContent ifPresent(@Nullable ToXContent toXContent) {
388398
return Objects.requireNonNullElse(toXContent, ToXContent.EMPTY);
389399
}
400+
401+
private static Iterator<ToXContent> singleChunkIfPresent(ToXContent toXContent) {
402+
return toXContent == null ? Collections.emptyIterator() : ChunkedToXContentHelper.singleChunk(toXContent);
403+
}
390404
}

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414
import org.elasticsearch.action.support.nodes.BaseNodesXContentResponse;
1515
import org.elasticsearch.cluster.ClusterName;
1616
import org.elasticsearch.common.Strings;
17+
import org.elasticsearch.common.collect.Iterators;
1718
import org.elasticsearch.common.io.stream.StreamInput;
1819
import org.elasticsearch.common.io.stream.StreamOutput;
19-
import org.elasticsearch.common.xcontent.ChunkedToXContent;
20+
import org.elasticsearch.common.xcontent.ChunkedToXContentHelper;
2021
import org.elasticsearch.xcontent.ToXContent;
2122

2223
import java.io.IOException;
@@ -41,12 +42,15 @@ protected void writeNodesTo(StreamOutput out, List<NodeStats> nodes) throws IOEx
4142

4243
@Override
4344
protected Iterator<? extends ToXContent> xContentChunks(ToXContent.Params outerParams) {
44-
return ChunkedToXContent.builder(outerParams)
45-
.object(
46-
"nodes",
47-
getNodes().iterator(),
48-
(b, ns) -> b.object(ns.getNode().getId(), ob -> ob.field("timestamp", ns.getTimestamp()).append(ns))
49-
);
45+
return Iterators.concat(
46+
ChunkedToXContentHelper.startObject("nodes"),
47+
Iterators.flatMap(getNodes().iterator(), nodeStats -> Iterators.concat(Iterators.single((builder, params) -> {
48+
builder.startObject(nodeStats.getNode().getId());
49+
builder.field("timestamp", nodeStats.getTimestamp());
50+
return builder;
51+
}), nodeStats.toXContentChunked(outerParams), ChunkedToXContentHelper.endObject())),
52+
ChunkedToXContentHelper.endObject()
53+
);
5054
}
5155

5256
@Override

server/src/main/java/org/elasticsearch/action/admin/cluster/reroute/ClusterRerouteResponse.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,20 @@
1313
import org.elasticsearch.action.support.master.IsAcknowledgedSupplier;
1414
import org.elasticsearch.cluster.ClusterState;
1515
import org.elasticsearch.cluster.routing.allocation.RoutingExplanations;
16+
import org.elasticsearch.common.collect.Iterators;
1617
import org.elasticsearch.common.io.stream.StreamInput;
1718
import org.elasticsearch.common.io.stream.StreamOutput;
1819
import org.elasticsearch.common.logging.DeprecationCategory;
1920
import org.elasticsearch.common.logging.DeprecationLogger;
20-
import org.elasticsearch.common.xcontent.ChunkedToXContent;
21+
import org.elasticsearch.common.xcontent.ChunkedToXContentHelper;
2122
import org.elasticsearch.common.xcontent.ChunkedToXContentObject;
2223
import org.elasticsearch.core.RestApiVersion;
2324
import org.elasticsearch.core.UpdateForV10;
2425
import org.elasticsearch.rest.action.search.RestSearchAction;
2526
import org.elasticsearch.xcontent.ToXContent;
2627

2728
import java.io.IOException;
29+
import java.util.Collections;
2830
import java.util.Iterator;
2931
import java.util.Objects;
3032

@@ -92,14 +94,17 @@ public Iterator<? extends ToXContent> toXContentChunked(ToXContent.Params outerP
9294
if (emitState(outerParams)) {
9395
deprecationLogger.critical(DeprecationCategory.API, "reroute_cluster_state", STATE_FIELD_DEPRECATION_MESSAGE);
9496
}
95-
return ChunkedToXContent.builder(outerParams).object(b -> {
96-
b.field(ACKNOWLEDGED_KEY, isAcknowledged());
97-
if (emitState(outerParams)) {
98-
b.xContentObject("state", state);
99-
}
100-
if (outerParams.paramAsBoolean("explain", false)) {
101-
b.append(explanations);
102-
}
103-
});
97+
return Iterators.concat(
98+
Iterators.single((builder, params) -> builder.startObject().field(ACKNOWLEDGED_KEY, isAcknowledged())),
99+
emitState(outerParams)
100+
? ChunkedToXContentHelper.wrapWithObject("state", state.toXContentChunked(outerParams))
101+
: Collections.emptyIterator(),
102+
Iterators.single((builder, params) -> {
103+
if (params.paramAsBoolean("explain", false)) {
104+
explanations.toXContent(builder, params);
105+
}
106+
return builder.endObject();
107+
})
108+
);
104109
}
105110
}

server/src/main/java/org/elasticsearch/action/bulk/BulkResponse.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import org.elasticsearch.common.collect.Iterators;
1515
import org.elasticsearch.common.io.stream.StreamInput;
1616
import org.elasticsearch.common.io.stream.StreamOutput;
17-
import org.elasticsearch.common.xcontent.ChunkedToXContent;
1817
import org.elasticsearch.common.xcontent.ChunkedToXContentObject;
1918
import org.elasticsearch.core.TimeValue;
2019
import org.elasticsearch.xcontent.ToXContent;
@@ -158,13 +157,14 @@ public void writeTo(StreamOutput out) throws IOException {
158157

159158
@Override
160159
public Iterator<? extends ToXContent> toXContentChunked(ToXContent.Params params) {
161-
return ChunkedToXContent.builder(params).object(ob -> ob.append((b, p) -> {
162-
b.field(ERRORS, hasFailures());
163-
b.field(TOOK, tookInMillis);
160+
return Iterators.concat(Iterators.single((builder, p) -> {
161+
builder.startObject();
162+
builder.field(ERRORS, hasFailures());
163+
builder.field(TOOK, tookInMillis);
164164
if (ingestTookInMillis != BulkResponse.NO_INGEST_TOOK) {
165-
b.field(INGEST_TOOK, ingestTookInMillis);
165+
builder.field(INGEST_TOOK, ingestTookInMillis);
166166
}
167-
return b;
168-
}).array(ITEMS, Iterators.forArray(responses)));
167+
return builder.startArray(ITEMS);
168+
}), Iterators.forArray(responses), Iterators.<ToXContent>single((builder, p) -> builder.endArray().endObject()));
169169
}
170170
}

server/src/main/java/org/elasticsearch/action/search/SearchResponse.java

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@
1414
import org.elasticsearch.action.OriginalIndices;
1515
import org.elasticsearch.common.Strings;
1616
import org.elasticsearch.common.bytes.BytesReference;
17+
import org.elasticsearch.common.collect.Iterators;
1718
import org.elasticsearch.common.io.stream.StreamInput;
1819
import org.elasticsearch.common.io.stream.StreamOutput;
1920
import org.elasticsearch.common.io.stream.Writeable;
2021
import org.elasticsearch.common.lucene.Lucene;
2122
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
22-
import org.elasticsearch.common.xcontent.ChunkedToXContent;
23+
import org.elasticsearch.common.xcontent.ChunkedToXContentHelper;
2324
import org.elasticsearch.common.xcontent.ChunkedToXContentObject;
2425
import org.elasticsearch.core.Nullable;
2526
import org.elasticsearch.core.RefCounted;
@@ -382,17 +383,24 @@ public Clusters getClusters() {
382383
@Override
383384
public Iterator<? extends ToXContent> toXContentChunked(ToXContent.Params params) {
384385
assert hasReferences();
385-
return ChunkedToXContent.builder(params).xContentObject(innerToXContentChunked(params));
386+
return Iterators.concat(
387+
ChunkedToXContentHelper.startObject(),
388+
this.innerToXContentChunked(params),
389+
ChunkedToXContentHelper.endObject()
390+
);
386391
}
387392

388393
public Iterator<? extends ToXContent> innerToXContentChunked(ToXContent.Params params) {
389-
return ChunkedToXContent.builder(params)
390-
.append(SearchResponse.this::headerToXContent)
391-
.append(clusters)
392-
.append(hits)
393-
.appendIfPresent(aggregations)
394-
.appendIfPresent(suggest)
395-
.appendIfPresent(profileResults);
394+
return Iterators.concat(
395+
ChunkedToXContentHelper.singleChunk(SearchResponse.this::headerToXContent),
396+
Iterators.single(clusters),
397+
Iterators.concat(
398+
hits.toXContentChunked(params),
399+
aggregations == null ? Collections.emptyIterator() : ChunkedToXContentHelper.singleChunk(aggregations),
400+
suggest == null ? Collections.emptyIterator() : ChunkedToXContentHelper.singleChunk(suggest),
401+
profileResults == null ? Collections.emptyIterator() : ChunkedToXContentHelper.singleChunk(profileResults)
402+
)
403+
);
396404
}
397405

398406
public XContentBuilder headerToXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {

server/src/main/java/org/elasticsearch/action/support/broadcast/ChunkedBroadcastResponse.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
package org.elasticsearch.action.support.broadcast;
1010

1111
import org.elasticsearch.action.support.DefaultShardOperationFailedException;
12+
import org.elasticsearch.common.collect.Iterators;
1213
import org.elasticsearch.common.io.stream.StreamInput;
13-
import org.elasticsearch.common.xcontent.ChunkedToXContent;
14+
import org.elasticsearch.common.xcontent.ChunkedToXContentHelper;
1415
import org.elasticsearch.common.xcontent.ChunkedToXContentObject;
1516
import org.elasticsearch.rest.action.RestActions;
1617
import org.elasticsearch.xcontent.ToXContent;
@@ -35,8 +36,11 @@ public ChunkedBroadcastResponse(
3536

3637
@Override
3738
public final Iterator<ToXContent> toXContentChunked(ToXContent.Params params) {
38-
return ChunkedToXContent.builder(params)
39-
.object(ob -> ob.append((b, p) -> RestActions.buildBroadcastShardsHeader(b, p, this)).append(this::customXContentChunks));
39+
return Iterators.concat(Iterators.single((b, p) -> {
40+
b.startObject();
41+
RestActions.buildBroadcastShardsHeader(b, p, this);
42+
return b;
43+
}), customXContentChunks(params), ChunkedToXContentHelper.endObject());
4044
}
4145

4246
protected abstract Iterator<ToXContent> customXContentChunks(ToXContent.Params params);

server/src/main/java/org/elasticsearch/action/support/nodes/BaseNodesXContentResponse.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111

1212
import org.elasticsearch.action.FailedNodeException;
1313
import org.elasticsearch.cluster.ClusterName;
14-
import org.elasticsearch.common.xcontent.ChunkedToXContent;
14+
import org.elasticsearch.common.collect.Iterators;
15+
import org.elasticsearch.common.xcontent.ChunkedToXContentHelper;
1516
import org.elasticsearch.common.xcontent.ChunkedToXContentObject;
1617
import org.elasticsearch.rest.action.RestActions;
1718
import org.elasticsearch.xcontent.ToXContent;
@@ -29,12 +30,11 @@ protected BaseNodesXContentResponse(ClusterName clusterName, List<TNodeResponse>
2930

3031
@Override
3132
public final Iterator<? extends ToXContent> toXContentChunked(ToXContent.Params params) {
32-
return ChunkedToXContent.builder(params)
33-
.object(
34-
ob -> ob.append((b, p) -> RestActions.buildNodesHeader(b, p, this))
35-
.field("cluster_name", getClusterName().value())
36-
.append(xContentChunks(params))
37-
);
33+
return Iterators.concat(Iterators.single((b, p) -> {
34+
b.startObject();
35+
RestActions.buildNodesHeader(b, p, this);
36+
return b.field("cluster_name", getClusterName().value());
37+
}), xContentChunks(params), ChunkedToXContentHelper.endObject());
3838
}
3939

4040
protected abstract Iterator<? extends ToXContent> xContentChunks(ToXContent.Params outerParams);

server/src/main/java/org/elasticsearch/cluster/ClusterFeatures.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111

1212
import org.elasticsearch.cluster.node.DiscoveryNode;
1313
import org.elasticsearch.cluster.node.DiscoveryNodes;
14+
import org.elasticsearch.common.collect.Iterators;
1415
import org.elasticsearch.common.io.stream.StreamInput;
1516
import org.elasticsearch.common.io.stream.StreamOutput;
16-
import org.elasticsearch.common.xcontent.ChunkedToXContent;
17+
import org.elasticsearch.common.xcontent.ChunkedToXContentHelper;
1718
import org.elasticsearch.common.xcontent.ChunkedToXContentObject;
1819
import org.elasticsearch.features.NodeFeature;
1920
import org.elasticsearch.xcontent.ToXContent;
@@ -278,12 +279,15 @@ public ClusterFeatures apply(ClusterFeatures part) {
278279

279280
@Override
280281
public Iterator<? extends ToXContent> toXContentChunked(ToXContent.Params params) {
281-
return ChunkedToXContent.builder(params)
282-
.array(nodeFeatures.entrySet().stream().sorted(Map.Entry.comparingByKey()).iterator(), e -> (builder, p) -> {
282+
return Iterators.concat(
283+
ChunkedToXContentHelper.startArray(),
284+
nodeFeatures.entrySet().stream().sorted(Map.Entry.comparingByKey()).<ToXContent>map(e -> (builder, p) -> {
283285
String[] features = e.getValue().toArray(String[]::new);
284286
Arrays.sort(features);
285287
return builder.startObject().field("node_id", e.getKey()).array("features", features).endObject();
286-
});
288+
}).iterator(),
289+
ChunkedToXContentHelper.endArray()
290+
);
287291
}
288292

289293
@Override

0 commit comments

Comments
 (0)