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

Optimize IngestCtxMap construction (#120833) (#120926)

Joe Gallo пре 8 месеци
родитељ
комит
0f46b562e6
18 измењених фајлова са 133 додато и 58 уклоњено
  1. 5 0
      docs/changelog/120833.yaml
  2. 7 7
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ConvertProcessorTests.java
  3. 4 4
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/DissectProcessorTests.java
  4. 15 8
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ForEachProcessorTests.java
  5. 1 1
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/JsonProcessorFactoryTests.java
  6. 3 6
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/JsonProcessorTests.java
  7. 2 2
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/KeyValueProcessorTests.java
  8. 3 4
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/SortProcessorTests.java
  9. 2 1
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/TerminateProcessorTests.java
  10. 2 2
      modules/ingest-geoip/src/internalClusterTest/java/org/elasticsearch/ingest/geoip/ReloadingDatabasesWhilePerformingGeoLookupsIT.java
  11. 8 9
      modules/ingest-geoip/src/test/java/org/elasticsearch/ingest/geoip/GeoIpProcessorFactoryTests.java
  12. 4 2
      server/src/main/java/org/elasticsearch/ingest/IngestCtxMap.java
  13. 4 5
      server/src/test/java/org/elasticsearch/ingest/ConditionalProcessorTests.java
  14. 12 0
      server/src/test/java/org/elasticsearch/ingest/IngestCtxMapTests.java
  15. 46 0
      server/src/test/java/org/elasticsearch/ingest/IngestDocumentTests.java
  16. 2 1
      x-pack/plugin/enrich/src/test/java/org/elasticsearch/xpack/enrich/GeoMatchProcessorTests.java
  17. 11 4
      x-pack/plugin/enrich/src/test/java/org/elasticsearch/xpack/enrich/MatchProcessorTests.java
  18. 2 2
      x-pack/plugin/redact/src/test/java/org/elasticsearch/xpack/redact/RedactProcessorTests.java

+ 5 - 0
docs/changelog/120833.yaml

@@ -0,0 +1,5 @@
+pr: 120833
+summary: Optimize `IngestCtxMap` construction
+area: Ingest Node
+type: enhancement
+issues: []

+ 7 - 7
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ConvertProcessorTests.java

@@ -527,7 +527,7 @@ public class ConvertProcessorTests extends ESTestCase {
             }
             default -> throw new UnsupportedOperationException();
         }
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Map.of("field", randomValue));
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>(Map.of("field", randomValue)));
         Processor processor = new ConvertProcessor(randomAlphaOfLength(10), null, "field", "field", Type.AUTO, false);
         processor.execute(ingestDocument);
         Object convertedValue = ingestDocument.getFieldValue("field", Object.class);
@@ -536,7 +536,7 @@ public class ConvertProcessorTests extends ESTestCase {
 
     public void testAutoConvertStringNotMatched() throws Exception {
         String value = "notAnIntFloatOrBool";
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Map.of("field", value));
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>(Map.of("field", value)));
         Processor processor = new ConvertProcessor(randomAlphaOfLength(10), null, "field", "field", Type.AUTO, false);
         processor.execute(ingestDocument);
         Object convertedValue = ingestDocument.getFieldValue("field", Object.class);
@@ -546,7 +546,7 @@ public class ConvertProcessorTests extends ESTestCase {
     public void testAutoConvertMatchBoolean() throws Exception {
         boolean randomBoolean = randomBoolean();
         String booleanString = Boolean.toString(randomBoolean);
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Map.of("field", booleanString));
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>(Map.of("field", booleanString)));
         Processor processor = new ConvertProcessor(randomAlphaOfLength(10), null, "field", "field", Type.AUTO, false);
         processor.execute(ingestDocument);
         Object convertedValue = ingestDocument.getFieldValue("field", Object.class);
@@ -556,7 +556,7 @@ public class ConvertProcessorTests extends ESTestCase {
     public void testAutoConvertMatchInteger() throws Exception {
         int randomInt = randomInt();
         String randomString = Integer.toString(randomInt);
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Map.of("field", randomString));
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>(Map.of("field", randomString)));
         Processor processor = new ConvertProcessor(randomAlphaOfLength(10), null, "field", "field", Type.AUTO, false);
         processor.execute(ingestDocument);
         Object convertedValue = ingestDocument.getFieldValue("field", Object.class);
