Skip to content

Commit

Permalink
Speed up String array writes to XContent (#98957)
Browse files Browse the repository at this point in the history
Jackson has a direct method for writing string arrays
that saves us some of the indirection we have when looping
over a string array. This normally doesn't gain much, but for extreme
cases like long index name lists in field caps it saves a couple percent
in CPU time.
  • Loading branch information
original-brownbear committed Aug 30, 2023
1 parent b6c85db commit 37d55da
Show file tree
Hide file tree
Showing 18 changed files with 51 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,31 @@ public void writeString(String value) throws IOException {
}
}

@Override
public void writeStringArray(String[] array) throws IOException {
try {
if (isFiltered()) {
// filtered serialization does not work correctly with the bulk array serializer, so we need to fall back to serializing
// the array one-by-one
// TODO: this can probably be removed after upgrading Jackson to 2.15.1 or later, see
// https://github.com/FasterXML/jackson-core/issues/1023
writeStringArrayFiltered(array);
} else {
generator.writeArray(array, 0, array.length);
}
} catch (JsonGenerationException e) {
throw new XContentGenerationException(e);
}
}

private void writeStringArrayFiltered(String[] array) throws IOException {
writeStartArray();
for (String s : array) {
writeString(s);
}
writeEndArray();
}

@Override
public void writeString(char[] value, int offset, int len) throws IOException {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -740,11 +740,7 @@ private XContentBuilder values(String[] values) throws IOException {
if (values == null) {
return nullValue();
}
startArray();
for (String s : values) {
value(s);
}
endArray();
generator.writeStringArray(values);
return this;
}

Expand Down Expand Up @@ -1055,8 +1051,7 @@ public XContentBuilder stringStringMap(String name, Map<String, String> values)
}
startObject();
for (Map.Entry<String, String> value : values.entrySet()) {
field(value.getKey());
value(value.getValue());
generator.writeStringField(value.getKey(), value.getValue());
}
return endObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ public interface XContentGenerator extends Closeable, Flushable {

void writeString(String value) throws IOException;

void writeStringArray(String[] array) throws IOException;

void writeString(char[] text, int offset, int len) throws IOException;

void writeUTF8String(byte[] value, int offset, int length) throws IOException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
builder.field("source", source);
builder.field("target", target);
if (indices != null) {
builder.startArray("indices");
for (String index : indices) {
builder.value(index);
}
builder.endArray();
builder.array("indices", indices);
}
if (indicesOptions != null) {
indicesOptions.toXContent(builder, params);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -428,17 +428,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
builder.startObject();
builder.field("repository", repository);
builder.field("snapshot", snapshot);
builder.startArray("indices");
for (String index : indices) {
builder.value(index);
}
builder.endArray();
builder.array("indices", indices);
if (featureStates != null) {
builder.startArray("feature_states");
for (String plugin : featureStates) {
builder.value(plugin);
}
builder.endArray();
builder.array("feature_states", featureStates);
}
builder.field("partial", partial);
builder.field("include_global_state", includeGlobalState);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -580,11 +580,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
}

private void toXContentFragment(XContentBuilder builder, Params params) throws IOException {
builder.startArray("indices");
for (String index : indices) {
builder.value(index);
}
builder.endArray();
builder.array("indices", indices);
if (indicesOptions != null) {
indicesOptions.toXContent(builder, params);
}
Expand All @@ -595,11 +591,7 @@ private void toXContentFragment(XContentBuilder builder, Params params) throws I
builder.field("rename_replacement", renameReplacement);
}
if (featureStates != null && featureStates.length > 0) {
builder.startArray("feature_states");
for (String plugin : featureStates) {
builder.value(plugin);
}
builder.endArray();
builder.array("feature_states", featureStates);
}
builder.field("include_global_state", includeGlobalState);
builder.field("partial", partial);
Expand All @@ -611,11 +603,7 @@ private void toXContentFragment(XContentBuilder builder, Params params) throws I
}
builder.endObject();
}
builder.startArray("ignore_index_settings");
for (String ignoreIndexSetting : ignoreIndexSettings) {
builder.value(ignoreIndexSetting);
}
builder.endArray();
builder.array("ignore_index_settings", ignoreIndexSettings);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,9 +300,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
List<Map.Entry<String, Set<String>>> entries = new ArrayList<>(meta.entrySet());
entries.sort(Map.Entry.comparingByKey()); // provide predictable order
for (Map.Entry<String, Set<String>> entry : entries) {
List<String> values = new ArrayList<>(entry.getValue());
values.sort(String::compareTo); // provide predictable order
builder.stringListField(entry.getKey(), values);
String[] values = entry.getValue().toArray(Strings.EMPTY_ARRAY);
Arrays.sort(values, String::compareTo); // provide predictable order
builder.array(entry.getKey(), values);
}
builder.endObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public static ReservedStateHandlerMetadata readFrom(StreamInput in) throws IOExc
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(name());
builder.stringListField(KEYS.getPreferredName(), keys().stream().sorted().toList()); // ordered keys for output consistency
builder.array(KEYS.getPreferredName(), keys().stream().sorted().toArray(String[]::new)); // ordered keys for output consistency
builder.endObject();
return builder;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,7 @@ public final XContentBuilder toXContent(XContentBuilder builder, Params params)
builder.startObject(type);

