Преглед изворни кода

Speed up String array writes to XContent (#98957)

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.
Armin Braun пре 2 година
родитељ
комит
37d55dac1c
18 измењених фајлова са 51 додато и 85 уклоњено
  1. 25 0
      libs/x-content/impl/src/main/java/org/elasticsearch/xcontent/provider/json/JsonXContentGenerator.java
  2. 2 7
      libs/x-content/src/main/java/org/elasticsearch/xcontent/XContentBuilder.java
  3. 2 0
      libs/x-content/src/main/java/org/elasticsearch/xcontent/XContentGenerator.java
  4. 1 5
      server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/clone/CloneSnapshotRequest.java
  5. 2 10
      server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotRequest.java
  6. 3 15
      server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java
  7. 3 3
      server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilities.java
  8. 1 1
      server/src/main/java/org/elasticsearch/cluster/metadata/ReservedStateHandlerMetadata.java
  9. 1 5
      server/src/main/java/org/elasticsearch/search/aggregations/pipeline/AbstractPipelineAggregationBuilder.java
  10. 1 5
      server/src/main/java/org/elasticsearch/search/aggregations/pipeline/InternalBucketMetricValue.java
  11. 1 1
      test/yaml-rest-runner/src/main/java/org/elasticsearch/test/rest/yaml/ESClientYamlSuiteTestCase.java
  12. 1 5
      x-pack/plugin/ccr/qa/src/main/java/org/elasticsearch/xpack/ccr/ESCCRRestTestCase.java
  13. 1 5
      x-pack/plugin/core/src/main/java/org/elasticsearch/license/PostStartBasicResponse.java
  14. 1 5
      x-pack/plugin/core/src/main/java/org/elasticsearch/license/RestPostStartTrialLicense.java
  15. 1 5
      x-pack/plugin/core/src/main/java/org/elasticsearch/protocol/xpack/license/PutLicenseResponse.java
  16. 1 5
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/execution/WatchExecutionSnapshot.java
  17. 3 3
      x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/TransportDownsampleAction.java
  18. 1 5
      x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/slack/message/Attachment.java

+ 25 - 0
libs/x-content/impl/src/main/java/org/elasticsearch/xcontent/provider/json/JsonXContentGenerator.java

@@ -364,6 +364,31 @@ public class JsonXContentGenerator implements XContentGenerator {
         }
     }
 
+    @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 {

+ 2 - 7
libs/x-content/src/main/java/org/elasticsearch/xcontent/XContentBuilder.java

@@ -740,11 +740,7 @@ public final class XContentBuilder implements Closeable, Flushable {
         if (values == null) {
             return nullValue();
         }
-        startArray();
-        for (String s : values) {
-            value(s);
-        }
-        endArray();
+        generator.writeStringArray(values);
         return this;
     }
 
@@ -1055,8 +1051,7 @@ public final class XContentBuilder implements Closeable, Flushable {
         }
         startObject();
         for (Map.Entry<String, String> value : values.entrySet()) {
-            field(value.getKey());
-            value(value.getValue());
+            generator.writeStringField(value.getKey(), value.getValue());
         }
         return endObject();
     }

+ 2 - 0
libs/x-content/src/main/java/org/elasticsearch/xcontent/XContentGenerator.java

@@ -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;

+ 1 - 5
server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/clone/CloneSnapshotRequest.java

@@ -139,11 +139,7 @@ public class CloneSnapshotRequest extends MasterNodeRequest<CloneSnapshotRequest
         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);

+ 2 - 10
server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotRequest.java

@@ -428,17 +428,9 @@ public class CreateSnapshotRequest extends MasterNodeRequest<CreateSnapshotReque
         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);

+ 3 - 15
server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java

@@ -580,11 +580,7 @@ public class RestoreSnapshotRequest extends MasterNodeRequest<RestoreSnapshotReq
     }
 
     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);
         }
@@ -595,11 +591,7 @@ public class RestoreSnapshotRequest extends MasterNodeRequest<RestoreSnapshotReq
             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);
@@ -611,11 +603,7 @@ public class RestoreSnapshotRequest extends MasterNodeRequest<RestoreSnapshotReq
             }
             builder.endObject();
         }
-        builder.startArray("ignore_index_settings");
-        for (String ignoreIndexSetting : ignoreIndexSettings) {
-            builder.value(ignoreIndexSetting);
-        }
-        builder.endArray();
+        builder.array("ignore_index_settings", ignoreIndexSettings);
     }
 
     @Override

+ 3 - 3
server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilities.java

@@ -300,9 +300,9 @@ public class FieldCapabilities implements Writeable, ToXContentObject {
             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();
         }

+ 1 - 1
server/src/main/java/org/elasticsearch/cluster/metadata/ReservedStateHandlerMetadata.java

@@ -59,7 +59,7 @@ public record ReservedStateHandlerMetadata(String name, Set<String> keys)
     @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;
     }

+ 1 - 5
server/src/main/java/org/elasticsearch/search/aggregations/pipeline/AbstractPipelineAggregationBuilder.java

@@ -92,11 +92,7 @@ public abstract class AbstractPipelineAggregationBuilder<PAB extends AbstractPip
         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);

+ 1 - 5
server/src/main/java/org/elasticsearch/search/aggregations/pipeline/InternalBucketMetricValue.java

@@ -96,11 +96,7 @@ public class InternalBucketMetricValue extends InternalNumericMetricsAggregation
         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;
     }
 

+ 1 - 1
test/yaml-rest-runner/src/main/java/org/elasticsearch/test/rest/yaml/ESClientYamlSuiteTestCase.java

@@ -490,7 +490,7 @@ public abstract class ESClientYamlSuiteTestCase extends ESRestTestCase {
             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");

+ 1 - 5
x-pack/plugin/ccr/qa/src/main/java/org/elasticsearch/xpack/ccr/ESCCRRestTestCase.java

@@ -347,11 +347,7 @@ public class ESCCRRestTestCase extends ESRestTestCase {
         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);
                 }

+ 1 - 5
x-pack/plugin/core/src/main/java/org/elasticsearch/license/PostStartBasicResponse.java

@@ -110,11 +110,7 @@ public class PostStartBasicResponse extends AcknowledgedResponse implements Stat
             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();
         }

+ 1 - 5
x-pack/plugin/core/src/main/java/org/elasticsearch/license/RestPostStartTrialLicense.java

@@ -56,11 +56,7 @@ public class RestPostStartTrialLicense extends BaseRestHandler {
                     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();
                 }

+ 1 - 5
x-pack/plugin/core/src/main/java/org/elasticsearch/protocol/xpack/license/PutLicenseResponse.java

@@ -82,11 +82,7 @@ public class PutLicenseResponse extends AcknowledgedResponse {
             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();
         }

+ 1 - 5
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/execution/WatchExecutionSnapshot.java

@@ -112,11 +112,7 @@ public class WatchExecutionSnapshot implements Writeable, ToXContentObject {
         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");

+ 3 - 3
x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/TransportDownsampleAction.java

@@ -595,12 +595,12 @@ public class TransportDownsampleAction extends AcknowledgedTransportMasterNodeAc
             // 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();

+ 1 - 5
x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/slack/message/Attachment.java

@@ -162,11 +162,7 @@ public class Attachment implements MessageElement {
             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");