@@ -566,7 +566,7 @@ public class ConvertProcessorTests extends ESTestCase {
     public void testAutoConvertMatchLong() throws Exception {
         long randomLong = randomLong();
         String randomString = Long.toString(randomLong);
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Map.of("field", randomString));
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>(Map.of("field", randomString)));
         Processor processor = new ConvertProcessor(randomAlphaOfLength(10), null, "field", "field", Type.AUTO, false);
         processor.execute(ingestDocument);
         Object convertedValue = ingestDocument.getFieldValue("field", Object.class);
@@ -577,7 +577,7 @@ public class ConvertProcessorTests extends ESTestCase {
         double randomDouble = randomDouble();
         String randomString = Double.toString(randomDouble);
         float randomFloat = Float.parseFloat(randomString);
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Map.of("field", randomString));
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>(Map.of("field", randomString)));
         Processor processor = new ConvertProcessor(randomAlphaOfLength(10), null, "field", "field", Type.AUTO, false);
         processor.execute(ingestDocument);
         Object convertedValue = ingestDocument.getFieldValue("field", Object.class);
@@ -588,7 +588,7 @@ public class ConvertProcessorTests extends ESTestCase {
     public void testAutoConvertMatchFloat() throws Exception {
         float randomFloat = randomFloat();
         String randomString = Float.toString(randomFloat);
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Map.of("field", randomString));
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>(Map.of("field", randomString)));
         Processor processor = new ConvertProcessor(randomAlphaOfLength(10), null, "field", "field", Type.AUTO, false);
         processor.execute(ingestDocument);
         Object convertedValue = ingestDocument.getFieldValue("field", Object.class);

+ 4 - 4
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/DissectProcessorTests.java

@@ -30,7 +30,7 @@ import static org.hamcrest.Matchers.equalTo;
 public class DissectProcessorTests extends ESTestCase {
 
     public void testMatch() {
-        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, Map.of("message", "foo,bar,baz"));
+        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, new HashMap<>(Map.of("message", "foo,bar,baz")));
         DissectProcessor dissectProcessor = new DissectProcessor("", null, "message", "%{a},%{b},%{c}", "", true);
         dissectProcessor.execute(ingestDocument);
         assertThat(ingestDocument.getFieldValue("a", String.class), equalTo("foo"));
@@ -45,7 +45,7 @@ public class DissectProcessorTests extends ESTestCase {
             1,
             null,
             null,
-            Map.of("message", "foo,bar,baz", "a", "willgetstompped")
+            new HashMap<>(Map.of("message", "foo,bar,baz", "a", "willgetstompped"))
         );
         assertThat(ingestDocument.getFieldValue("a", String.class), equalTo("willgetstompped"));
         DissectProcessor dissectProcessor = new DissectProcessor("", null, "message", "%{a},%{b},%{c}", "", true);
@@ -62,7 +62,7 @@ public class DissectProcessorTests extends ESTestCase {
             1,
             null,
             null,
-            Map.of("message", "foo       bar,,,,,,,baz nope:notagain 😊 🐇 🙃")
+            new HashMap<>(Map.of("message", "foo       bar,,,,,,,baz nope:notagain 😊 🐇 🙃"))
         );
         DissectProcessor dissectProcessor = new DissectProcessor(
             "",
@@ -81,7 +81,7 @@ public class DissectProcessorTests extends ESTestCase {
     }
 
     public void testMiss() {
-        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, Map.of("message", "foo:bar,baz"));
+        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, new HashMap<>(Map.of("message", "foo:bar,baz")));
         DissectProcessor dissectProcessor = new DissectProcessor("", null, "message", "%{a},%{b},%{c}", "", true);
         DissectException e = expectThrows(DissectException.class, () -> dissectProcessor.execute(ingestDocument));
         assertThat(e.getMessage(), containsString("Unable to find match for dissect pattern"));

+ 15 - 8
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ForEachProcessorTests.java