if (overrideBucketsPath() == false && bucketsPaths != null) {
builder.startArray(PipelineAggregator.Parser.BUCKETS_PATH.getPreferredName());
for (String path : bucketsPaths) {
builder.value(path);
}
builder.endArray();
builder.array(PipelineAggregator.Parser.BUCKETS_PATH.getPreferredName(), bucketsPaths);
}

internalXContent(builder, params);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,7 @@ public XContentBuilder doXContentBody(XContentBuilder builder, Params params) th
if (hasValue && format != DocValueFormat.RAW) {
builder.field(CommonFields.VALUE_AS_STRING.getPreferredName(), format.format(value).toString());
}
builder.startArray(KEYS_FIELD.getPreferredName());
for (String key : keys) {
builder.value(key);
}
builder.endArray();
builder.array(KEYS_FIELD.getPreferredName(), keys);
return builder;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ public void test() throws IOException {
final XContentBuilder template = jsonBuilder();
template.startObject();
{
template.startArray("index_patterns").value("*").endArray();
template.array("index_patterns", "*");
if (useComponentTemplate) {
template.field("priority", 4); // relatively low priority, but hopefully uncommon enough not to conflict
template.startObject("template");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,11 +347,7 @@ protected static void createAutoFollowPattern(
try (XContentBuilder bodyBuilder = JsonXContent.contentBuilder()) {
bodyBuilder.startObject();
{
bodyBuilder.startArray("leader_index_patterns");
{
bodyBuilder.value(pattern);
}
bodyBuilder.endArray();
bodyBuilder.array("leader_index_patterns", pattern);
if (followIndexPattern != null) {
bodyBuilder.field("follow_index_pattern", followIndexPattern);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,7 @@ protected void addCustomFields(XContentBuilder builder, Params params) throws IO
builder.startObject("acknowledge");
builder.field(MESSAGE_FIELD.getPreferredName(), acknowledgeMessage);
for (Map.Entry<String, String[]> entry : acknowledgeMessages.entrySet()) {
builder.startArray(entry.getKey());
for (String message : entry.getValue()) {
builder.value(message);
}
builder.endArray();
builder.array(entry.getKey(), entry.getValue());
}
builder.endObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,7 @@ public RestResponse buildResponse(PostStartTrialResponse response, XContentBuild
builder.startObject("acknowledge");
builder.field("message", response.getAcknowledgementMessage());
for (Map.Entry<String, String[]> entry : acknowledgementMessages.entrySet()) {
builder.startArray(entry.getKey());
for (String message : entry.getValue()) {
builder.value(message);
}
builder.endArray();
builder.array(entry.getKey(), entry.getValue());
}
builder.endObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,7 @@ protected void addCustomFields(XContentBuilder builder, Params params) throws IO
builder.startObject("acknowledge");
builder.field("message", acknowledgeHeader);
for (Map.Entry<String, String[]> entry : acknowledgeMessages.entrySet()) {
builder.startArray(entry.getKey());
for (String message : entry.getValue()) {
builder.value(message);
}
builder.endArray();
builder.array(entry.getKey(), entry.getValue());
}
builder.endObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
builder.timeField("execution_time", executionTime);
builder.field("execution_phase", phase);
if (executedActions != null) {
builder.startArray("executed_actions");
for (String executedAction : executedActions) {
builder.value(executedAction);
}
builder.endArray();
builder.array("executed_actions", executedActions);
}
if (params.paramAsBoolean("emit_stacktraces", false)) {
builder.startArray("stack_trace");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -595,12 +595,12 @@ private static void addMetricFieldMapping(final XContentBuilder builder, final S
// only one value (the last value of the counter)
builder.startObject(field).field("type", fieldProperties.get("type")).field(TIME_SERIES_METRIC_PARAM, metricType).endObject();
} else {
final List<String> supportedAggs = List.of(metricType.supportedAggs());
final String[] supportedAggsArray = metricType.supportedAggs();
// We choose max as the default metric
final String defaultMetric = supportedAggs.contains("max") ? "max" : supportedAggs.get(0);
final String defaultMetric = List.of(supportedAggsArray).contains("max") ? "max" : supportedAggsArray[0];
builder.startObject(field)
.field("type", AggregateDoubleMetricFieldMapper.CONTENT_TYPE)
.stringListField(AggregateDoubleMetricFieldMapper.Names.METRICS, supportedAggs)
.array(AggregateDoubleMetricFieldMapper.Names.METRICS, supportedAggsArray)
.field(AggregateDoubleMetricFieldMapper.Names.DEFAULT_METRIC, defaultMetric)
.field(TIME_SERIES_METRIC_PARAM, metricType)
.endObject();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
builder.field(XField.THUMB_URL.getPreferredName(), thumbUrl);
}
if (markdownSupportedFields != null) {
builder.startArray(XField.MARKDOWN_IN.getPreferredName());
for (String field : markdownSupportedFields) {
builder.value(field);
}
builder.endArray();
builder.array(XField.MARKDOWN_IN.getPreferredName(), markdownSupportedFields);
}
if (actions != null && actions.isEmpty() == false) {
builder.startArray("actions");
Expand Down

0 comments on commit 37d55da

Please sign in to comment.