Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [servlet-dev] WebSocket and HttpSession



On Tue, 13 Jun 2023 at 17:41, Greg Wilkins <gregw@xxxxxxxxxxx> wrote:

Mark, Stuart,

The reason I was thinking about asynchronous access was for the case when a container has an aggressive passivation setting, so sessions are passivated either immediately (or soon) after request count goes to zero (or in some deployments after every request when the session instance is not shared between requests).    A websocket app might like to hold the session in memory for an extended period of time, which could be done with start/end methods, but not with an access(Consumer<Session>) approach... unless they block within the consumer.

But the flip side of this is that we really don't want websockets pretending to be really long request handling to subvert container configuration.   They'd be better off tuning the passivation settings rather than trying to trick them.  So, yeah, let's just go blocking.

I also thought about an AutoCloseable based API, but that doesn't provide the Session instance, so the websocket would still need to retain a reference to the session object itself, which I'd really like to avoid.

With regards to limitations from "current implementations of one container",  it is not so much what Jetty does, but how I have seen it used in many different deployments.   Request and Session objects are often wrapped and have resources associated with them.  The session management mechanism is also often extended so we have some big deployments implementing very specific session semantics that fill in the gaps of the specification.   So I agree that there is nothing much in the spec either way, on many of these issues, but I do think we have been pretty consistent in saying that references to objects provided by the container should not be kept beyond the request lifecycle.

Thus I do not think it would be good to suddenly after all these decades, suddenly say that it is OK to keep request and/or session references beyond the end of the request lifecycle.  Doing so is not going to just break "one container", but many applications/frameworks/deployments that have extended containers based on the non reference assumption.

This would definitely break WildFly/EAP clustering (attempting to use the session outside the scope of a request).

We could potentially just make this method optional, and allow containers to not implement it, or only support it for some session configurations. I don't think we would want to support this for clustered sessions in WildFly, clustering is complex and something like this would just add additional code paths that likely won't get the same level of testing as the main clustering code paths.

Stuart
 

However, we could add a method to the request or session to get an object that explicitly can be held long term on which the access can be called.     This is a bit ugly but would capture the semantic:

  Consumer<Consumer<Session>> Session.getAccessor()

I.e when asked, the session can provide a consumer of Consumer<Session> that can be kept long term.   When they want to access the session, then call that consumer with their Consumer<Session>, which is called back with the session instance.

We can also allow this method to return null, so that websockets know in advance they can't keep the session active (and fail or take some other action) rather than keeping a reference to the session and hoping it works.

cheers






 









 













On Mon, 12 Jun 2023 at 18:10, Mark Thomas <markt@xxxxxxxxxx> wrote:
On 12/06/2023 15:27, Greg Wilkins wrote:
>
> Mark,
>
> Something like that might be possible, but I don't really don't like
> many aspects of this proposal.

Fair enough. Happy to keep discussing until we find a consensus. My aim
is to try and find a solution to the problem.

> The Session object returned from request.getSession was never intended
> to be used beyond the scope of the request lifecycle.

I don't think the spec makes that clear one way or the other. I can see
how it could be read that way but I can also see the counter argument.

>  Putting
> startAccess/endAccess on the session API would be problematic for
> several of our implementations as we really don't like objects that have
> been passivated suddenly becoming active again. Doing so is just asking
> for races!   Nor are all our implementations intended to be shared
> between multiple requests, let alone other threads. Thus I think the API
> needs to be on something else other than the session.

I understand the points you are making. I worry slightly that we are
constraining the solution space to suit the current implementations of
one container but lets see if using some other object could work.

> Applications can call unmatched endAccess.

Fixing that was a detail I was intending to leave until later if the
general approach was agreeable.