@@ -39,7 +39,7 @@ public class ForEachProcessorTests extends ESTestCase {
         values.add("foo");
         values.add("bar");
         values.add("baz");
-        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, Map.of("values", values));
+        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, new HashMap<>(Map.of("values", values)));
 
         ForEachProcessor processor = new ForEachProcessor("_tag", null, "values", new AsyncUpperCaseProcessor("_ingest._value"), false);
         execProcessor(processor, ingestDocument, (result, e) -> {});
@@ -55,7 +55,14 @@ public class ForEachProcessorTests extends ESTestCase {
     }
 
     public void testExecuteWithFailure() {
-        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, Map.of("values", List.of("a", "b", "c")));
+        IngestDocument ingestDocument = new IngestDocument(
+            "_index",
+            "_id",
+            1,
+            null,
+            null,
+            new HashMap<>(Map.of("values", List.of("a", "b", "c")))
+        );
 
         TestProcessor testProcessor = new TestProcessor(id -> {
             if ("c".equals(id.getFieldValue("_ingest._value", String.class))) {
@@ -173,7 +180,7 @@ public class ForEachProcessorTests extends ESTestCase {
         int numValues = randomIntBetween(1, 10000);
         List<String> values = IntStream.range(0, numValues).mapToObj(i -> "").toList();
 
-        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, Map.of("values", values));
+        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, new HashMap<>(Map.of("values", values)));
 
         ForEachProcessor processor = new ForEachProcessor("_tag", null, "values", innerProcessor, false);
         execProcessor(processor, ingestDocument, (result, e) -> {});
@@ -189,7 +196,7 @@ public class ForEachProcessorTests extends ESTestCase {
         values.add("string");
         values.add(1);
         values.add(null);
-        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, Map.of("values", values));
+        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, new HashMap<>(Map.of("values", values)));
 
         TemplateScript.Factory template = new TestTemplateService.MockTemplateScript.Factory("errors");
 
@@ -282,7 +289,7 @@ public class ForEachProcessorTests extends ESTestCase {
         Map<String, Object> innerMap3 = Map.of("foo3", 7, "bar3", 8, "baz3", 9, "otherKey", 42);
 
         Map<String, Object> outerMap = Map.of("foo", innerMap1, "bar", innerMap2, "baz", innerMap3);
-        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, Map.of("field", outerMap));
+        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, new HashMap<>(Map.of("field", outerMap)));
 
         List<String> visitedKeys = new ArrayList<>();
         List<Object> visitedValues = new ArrayList<>();
@@ -361,7 +368,7 @@ public class ForEachProcessorTests extends ESTestCase {
 
     public void testMapIteration() {
         Map<String, Object> mapValue = Map.of("foo", 1, "bar", 2, "baz", 3);
-        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, Map.of("field", mapValue));
+        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, new HashMap<>(Map.of("field", mapValue)));
 
         List<String> encounteredKeys = new ArrayList<>();
         List<Object> encounteredValues = new ArrayList<>();
@@ -390,7 +397,7 @@ public class ForEachProcessorTests extends ESTestCase {
 
     public void testRemovalOfMapKey() {
         Map<String, Object> mapValue = Map.of("foo", 1, "bar", 2, "baz", 3);
-        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, Map.of("field", mapValue));
+        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, new HashMap<>(Map.of("field", mapValue)));
 
         List<String> encounteredKeys = new ArrayList<>();
         List<Object> encounteredValues = new ArrayList<>();
@@ -419,7 +426,7 @@ public class ForEachProcessorTests extends ESTestCase {
         Map<String, Object> innerMap3 = Map.of("foo3", 7, "bar3", 8, "baz3", 9, "otherKey", 42);
 
         Map<String, Object> outerMap = Map.of("foo", innerMap1, "bar", innerMap2, "baz", innerMap3);
-        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, Map.of("field", outerMap));
+        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1, null, null, new HashMap<>(Map.of("field", outerMap)));
 
         List<String> visitedKeys = new ArrayList<>();
         List<Object> visitedValues = new ArrayList<>();

+ 1 - 1
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/JsonProcessorFactoryTests.java

