Skip to content

Commit 5edfbcc

Browse files
Make use of Chunked Encoding in the REST segments API (#90136)
This one returns the largest of all responses in the diagnostics in most cases, making it chunked by index which should be good enough.
1 parent 416492a commit 5edfbcc

File tree

4 files changed

+52
-22
lines changed

4 files changed

+52
-22
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,7 @@ public void initRestHandlers(Supplier<DiscoveryNodes> nodesInCluster) {
759759
registerHandler.accept(new RestPostFeatureUpgradeAction());
760760
registerHandler.accept(new RestGetIndicesAction(threadPool));
761761
registerHandler.accept(new RestIndicesStatsAction());
762-
registerHandler.accept(new RestIndicesSegmentsAction(threadPool));
762+
registerHandler.accept(new RestIndicesSegmentsAction());
763763
registerHandler.accept(new RestIndicesShardStoresAction());
764764
registerHandler.accept(new RestGetAliasesAction());
765765
registerHandler.accept(new RestIndexDeleteAliasesAction());

server/src/main/java/org/elasticsearch/action/admin/indices/segments/IndicesSegmentResponse.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,27 @@
1515
import org.apache.lucene.util.Accountable;
1616
import org.elasticsearch.action.support.DefaultShardOperationFailedException;
1717
import org.elasticsearch.action.support.broadcast.BroadcastResponse;
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.unit.ByteSizeValue;
22+
import org.elasticsearch.common.xcontent.ChunkedToXContent;
2123
import org.elasticsearch.core.RestApiVersion;
2224
import org.elasticsearch.index.engine.Segment;
23-
import org.elasticsearch.transport.Transports;
25+
import org.elasticsearch.rest.action.RestActions;
26+
import org.elasticsearch.xcontent.ToXContent;
2427
import org.elasticsearch.xcontent.XContentBuilder;
2528

2629
import java.io.IOException;
2730
import java.util.ArrayList;
2831
import java.util.Collection;
2932
import java.util.HashMap;
33+
import java.util.Iterator;
3034
import java.util.List;
3135
import java.util.Locale;
3236
import java.util.Map;
3337

34-
public class IndicesSegmentResponse extends BroadcastResponse {
38+
public class IndicesSegmentResponse extends BroadcastResponse implements ChunkedToXContent {
3539

3640
private final ShardSegments[] shards;
3741

@@ -77,12 +81,12 @@ public void writeTo(StreamOutput out) throws IOException {
7781
}
7882

7983
@Override
80-
protected void addCustomXContentFields(XContentBuilder builder, Params params) throws IOException {
81-
assert Transports.assertNotTransportThread("segments are very numerous, too expensive to serialize on a transport thread");
82-
83-
builder.startObject(Fields.INDICES);
84-
85-
for (IndexSegments indexSegments : getIndices().values()) {
84+
public Iterator<? extends ToXContent> toXContentChunked() {
85+
return Iterators.concat(Iterators.single(((builder, params) -> {
86+
builder.startObject();
87+
RestActions.buildBroadcastShardsHeader(builder, params, this);
88+
return builder.startObject(Fields.INDICES);
89+
})), getIndices().values().stream().map(indexSegments -> (ToXContent) (builder, params) -> {
8690
builder.startObject(indexSegments.getIndex());
8791

8892
builder.startObject(Fields.SHARDS);
@@ -141,9 +145,15 @@ protected void addCustomXContentFields(XContentBuilder builder, Params params) t
141145
builder.endObject();
142146

143147
builder.endObject();
144-
}
148+
return builder;
149+
}).iterator(), Iterators.single((builder, params) -> builder.endObject().endObject()));
150+
}
145151

146-
builder.endObject();
152+
@Override
153+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
154+
// override the BroadcastResponse behavior that is not compatible with the toXContentChunked implementation
155+
// TODO: make this not a ToXContent to make this unnecessary
156+
return ChunkedToXContent.super.toXContent(builder, params);
147157
}
148158

149159
private static void toXContent(XContentBuilder builder, Sort sort) throws IOException {

server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestIndicesSegmentsAction.java

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@
1616
import org.elasticsearch.common.logging.DeprecationLogger;
1717
import org.elasticsearch.rest.BaseRestHandler;
1818
import org.elasticsearch.rest.RestRequest;
19-
import org.elasticsearch.rest.action.DispatchingRestToXContentListener;
2019
import org.elasticsearch.rest.action.RestCancellableNodeClient;
21-
import org.elasticsearch.threadpool.ThreadPool;
20+
import org.elasticsearch.rest.action.RestChunkedToXContentListener;
2221

2322
import java.io.IOException;
2423
import java.util.List;
@@ -29,11 +28,7 @@ public class RestIndicesSegmentsAction extends BaseRestHandler {
2928

3029
private static final DeprecationLogger DEPRECATION_LOGGER = DeprecationLogger.getLogger(RestIndicesSegmentsAction.class);
3130

32-
private final ThreadPool threadPool;
33-
34-
public RestIndicesSegmentsAction(ThreadPool threadPool) {
35-
this.threadPool = threadPool;
36-
}
31+
public RestIndicesSegmentsAction() {}
3732

3833
@Override
3934
public List<Route> routes() {
@@ -60,9 +55,6 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC
6055
indicesSegmentsRequest.indicesOptions(IndicesOptions.fromRequest(request, indicesSegmentsRequest.indicesOptions()));
6156
return channel -> new RestCancellableNodeClient(client, request.getHttpChannel()).admin()
6257
.indices()
63-
.segments(
64-
indicesSegmentsRequest,
65-
new DispatchingRestToXContentListener<>(threadPool.executor(ThreadPool.Names.MANAGEMENT), channel, request)
66-
);
58+
.segments(indicesSegmentsRequest, new RestChunkedToXContentListener<>(channel));
6759
}
6860
}

server/src/test/java/org/elasticsearch/action/admin/indices/segments/IndicesSegmentResponseTests.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
import org.elasticsearch.xcontent.ToXContent;
1919
import org.elasticsearch.xcontent.XContentBuilder;
2020

21+
import java.util.ArrayList;
2122
import java.util.Collections;
23+
import java.util.List;
2224

2325
import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder;
2426

@@ -44,4 +46,30 @@ public void testToXContentSerialiationWithSortedFields() throws Exception {
4446
response.toXContent(builder, ToXContent.EMPTY_PARAMS);
4547
}
4648
}
49+
50+
public void testSerializesOneChunkPerIndex() {
51+
final int indices = randomIntBetween(1, 10);
52+
final List<ShardRouting> routings = new ArrayList<>(indices);
53+
for (int i = 0; i < indices; i++) {
54+
routings.add(TestShardRouting.newShardRouting("index-" + i, 0, "node_id", true, ShardRoutingState.STARTED));
55+
}
56+
Segment segment = new Segment("my");
57+
SortField sortField = new SortField("foo", SortField.Type.STRING);
58+
sortField.setMissingValue(SortField.STRING_LAST);
59+
segment.segmentSort = new Sort(sortField);
60+
IndicesSegmentResponse response = new IndicesSegmentResponse(
61+
routings.stream().map(routing -> new ShardSegments(routing, List.of(segment))).toArray(ShardSegments[]::new),
62+
indices,
63+
indices,
64+
0,
65+
Collections.emptyList()
66+
);
67+
int chunks = 0;
68+
final var iterator = response.toXContentChunked();
69+
while (iterator.hasNext()) {
70+
iterator.next();
71+
chunks++;
72+
}
73+
assertEquals(indices + 2, chunks);
74+
}
4775
}

0 commit comments

Comments
 (0)