>  It would be far better to
> have something like access(Consumer<Session>) that could be called with
> the activated session.  Hmmm but that is blocking.  Maybe something like
> access(BiConsumer<Session, Runnable>, with the Runnable being the end event.

I like the access(Consumer<Session>) suggestion.

Session access is currently blocking so I'm not overly concerned about
blocking vs non-blocking. In what scenarios do you see non-blocking
being useful here?

> All in all, session semantics are poorly defined enough for incoming
> HTTP requests, without adding this complication.

Hopefully we can improve the definition of the semantics. I do think
defining this behaviour it terms of an HTTP request - even if it isn't -
would aid simplicity. Something like:

For the purposes of session access, validity, passivation, activation,
etc. the actions taken during this method should be considered
equivalent to an HTTP request starting, performing the actions and then
finishing. (I'm sure that wording can be improved.)

> Actually that might be a better solution: let websocket use actual HTTP
> requests sent over a local connector to hit the application normally, so
> all normal session semantics will apply.  I.e. we should provide a
> simple way to create locally HTTP invocations.

That should be largely doable with the existing API. I don't really like
the idea of processing a full HTTP request just to update the session.
It seems rather inefficient.

Going back to your "do this via some other object" suggestion, did you
have anything in mind?

If we are going to add a access(Consumer<Session>) method then it needs
to be added to an object where the current session is implied or we need
to provide the session ID as well.

Currently the WebSocket HandshakeRequest doesn't have access to much.
The HttpSession looks like the most likely candidate. Something like

Xxxxxxx HttpSession.getXxxxxxx()

where new class Xxxxxxx has the method

Xxxxxxx.access(Consumer<Session>)

(I'm using Xxxxxxx as I can't think of a good name right now.)



Mark


>
> On Mon, 12 Jun 2023 at 12:38, Mark Thomas <markt@xxxxxxxxxx
> <mailto:markt@xxxxxxxxxx>> wrote:
>
>     On 09/06/2023 18:34, Greg Wilkins wrote:
>      > On Thu, 8 Jun 2023 at 17:58, Mark Thomas <markt@xxxxxxxxxx
>     <mailto:markt@xxxxxxxxxx>
>      > <mailto:markt@xxxxxxxxxx <mailto:markt@xxxxxxxxxx>>> wrote:
>      >
>      >
>      >     My proposal is to add the following method to HttpSession:
>      >
>      >     public void access()
>      >
>      >
>      > That may not be sufficient, because there are two states of a
>     session
>      > that websockets needs to avoid: invalidation and passivation!
>
>     I agree the proposal will not be sufficient for all scenarios. However,
>     if it is sufficient for some scenarios then I think it is worth
>     considering.
>
>      > Having an access method could well prevent invalidation due to an
>     idle
>      > timeout, but it will stop passivation if that is configured on the
>      > container.      Typically a session is passivated some time interval
>      > after the active request count goes to 0.  Often this timeout is
>     much
>      > shorter than the idle timeout and we have some deployments that
>     have a
>      > zero passivation timeout, so the session is passivated as soon as
>     the
>      > last request exits the servlet container.
>      >
>      > A passivated session will not be available to be accessed by a
>     websocket
>      > endpoint.
>      >
>      > There are further complications with websocket endpoint accessing
>      > sessions.  For example, what is the dirty semantic? i.e. if the
>      > websocket endpoint modifies the session, then when are those changes
>      > persisted/distributed in the cluster?  We HTTP request, we have
>     commit
>      > and complete events on which we can flush a dirty session.
>      >
>      > Session semantics is really poorly defined and barely sufficient for
>      > purpose for HTTP requests/responses.   I've very dubious that it's
>      > semantics can be extended to websockets without creating some more
>      > horrid corner cases.   Very careful thought is needed and I do
>     not think
>      > there are any simple solutions.
>
>     I agree a complete solution may not be simple. I'm happy to look at the
>     wider problem (we have a number of open issues against the Servlet spec
>     for those) but I'd also like to make progress on this WebSocket
>     issue if
>     we can.
>
>     I'm not sure I like it but something that could work would be two
>     methods:
>     public void startAccess()
>     public void endAccess()
>
>     These would be intended for non-HTTP based application components (e.g.
>     WebSocket) to work with the session. Roughly (details to be fleshed out
>     if the principal is acceptable):
>
>     - startAccess()
>         - container treats it as the start of an HTTP request
>         - active request count is incremented
>         - last access time is updated
>
>     - endAccess()
>         - container treats it as the start of an HTTP request
>         - active request count is incremented
>         - changes to the session are distributed/persisted
>
>     The idea being that if WebSocket (or anything else) wants to update the
>     session it needs to do so between startAccess() and endAccess().
>
>     Mark
>     _______________________________________________
>     servlet-dev mailing list
>     servlet-dev@xxxxxxxxxxx <mailto:servlet-dev@xxxxxxxxxxx>
>     To unsubscribe from this list, visit
>     https://www.eclipse.org/mailman/listinfo/servlet-dev
>     <https://www.eclipse.org/mailman/listinfo/servlet-dev>
>
>
>
> --
> Greg Wilkins <gregw@xxxxxxxxxxx <mailto:gregw@xxxxxxxxxxx>> CTO
> http://webtide.com <http://webtide.com>
>
> _______________________________________________
> servlet-dev mailing list
> servlet-dev@xxxxxxxxxxx
> To unsubscribe from this list, visit https://www.eclipse.org/mailman/listinfo/servlet-dev
_______________________________________________
servlet-dev mailing list
servlet-dev@xxxxxxxxxxx
To unsubscribe from this list, visit https://www.eclipse.org/mailman/listinfo/servlet-dev


--
_______________________________________________
servlet-dev mailing list
servlet-dev@xxxxxxxxxxx
To unsubscribe from this list, visit https://www.eclipse.org/mailman/listinfo/servlet-dev

Back to the top