@@ -74,7 +74,7 @@ public class JsonProcessorFactoryTests extends ESTestCase {
     public void testCreateWithStrictParsingParameter() throws Exception {
         String fieldName = randomAlphaOfLength(10);
         String processorTag = randomAlphaOfLength(10);
-        IngestDocument document = new IngestDocument("_index", "_id", 1, null, null, Map.of(fieldName, "123 \"foo\""));
+        IngestDocument document = new IngestDocument("_index", "_id", 1, null, null, new HashMap<>(Map.of(fieldName, "123 \"foo\"")));
 
         {
             Map<String, Object> strictConfig = new HashMap<>();

+ 3 - 6
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/JsonProcessorTests.java

@@ -170,12 +170,9 @@ public class JsonProcessorTests extends ESTestCase {
         String processorTag = randomAlphaOfLength(3);
         JsonProcessor lenientJsonProcessor = new JsonProcessor(processorTag, null, "a", null, true, REPLACE, true);
 
-        Map<String, Object> document = new HashMap<>();
-        String json = "{\"a\": 1, \"a\": 2}";
-        document.put("a", json);
-        document.put("c", "see");
+        Map<String, Object> document = Map.of("a", "{\"a\": 1, \"a\": 2}", "c", "see");
 
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>(document));
         lenientJsonProcessor.execute(ingestDocument);
 
         Map<String, Object> sourceAndMetadata = ingestDocument.getSourceAndMetadata();
@@ -185,7 +182,7 @@ public class JsonProcessorTests extends ESTestCase {
         JsonProcessor strictJsonProcessor = new JsonProcessor(processorTag, null, "a", null, true, REPLACE, false);
         Exception exception = expectThrows(
             IllegalArgumentException.class,
-            () -> strictJsonProcessor.execute(RandomDocumentPicks.randomIngestDocument(random(), document))
+            () -> strictJsonProcessor.execute(RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>(document)))
         );
         assertThat(exception.getMessage(), containsString("Duplicate field 'a'"));
     }

+ 2 - 2
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/KeyValueProcessorTests.java

@@ -40,7 +40,7 @@ public class KeyValueProcessorTests extends ESTestCase {
     }
 
     public void testRootTarget() throws Exception {
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Map.of());
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         ingestDocument.setFieldValue("myField", "first=hello&second=world&second=universe");
         Processor processor = createKvProcessor("myField", "&", "=", null, null, null, false);
         processor.execute(ingestDocument);
@@ -49,7 +49,7 @@ public class KeyValueProcessorTests extends ESTestCase {
     }
 
     public void testKeySameAsSourceField() throws Exception {
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Map.of());
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         ingestDocument.setFieldValue("first", "first=hello");
         Processor processor = createKvProcessor("first", "&", "=", null, null, null, false);
         processor.execute(ingestDocument);

+ 3 - 4
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/SortProcessorTests.java

@@ -19,7 +19,6 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
@@ -266,7 +265,7 @@ public class SortProcessorTests extends ESTestCase {
     }
 
     public void testDescendingSortWithTargetField() throws Exception {
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Map.of());
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         int numItems = randomIntBetween(1, 10);
         List<String> fieldValue = new ArrayList<>(numItems);
         List<String> expectedResult = new ArrayList<>(numItems);
@@ -286,7 +285,7 @@ public class SortProcessorTests extends ESTestCase {
     }
 
     public void testAscendingSortWithTargetField() throws Exception {
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Map.of());
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         int numItems = randomIntBetween(1, 10);
         List<String> fieldValue = new ArrayList<>(numItems);
         List<String> expectedResult = new ArrayList<>(numItems);
@@ -306,7 +305,7 @@ public class SortProcessorTests extends ESTestCase {
     }
 
     public void testSortWithTargetFieldLeavesOriginalUntouched() throws Exception {
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Map.of());
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         List<Integer> fieldValue = List.of(1, 5, 4);
         List<Integer> expectedResult = new ArrayList<>(fieldValue);
         Collections.sort(expectedResult);

+ 2 - 1
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/TerminateProcessorTests.java

@@ -16,6 +16,7 @@ import org.elasticsearch.ingest.TestTemplateService;
 import org.elasticsearch.ingest.ValueSource;
 import org.elasticsearch.test.ESTestCase;
 
+import java.util.HashMap;
 import java.util.Map;
 
 import static org.elasticsearch.ingest.RandomDocumentPicks.randomIngestDocument;
@@ -48,7 +49,7 @@ public class TerminateProcessorTests extends ESTestCase {
                 )
             )
         );
