Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't throw exceptions during DocumentField serialization #95673

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,12 @@ public ToXContentFragment getValidValuesWriter() {
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
Copy link
Contributor

@quux00 quux00 Apr 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are catching a generic RuntimeException, we don't know if this was an intentional error (UnsupportedOperationException) or unintentional (NPE or other type of unexpected error, possibly indicating a code bug). Should we inspect the error and log it if it is not UnsupportedOperationException?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could make the placeholder value more explicit by including the exception message maybe? I'm not sure how frequent this issue actually is, or how useful an error message would be for something like a script field.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd vote for logging to help find rare bugs and avoid "swallowing" unexpected errors, but I'm still ramping up on standard practices here and don't have much context on this issue yet :-)
But this is just optional feedback.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having thought about this, I think I'd like to leave it as it is for now - really the only useful information we could return here would be a stack trace, which isn't really very user friendly. It shouldn't be too difficult to reproduce a problem if it comes up as we will have the document source.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this reminded me of Strings#toString, but there we catch IOException which is a checked exception. What are the exceptions that can happen in practice? I see that XContentBuilder#value may throw IllegalArgumentException. We could make the builder print out a more user-friendly error?

builder.value("<unserializable>");
}
}
builder.endArray();
return builder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ public void testToXContent() {
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(),
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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\\].*")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -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":
Expand Down