Bladeren bron

Don't throw exceptions during DocumentField serialization (#95673)

As a prerequisite to making search responses use chunked REST serialization,
we need to ensure that no exceptions are thrown when toXContent gets called.
DocumentFields can currently throw exceptions if their contained values are not
serializable. This commit changes the serialization code here to replace
unserializable values with a placeholder value rather than failing the whole request.

Relates to #95661
Alan Woodward 2 jaren geleden
bovenliggende
commit
636574904d

+ 6 - 4
server/src/main/java/org/elasticsearch/common/document/DocumentField.java

@@ -135,10 +135,12 @@ public class DocumentField implements Writeable, Iterable<Object> {
         return (builder, params) -> {
             builder.startArray(name);
             for (Object value : values) {
-                // This call doesn't really need to support writing any kind of object, since the values
-                // here are always serializable to xContent. Each value could be a leaf types like a string,
-                // number, or boolean, a list of such values, or a map of such values with string keys.
-                builder.value(value);
+                try {
+                    builder.value(value);
+                } catch (RuntimeException e) {
+                    // if the value cannot be serialized, we catch here and return a placeholder value
+                    builder.value("<unserializable>");
+                }
             }
             builder.endArray();
             return builder;

+ 10 - 0
server/src/test/java/org/elasticsearch/index/get/DocumentFieldTests.java

@@ -43,6 +43,16 @@ public class DocumentFieldTests extends ESTestCase {
         assertEquals("{\"field\":[\"ignored1\",\"ignored2\"]}", ignoredOutput);
     }
 
+    public void testUnserializableXContent() {
+        DocumentField df = new DocumentField(
+            "field",
+            List.of((ToXContent) (builder, params) -> { throw new UnsupportedOperationException(); })
+        );
+        String output = Strings.toString(df.getValidValuesWriter());
+        assertEquals("""
+            {"field":["<unserializable>"]}""", output);
+    }
+
     public void testEqualsAndHashcode() {
         checkEqualsAndHashCode(
             randomDocumentField(XContentType.JSON).v1(),

+ 1 - 0
x-pack/plugin/build.gradle

@@ -154,6 +154,7 @@ tasks.named("yamlRestTestV7CompatTransform").configure { task ->
   task.skipTest("sql/translate/Translate SQL", "query folding changed in v 8.5, added track_total_hits: -1")
   task.skipTest("service_accounts/10_basic/Test get service accounts", "new service accounts are added")
   task.skipTest("spatial/70_script_doc_values/diagonal length", "precision changed in 8.4.0")
+  task.skipTest("spatial/70_script_doc_values/geoshape value", "error message changed in 8.9.0")
 
   task.replaceValueInMatch("_type", "_doc")
   task.addAllowedWarningRegex("\\[types removal\\].*")

+ 7 - 6
x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/spatial/70_script_doc_values.yml

@@ -93,8 +93,11 @@ setup:
 
 ---
 "geoshape value":
+  - skip:
+      version: " - 8.8.99"
+      reason: output changed in 8.9
+
   - do:
-      catch: /illegal_argument_exception/
       search:
         rest_total_hits_as_int: true
         body:
@@ -103,10 +106,9 @@ setup:
               script:
                 source: "doc['geo_shape'].get(0)"
 
-  - match: { error.root_cause.0.reason: "cannot write xcontent for geo_shape doc value" }
+  - match: { hits.hits.0.fields.type.0: '<unserializable>' }
 
   - do:
-      catch: /illegal_argument_exception/
       search:
         rest_total_hits_as_int: true
         body:
@@ -115,10 +117,9 @@ setup:
               script:
                 source: "field('geo_shape').get(null)"
 
-  - match: { error.root_cause.0.reason: "cannot write xcontent for geo_shape doc value" }
+  - match: { hits.hits.0.fields.type.0: '<unserializable>' }
 
   - do:
-      catch: /illegal_argument_exception/
       search:
         rest_total_hits_as_int: true
         body:
@@ -127,7 +128,7 @@ setup:
               script:
                 source: "/* avoid yaml stash */ $('geo_shape', null)"
 
-  - match: { error.root_cause.0.reason: "cannot write xcontent for geo_shape doc value" }
+  - match: { hits.hits.0.fields.type.0: '<unserializable>' }
 
 ---
 "diagonal length":