-        IngestDocument input = randomIngestDocument(random(), Map.of("foo", "bar"));
+        IngestDocument input = randomIngestDocument(random(), new HashMap<>(Map.of("foo", "bar")));
         PipelineOutput output = new PipelineOutput();
 
         pipeline.execute(input, output::set);

+ 2 - 2
modules/ingest-geoip/src/internalClusterTest/java/org/elasticsearch/ingest/geoip/ReloadingDatabasesWhilePerformingGeoLookupsIT.java

@@ -99,7 +99,7 @@ public class ReloadingDatabasesWhilePerformingGeoLookupsIT extends ESTestCase {
                             1L,
                             "routing",
                             VersionType.EXTERNAL,
-                            Map.of("_field", "89.160.20.128")
+                            new HashMap<>(Map.of("_field", "89.160.20.128"))
                         );
                         processor1.execute(document1);
                         assertThat(document1.getSourceAndMetadata().get("geoip"), notNullValue());
@@ -109,7 +109,7 @@ public class ReloadingDatabasesWhilePerformingGeoLookupsIT extends ESTestCase {
                             1L,
                             "routing",
                             VersionType.EXTERNAL,
-                            Map.of("_field", "89.160.20.128")
+                            new HashMap<>(Map.of("_field", "89.160.20.128"))
                         );
                         processor2.execute(document2);
                         assertThat(document2.getSourceAndMetadata().get("geoip"), notNullValue());

+ 8 - 9
modules/ingest-geoip/src/test/java/org/elasticsearch/ingest/geoip/GeoIpProcessorFactoryTests.java

@@ -387,7 +387,7 @@ public class GeoIpProcessorFactoryTests extends ESTestCase {
             assertNull(lazyLoader.databaseReader.get());
         }
 
-        final Map<String, Object> field = Map.of("_field", "1.1.1.1");
+        final Map<String, Object> field = new HashMap<>(Map.of("_field", "1.1.1.1"));
         final IngestDocument document = new IngestDocument("index", "id", 1L, "routing", VersionType.EXTERNAL, field);
 
         Map<String, Object> config = new HashMap<>();
@@ -456,7 +456,7 @@ public class GeoIpProcessorFactoryTests extends ESTestCase {
             assertNull(lazyLoader.databaseReader.get());
         }
 
-        final Map<String, Object> field = Map.of("_field", "1.1.1.1");
+        final Map<String, Object> field = new HashMap<>(Map.of("_field", "1.1.1.1"));
         final IngestDocument document = new IngestDocument("index", "id", 1L, "routing", VersionType.EXTERNAL, field);
 
         Map<String, Object> config = new HashMap<>();
