[wildfly-dev] EJB over HTTP
David M. Lloyd
david.lloyd at redhat.com
Wed May 4 14:12:17 EDT 2016
On 05/04/2016 12:51 PM, Jason Greene wrote:
>
>> On May 4, 2016, at 9:11 AM, David M. Lloyd <david.lloyd at redhat.com> wrote:
>>
>> One thing I noticed is that you went a different way with async
>> invocations and cancellation support.
>>
>> The way I originally proposed was that the request/response works as per
>> normal, but a 1xx header is used to send the client a cancellation token
>> which can be POSTed back to the server to cancel the invocation. I
>> understand that this approach requires 1xx support which some clients
>> might not have.
>>
>> In your proposal, the async EJB request always returns immediately and
>> uses an invocation ID which can later be retrieved. I rejected this
>> approach because it requires the server to maintain state outside of the
>> request - something that is sure to fail.
>
> I don’t think this is too big of a deal, I mean you just have a data structure with a built-in automated expiration mechanism. However, it does have a negative in the its naturally racy, which I guess is your concern?
I know it's "just" have a data structure, but it's overhead and
complexity that the server doesn't need and shouldn't have, and it's one
more knob for users to screw around with to trade off between memory
usage and error behavior. I'd prefer any approach that doesn't have
state maintained by a timeout and can never fail due to misconfiguration
of the server, and has deterministic cleanup properties.
Cancellation is always naturally racy but both of my proposals also
address this: cancellation is idempotent and unrecognized IDs are
ignored. The cancellation result is sent back on the original request:
it either receives a 200 or 204 indicating success (with or without a
response body), or it receives a 408 indicating that the request was
cancelled cleanly. The client can ping the server with cancellation
POSTs as much as it wants without mucking up the state of the
invocation, prior cancel requests, or future cancel requests.
>> Also the client doesn't
>> really have any notion as to when it can GET the response: it would have
>> to do it more or less immediately to avoid a late response (or when
>> Future.get() is called), meaning you need two round trips in the common
>> case, which is not so good.
>
> Yeah I think a key aspect of this is the client has to be able to say it wants blocking behavior, even if the server side is mapped asynchronously.
I think this misses the complete picture, see below.
>>
>> I think that the best compromise solution is to treat async invocations
>> identically to regular invocations, and instead let the *client* give a
>> cancellation ID to the server, which it can later POST to the server as
>> I described in my original document. If the server receives the
>> client's ID (maybe also matching the client's IP address) then the
>> request can be canceled if it is still running.
>
> I think thats a reasonable way to do it, provided the ID is sufficiently unique to not be repeated. Actually with HTTP 2, we could just hang-up the stream for cancellation, as the stream-id is an effective invocation id. However, it’s perhaps best to keep consistent semantics between h1 and h2.
>
> I think a key question, which I don’t have the answer for, is if we need to support more concurrent long-running invocations than connections(h1)/streams(h2). If the answer is yes then long polling is bad. I am also slightly worried about HTTP intermediaries imposing timeouts for long running operations.
To me this is a configuration concern: the same issues arise for
REST-ish things. It's not really long polling per se (which usually
corresponds to using potentially infinitely long connections to
accommodate asynchronous requests or messages from server to client,
something we do not do), just a (possibly) long request (but no longer
than any typical REST request on a potentially slow resource). We don't
want or need a special one-off solution to this (general, long-standing)
issue in the context of EJB; any solution to this problem should be
general to all long requests.
The point of my proposals are that there is absolutely no need to
conflate the HTTP request lifecycle with EJB asynchronous invocation
semantics, and in fact doing so is actively harmful. All EJB request
types - sync and async - have a request and reply and conceptually could
be cancellable (even in the sync case the client may invoke
asynchronously or be cancelled), except for one-way invocations which
would immediately return a 202 status anyway. The only difference is in
whether the client thread directly waits for the response or whether it
waits via a Future, which the client can independently determine based
on information it already has locally. By the time the invocation gets
to the transport provider, the asynchronicity of the request has lost
its relevance; the transport provider is merely responsible for
conveyance of the request and response and possibly the cancel request
message.
So really this is just about how we handle cancellation, and
cancellation is not a common case so we should optimize for requests
which aren't cancelled, which means one HTTP request and response per
EJB invocation with cancellation being somehow out-of-band.
--
- DML
More information about the wildfly-dev
mailing list