-
Notifications
You must be signed in to change notification settings - Fork 25k
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
Compatible version of typed Index And Get API #53228
Compatible version of typed Index And Get API #53228
Conversation
1226 tests, 227 failures, 16ignored 23 / 2 failled in index/* failing CompatRestIT. test {yaml=index/70_mix_typeless_typeful/Index call that introduces new field mappings} CompatRestIT. test {yaml=index/70_mix_typeless_typeful/Index with typeless API on an index that has types}
Pinging @elastic/es-core-infra (:Core/Infra/Core) |
@@ -86,4 +100,30 @@ protected RestStatus getStatus(final GetResponse response) { | |||
}); | |||
} | |||
|
|||
public static class CompatibleRestGetAction extends RestGetAction { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Until we defined a better approach, can you start adding in something like this
assert Version.CURRENT.major == 8 : "REST API compatilbity for version 7 is only supported on version 8";```
import java.util.function.Consumer; | ||
import java.util.function.UnaryOperator; | ||
|
||
public class CompatibleHandlers { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a TODO in here to ensure we get some buy in on #52370 before we merge the header format.
ok to test |
Introduce module for compat code
code review follow up
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a couple comments from the types deprecation side. One high-level comment: we should ensure there's test coverage for all the deprecation messages we're adding back. In 7.x we had dedicated unit tests in the various actions -- here's an example for RestIndexAction
.
|
||
public class CompatibleHandlers { | ||
|
||
public static Consumer<RestRequest> consumeParameterType(DeprecationLogger deprecationLogger) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think renaming this to consumeTypeParameter
would make its purpose more clear. Also, could we just have a method void consumeParameterType(DeprecationLogger deprecationLogger, RestRequest request)
instead of using a Consumer
? Let me know if I'm missing something around why consumers are necessary, to me they make the logic less straightforward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree with both - method rename and making this a method.
Originally the reason for this being a consumer was because I was trying to fit in into the previous style of registering handlers. Extending the RestController.registerHandler with additional handlerWrappers. This is no longer needed after the recent refactoring made by Jay
private static final DeprecationLogger deprecationLogger = new DeprecationLogger(LogManager.getLogger(RestGetAction.class)); | ||
private static final String TYPES_DEPRECATION_MESSAGE = "[types removal] Specifying types in " | ||
+ "document get requests is deprecated, use the /{index}/_doc/{id} endpoint instead."; | ||
private static final Consumer<RestRequest> DEPRECATION_WARNING = r -> deprecationLogger.deprecatedAndMaybeLog( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we avoid introducing a Consumer
here and just call deprecationLogger.deprecatedAndMaybeLog(...)
directly inside prepareRequest
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure, that similarly to previous use of consumer was influenced by previous style of registering handlers.
a direct call is better here.
String TYPES_DEPRECATION_MESSAGE = "[types removal] Using type as a path parameter is deprecated."; | ||
|
||
return r -> { | ||
deprecationLogger.deprecatedAndMaybeLog("create_index_with_types", TYPES_DEPRECATION_MESSAGE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm understanding correctly, it seems like we'll log two deprecation messages per request, both the endpoint-specific one like "Specifying types in document get requests is deprecated..." and this more general one "Using type as a path parameter is deprecated". I think we could just stick with the endpoint-specific one to avoid warning twice about the same issue.
As a side note, this general deprecation message is also logged under the key "create_index_with_types", I think we'd want a more precise name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree - let's stick with just per endpoint warning.
WIth that I feel like we can even get rid of CompatibleHandlers.consumeParameterType
- it would only consume a type param - and the whole utility class.
I was worried that it would end up a bag of all the helpers for compatibility. It might generate a little bit of duplication, but there is no need for it now.
Do you mean more precise name for a deprecation key? Like to be the same for Get and Index and other handlers?
@@ -276,6 +280,9 @@ public void writeTo(StreamOutput out) throws IOException { | |||
@Override | |||
public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { | |||
builder.startObject(); | |||
if (builder.getCompatibleMajorVersion() == Version.V_7_0_0.major) { | |||
builder.field(TYPE_FIELD_NAME, SINGLE_MAPPING_TYPE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can just do the following, I've never had to wrap a string inside a Text
object:
builder.field(TYPE_FIELD_NAME, MapperService.SINGLE_MAPPING_NAME);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will do
@@ -107,4 +107,6 @@ public void testToXContentDoesntIncludeForcedRefreshUnlessForced() throws IOExce | |||
} | |||
} | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small comment, would be good to revert this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not an exhaustive review, but I have a few minor comments.
) { | ||
if (Version.CURRENT.major == 8) { | ||
return List.of( | ||
new RestGetActionV7(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't we never have more than one major versions worth of compat in a single version? If that is the case, why do we need to version the class names?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a narrow window where 2 versions can be present.
Let's say we released version 8 (or a feature freeze) and we are now already developing ES9 and there will be features compatible with v8.
We won't be able to remove the v7 compat features instantly so for some time there will be both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am +1 on using the version in the class for this reason, and for the obviousness they provide. While (arguably) ugly, these classes are special in that they are not intended to be maintained, rather they are intended to be deleted after a some point in time. Having the version in the name makes is more obvious and helps to prevent accidental modifications (like from a simple class search) and makes it really obvious if these things exist for too long.
classname 'org.elasticsearch.rest.compat.RestCompatPlugin' | ||
} | ||
|
||
integTest.enabled = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see tests in this PR; why are they disabled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
they should be enabled. fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually there are no integTests, but there are unit tests so integTests.enabled = false tests.enabled=true
@@ -276,6 +278,9 @@ public void writeTo(StreamOutput out) throws IOException { | |||
@Override | |||
public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { | |||
builder.startObject(); | |||
if (builder.getCompatibleMajorVersion() == Version.V_7_0_0.major) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are just looking at major version number, using the Version constant just to extract "7" seems overkill and less readable than just using the literal 7
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this will help the removal of compatible code. We have not decided on what mechanism to use in order to help finding this code.
The plan at the moment is to put as much code v7-compatible into module/rest-compatibility
Using Version.V_7_0_0.major would make sure that if the version is removed after major upgrade it will also highlight the places where v7-compatible code has to be removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, the removal of the of the Version.V_7_0_0 constant in the code (for v9) will cause this to fail compilation. Which is likely the correct the behavior. It does indeed make major version bumps harder (one more thing that breaks), but for now I think this is the best we can do. It feels like there is something that can be done to ease that upgrade pain, but that is outside the scope of this PR.
@@ -340,6 +349,11 @@ private void tryAllHandlers(final RestRequest request, final RestChannel channel | |||
handleBadRequest(uri, requestMethod, channel); | |||
} | |||
|
|||
public static boolean isV7Compatible(ToXContent.Params params) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this V7 specific? Aren't the compatible params version agnostic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point, fixed
This reverts commit a0fce89.
@pgomulka - looking good. I have question about error handling and the combinations of Indexing with types and compatibility mode requires both For example this works just fine:
However, I think we could be more helpful for when it appears compatility mode is request. For example the following (correctly) doesn't work :
I think the above cases are reasonable attempts to trigger compatibility mode, but for (arguably pedantic) reasons don't. I think these should error in a meaningful way, maybe a 406 with a description stating that you need to set both the I think the logic would be be if no body required on the request then only To put it another way if the Also we may want to surface a warning in the logs around poorly requested no-body requests. For example (the
Also, for a valid request with correct header combinations, but invalid version, I believe that too should be an 406 error. For example:
Thoughts ? Also, I am fine if this happens in a follow up PR. |
@jakelandis totally agree with all points raised. I wonder if we should not do this in a separate PR. Will raise it shortly. |
ok to test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Let's get this in the feature branch as the foundation for other compatibility changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't do an exhaustive review so I won't give an explicit approval, but all my earlier comments have been addressed. Thanks!
Adding validation of Accept And Content-Type headers. The idea is to verify combinations of presence and values of both headers depending if a request has a content, headers are versioned (type/vnd.elasticsearch+subtype;compatible-with) . It also changes the way media type is parsed (previously always assuming application as a type i.e application/json) It should expect different types like text - used in SQL Not adding a compatible headers for _cat api. These in order to return a default text do not expect Accept header (Content-type is not needed as content is not present) See #53228 (comment) for more context
Providing a REST infrastructure to handle compatible APIs. Rest APIs that were removed in 8 will be available only if an HTTP request had a compatible header. At the moment is is expecting an Accept header to contain
application/vnd.elasticsearch+json;compatible-with=7
. This might be changed though when #52370 is resolved.The REST changes contain
enriching RestRequest with a parameter
Compatible-With
. The value is taken fromAccept
header. See RestRequest creation. Used when deciding if a request to a compatible handler can be dispatched. See RestController.//TODO this at the moment can be simplified to only use a value from a header. However in rest layer we often have only access to Params, so headers won't be accessible when implementing some compatible code
enriching XContentBuilder with a compatible version value. See AbstractRestChannel. Used when shape of a response is changed. see DocWriteResponse and GetResult
MediaType parsing change - see XContentType. because we expect a different media type now
application/vnd.elasticsearch+json;compatible-with=7
(when previously application/json or similar)testing changes - compat tests will have a compatible header set. see AbstractCompatRestTest.java
Compatible rest handlers - based on RestIndexAction and RestGetAction. See modules/rest-compatibility. These extend already existing handlers and register them in a RestCompatPlugin under a typed paths. They will only be accessible when a compatible header is present (use of compatibilityRequired method from RestHandler interface)
Without this PR 283 tests from 7.x are failing. - rest api spec tests only
with this PR 228 tests are passing - with almost all tests from index/* package passed (missing 2 tests that require change for include_type_param, to be added in a new PR)
relates #51816