@@ -509,7 +509,7 @@ public class GeoIpProcessorFactoryTests extends ESTestCase {
 
         GeoIpProcessor processor = (GeoIpProcessor) factory.create(null, processorTag, null, config);
 
-        processor.execute(RandomDocumentPicks.randomIngestDocument(random(), Map.of("_field", "89.160.20.128")));
+        processor.execute(RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>(Map.of("_field", "89.160.20.128"))));
     }
 
     public void testUpdateDatabaseWhileIngesting() throws Exception {
@@ -517,17 +517,16 @@ public class GeoIpProcessorFactoryTests extends ESTestCase {
         Map<String, Object> config = new HashMap<>();
         config.put("field", "source_field");
         GeoIpProcessor processor = (GeoIpProcessor) factory.create(null, null, null, config);
-        Map<String, Object> document = new HashMap<>();
-        document.put("source_field", "89.160.20.128");
+        Map<String, Object> document = Map.of("source_field", "89.160.20.128");
         {
-            IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
+            IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>(document));
             processor.execute(ingestDocument);
             Map<?, ?> geoData = (Map<?, ?>) ingestDocument.getSourceAndMetadata().get("geoip");
             assertThat(geoData.get("city_name"), equalTo("Tumba"));
         }
         {
             copyDatabase("GeoLite2-City-Test.mmdb", geoipTmpDir);
-            IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
+            IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>(document));
             databaseNodeService.updateDatabase("GeoLite2-City.mmdb", "md5", geoipTmpDir.resolve("GeoLite2-City-Test.mmdb"));
             processor.execute(ingestDocument);
             Map<?, ?> geoData = (Map<?, ?>) ingestDocument.getSourceAndMetadata().get("geoip");
@@ -535,7 +534,7 @@ public class GeoIpProcessorFactoryTests extends ESTestCase {
         }
         {
             // No databases are available, so assume that databases still need to be downloaded and therefore not fail:
-            IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
+            IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>(document));
             databaseNodeService.removeStaleEntries(List.of("GeoLite2-City.mmdb"));
             configDatabases.updateDatabase(geoIpConfigDir.resolve("GeoLite2-City.mmdb"), false);
             processor.execute(ingestDocument);
@@ -545,7 +544,7 @@ public class GeoIpProcessorFactoryTests extends ESTestCase {
         {
             // There are databases available, but not the right one, so tag:
             databaseNodeService.updateDatabase("GeoLite2-City-Test.mmdb", "md5", geoipTmpDir.resolve("GeoLite2-City-Test.mmdb"));
-            IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
+            IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>(document));
             processor.execute(ingestDocument);
             assertThat(ingestDocument.getSourceAndMetadata(), hasEntry("tags", List.of("_geoip_database_unavailable_GeoLite2-City.mmdb")));
         }

+ 4 - 2
server/src/main/java/org/elasticsearch/ingest/IngestCtxMap.java

@@ -13,7 +13,6 @@ import org.elasticsearch.index.VersionType;
 import org.elasticsearch.script.CtxMap;
 
 import java.time.ZonedDateTime;
-import java.util.HashMap;
 import java.util.Map;
 
 /**
@@ -33,6 +32,9 @@ final class IngestCtxMap extends CtxMap<IngestDocMetadata> {
 
     /**
      * Create an IngestCtxMap with the given metadata, source and default validators
+     * <p>
+     * The passed-in source map is used directly (that is, it's neither shallowly nor deeply copied). mutation-like methods (e.g. setters,
+     * put, etc.) may rely on the map being mutable, and will fail if the passed-in map isn't mutable.
      */
     IngestCtxMap(
         String index,
@@ -43,7 +45,7 @@ final class IngestCtxMap extends CtxMap<IngestDocMetadata> {
         ZonedDateTime timestamp,
         Map<String, Object> source
     ) {
-        super(new HashMap<>(source), new IngestDocMetadata(index, id, version, routing, versionType, timestamp));
+        super(source, new IngestDocMetadata(index, id, version, routing, versionType, timestamp));
     }
 
     /**

+ 4 - 5
server/src/test/java/org/elasticsearch/ingest/ConditionalProcessorTests.java

@@ -63,7 +63,6 @@ public class ConditionalProcessorTests extends ESTestCase {
             new HashMap<>(ScriptModule.CORE_CONTEXTS),
             () -> 1L
         );
-        Map<String, Object> document = new HashMap<>();
         LongSupplier relativeTimeProvider = mock(LongSupplier.class);
         when(relativeTimeProvider.getAsLong()).thenReturn(0L, TimeUnit.MILLISECONDS.toNanos(1), 0L, TimeUnit.MILLISECONDS.toNanos(2));
         ConditionalProcessor processor = new ConditionalProcessor(
@@ -102,7 +101,7 @@ public class ConditionalProcessorTests extends ESTestCase {
 
         // false, never call processor never increments metrics
         String falseValue = "falsy";
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         ingestDocument.setFieldValue(conditionalField, falseValue);
         execProcessor(processor, ingestDocument, (result, e) -> {});
         assertThat(ingestDocument.getSourceAndMetadata().get(conditionalField), is(falseValue));
@@ -110,21 +109,21 @@ public class ConditionalProcessorTests extends ESTestCase {
         assertStats(processor, 0, 0, 0);
         assertEquals(scriptName, processor.getCondition());
 
-        ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
+        ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         ingestDocument.setFieldValue(conditionalField, falseValue);
         ingestDocument.setFieldValue("error", true);
         execProcessor(processor, ingestDocument, (result, e) -> {});
         assertStats(processor, 0, 0, 0);
 
         // true, always call processor and increments metrics
-        ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
+        ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         ingestDocument.setFieldValue(conditionalField, trueValue);
         execProcessor(processor, ingestDocument, (result, e) -> {});
         assertThat(ingestDocument.getSourceAndMetadata().get(conditionalField), is(trueValue));
         assertThat(ingestDocument.getSourceAndMetadata().get("foo"), is("bar"));
         assertStats(processor, 1, 0, 1);
 
-        ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
+        ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         ingestDocument.setFieldValue(conditionalField, trueValue);
         ingestDocument.setFieldValue("error", true);
         IngestDocument finalIngestDocument = ingestDocument;

+ 12 - 0
server/src/test/java/org/elasticsearch/ingest/IngestCtxMapTests.java

@@ -21,6 +21,7 @@ import java.util.Set;
 
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.sameInstance;
 
 public class IngestCtxMapTests extends ESTestCase {
 
@@ -341,6 +342,17 @@ public class IngestCtxMapTests extends ESTestCase {
         assertThat(map.getOrDefault("baz", "quux"), equalTo("quux"));
     }
 
+    public void testSourceHashMapIsNotCopied() {
+        // a ctxMap will, as an optimization, just use the passed-in map reference
+        Map<String, Object> source = Map.of("index", "id");
+
+        map = new IngestCtxMap(source, new IngestDocMetadata(Map.of("_version", 5L), null));
+        assertThat(map.getSource(), sameInstance(source));
+
+        map = new IngestCtxMap(null, null, 10L, null, null, null, source);
+        assertThat(map.getSource(), sameInstance(source));
+    }
+
     private static class TestEntry implements Map.Entry<String, Object> {
         String key;
         Object value;

+ 46 - 0
server/src/test/java/org/elasticsearch/ingest/IngestDocumentTests.java

@@ -9,7 +9,10 @@
 
 package org.elasticsearch.ingest;
 
+import org.elasticsearch.common.bytes.BytesArray;
+import org.elasticsearch.common.xcontent.XContentHelper;
 import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.xcontent.XContentType;
 import org.hamcrest.Matchers;
 import org.junit.Before;
 
@@ -1128,4 +1131,47 @@ public class IngestDocumentTests extends ESTestCase {
         assertFalse(ingestDocument.updateIndexHistory(index1));
         assertThat(ingestDocument.getIndexHistory(), Matchers.contains(index1, index2));
     }
+
+    public void testSourceHashMapIsNotCopied() {
+        // an ingest document's ctxMap will, as an optimization, just use the passed-in map reference
+        {
+            Map<String, Object> source = new HashMap<>(Map.of("foo", 1));
+            IngestDocument document = new IngestDocument("index", "id", 1, null, null, source);
+            assertThat(document.getSource(), sameInstance(source));
+            assertThat(document.getCtxMap().getSource(), sameInstance(source));
+        }
+
+        {
+            Map<String, Object> source = XContentHelper.convertToMap(new BytesArray("{ \"foo\": 1 }"), false, XContentType.JSON).v2();
+            IngestDocument document = new IngestDocument("index", "id", 1, null, null, source);
+            assertThat(document.getSource(), sameInstance(source));
+            assertThat(document.getCtxMap().getSource(), sameInstance(source));
+        }
+
+        {
+            Map<String, Object> source = Map.of("foo", 1);
+            IngestDocument document = new IngestDocument("index", "id", 1, null, null, source);
+            assertThat(document.getSource(), sameInstance(source));
+            assertThat(document.getCtxMap().getSource(), sameInstance(source));
+        }
+
+        // a cloned ingest document will copy the map, though
+        {
+            Map<String, Object> source = Map.of("foo", 1);
+            IngestDocument document1 = new IngestDocument("index", "id", 1, null, null, source);
+            document1.getIngestMetadata().put("bar", 2);
+            IngestDocument document2 = new IngestDocument(document1);
+            assertThat(document2.getCtxMap().getMetadata(), equalTo(document1.getCtxMap().getMetadata()));
+            assertThat(document2.getSource(), not(sameInstance(source)));
+            assertThat(document2.getCtxMap().getMetadata(), equalTo(document1.getCtxMap().getMetadata()));
+            assertThat(document2.getCtxMap().getSource(), not(sameInstance(source)));
+
+            // it also copies these other nearby maps
+            assertThat(document2.getIngestMetadata(), equalTo(document1.getIngestMetadata()));
+            assertThat(document2.getIngestMetadata(), not(sameInstance(document1.getIngestMetadata())));
+
+            assertThat(document2.getCtxMap().getMetadata(), not(sameInstance(document1.getCtxMap().getMetadata())));
+            assertThat(document2.getCtxMap().getMetadata(), not(sameInstance(document1.getCtxMap().getMetadata())));
+        }
+    }
 }

+ 2 - 1
x-pack/plugin/enrich/src/test/java/org/elasticsearch/xpack/enrich/GeoMatchProcessorTests.java

@@ -24,6 +24,7 @@ import org.elasticsearch.ingest.IngestDocument;
 import org.elasticsearch.test.ESTestCase;
 
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.function.BiConsumer;
@@ -100,7 +101,7 @@ public class GeoMatchProcessorTests extends ESTestCase {
             1L,
             "_routing",
             VersionType.INTERNAL,
-            Map.of("location", fieldValue)
+            new HashMap<>(Map.of("location", fieldValue))
         );
         // Run
         IngestDocument[] holder = new IngestDocument[1];

+ 11 - 4
x-pack/plugin/enrich/src/test/java/org/elasticsearch/xpack/enrich/MatchProcessorTests.java

@@ -56,7 +56,7 @@ public class MatchProcessorTests extends ESTestCase {
             1L,
             "_routing",
             VersionType.INTERNAL,
-            Map.of("domain", "elastic.co")
+            new HashMap<>(Map.of("domain", "elastic.co"))
         );
         // Run
         IngestDocument[] holder = new IngestDocument[1];
@@ -158,7 +158,7 @@ public class MatchProcessorTests extends ESTestCase {
             1L,
             "_routing",
             VersionType.INTERNAL,
-            Map.of("domain", "elastic.com")
+            new HashMap<>(Map.of("domain", "elastic.com"))
         );
         // Run
         IngestDocument[] resultHolder = new IngestDocument[1];
@@ -308,7 +308,14 @@ public class MatchProcessorTests extends ESTestCase {
             "domain",
             1
         );
-        IngestDocument ingestDocument = new IngestDocument("_index", "_id", 1L, "_routing", VersionType.INTERNAL, Map.of("domain", 2));
+        IngestDocument ingestDocument = new IngestDocument(
+            "_index",
+            "_id",
+            1L,
+            "_routing",
+            VersionType.INTERNAL,
+            new HashMap<>(Map.of("domain", 2))
+        );
 
         // Execute
         IngestDocument[] holder = new IngestDocument[1];
@@ -351,7 +358,7 @@ public class MatchProcessorTests extends ESTestCase {
             1L,
             "_routing",
             VersionType.INTERNAL,
-            Map.of("domain", List.of("1", "2"))
+            new HashMap<>(Map.of("domain", List.of("1", "2")))
         );
 
         // Execute

+ 2 - 2
x-pack/plugin/redact/src/test/java/org/elasticsearch/xpack/redact/RedactProcessorTests.java

@@ -571,7 +571,7 @@ public class RedactProcessorTests extends ESTestCase {
         }
     }
 
-    private IngestDocument createIngestDoc(Map<String, Object> source) {
-        return new IngestDocument("index", "id", 0L, "routing", VersionType.INTERNAL, source);
+    private static IngestDocument createIngestDoc(Map<String, Object> source) {
+        return new IngestDocument("index", "id", 0L, "routing", VersionType.INTERNAL, new HashMap<>(source));
     }
 }