You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomcat.apache.org by Christopher Schultz <ch...@christopherschultz.net> on 2018/01/17 17:05:52 UTC

Thread-safety with sessions

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

All,

I have a use-case related to caching where I need to make sure that an
operation only happens one time with respect to an object in the
session. Basically, I want to build a cache and put it into the
session, but it needs to be thread-safe enough that two threads can't
see the object isn't there, build such an object, and then put it into
the session (thereby overwriting each other).

This is not a theoretical problem... we are observing threads
overwriting each other's caches in the session.

The good news is that I only have to deal with a single container
instance on a single server/JVM, so there is no concern about session
replication or anything else like that. I also don't have to worry
about the session being created/destroyed around the same time.

The servlet API says that HttpServletRequest.getSession() returns an
HttpSession object (well, an object implementing HttpSession), but
makes no guarantees about its thread-safety (e.g. with respect to
getAttribute/setAttribute calls), so I cannot guarantee anything about
whether getAttribute/setAttribute calls will be safe, though they
would really have to be otherwise the server would encounter all kinds
of problems.

That doesn't really matter because I need to do something like this:

Cache myCache = null;
synchronized (something) {
  myCache = session.getAttribute("cacheKey");
  if(null == myCache) {
    myCache = new Cache();
    session.setAttribute("cacheKey", cache);
  }
}

If I don't synchronize on a consistent monitor, there is a race
condition between the "get" and the "set" between threads using the
session.

So the question is "what should I use as the monitor?"

My first thought was that I should use the session object itself:

Cache myCache = null;
synchronized (session) {
  myCache = session.getAttribute("cacheKey");
  if(null == myCache) {
    myCache = new Cache();
    session.setAttribute("cacheKey", cache);
  }
}

That would be the best option, since it's the object I actually care
about. However, I believe Tomcat sometimes (always) provides wrapper
objects around servlet-spec-defined objects and I'm wondering if there
are any guarantees about the HttpSession object being consistent
across threads?

That is, will Tomcat always give an identical object to two different
threads whose requests are associated with the same session? Or are
there cases where the two threads might get different objects?

Thanks,
- -chris
-----BEGIN PGP SIGNATURE-----
Comment: GPGTools - http://gpgtools.org
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iQJRBAEBCAA7FiEEMmKgYcQvxMe7tcJcHPApP6U8pFgFAlpfgnAdHGNocmlzQGNo
cmlzdG9waGVyc2NodWx0ei5uZXQACgkQHPApP6U8pFi1fhAAtvyxHgkWCVPioxVR
eeXMg5LUg8bmGlcDTsGJQiIYhsParaXoL+Q8okMWGoS1bj7+jdEck4ttOBamM/iz
oT9X3SACFqoDoEGq9BDqxI3yWRUpVn1a5qwJCBgwdbWOxzrWeJMP/QFCjU2d2x7p
Fw50qkpNx33of+/hxXIRIHhfLoblK1cF/B/Vdvdz8QStYyU1jVOSgUGmFAU+xNG+
akUXOTdppp+xyf/mkkwO7BeUDrSMXt32JkPhyCx/BdQ29YAXSpueYHs2+V4D66rU
S44+N9I9FogJsN3xIbT+vMLxu26abRIZ+kMKMgdDZ8F/AfrK7J5/OVfZYcxr1xia
uPaPOgAVeZcPi2eRCjwYmc60DzbDenUxqm+eGxIJiCB7BB2PvlA06497YnDNRu0H
ipQxezLz3mHfICLrqn5fNY0HKzrWAqwZ4rlyQA3myUsWKvkHsrdmt+qFIcU8G439
Sq8lcQTEDhQxjFOzLpHqwjIlrlgFA3895INTIX1qhcEiqQ7QUMBJHiLQ1NtQhV8c
R8uAsBLSq0y2FwYVkY40lPJYxday5QVj07mwwWv+wQl6liT3xgJRC58i/7S2uJ73
Ag4tZ1QdoS4A/xIX68OcLQUGB0/9J62q/OACHdIFc6ma37M5vj0fxesYa1m/eNXZ
5MV+CLu7VlmTZTqQJmgw5JWGvI0=
=ENOR
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: Thread-safety with sessions

Posted by Mark Thomas <ma...@apache.org>.
On 19/01/18 20:40, Christopher Schultz wrote:
> Mark,
> 
> On 1/19/18 3:04 AM, Mark Thomas wrote:
>> On 18/01/18 20:11, Christopher Schultz wrote:
>>> Speaking of "expensive" objects, we do have a "user" object in
>>> the session. If the user isn't there, we throw all kinds of
>>> exceptions and don't let "users" actually do anything except go
>>> to their login pages.
>>>
>>> So I expect that I can (a) always rely on a "user" attribute
>>> being in the session and (b) I can use that as my monitor.
> 
>> I'd check how it is wrapped before you do that. From memory, I'm
>> not sure the same wrapper will always be used.
> 
> That "user" attribute is application-defined. I don't believe Tomcat
> wraps any application objects that are stored in the session, does it?
> Or did I misunderstand your statement?

No. I mis-understood your use of 'we'. I thought you meant Tomcat.

Tomcat won't wrap an application defined objects.

Mark

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: Thread-safety with sessions

Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Mark,

On 1/19/18 3:04 AM, Mark Thomas wrote:
> On 18/01/18 20:11, Christopher Schultz wrote:
>> Speaking of "expensive" objects, we do have a "user" object in
>> the session. If the user isn't there, we throw all kinds of
>> exceptions and don't let "users" actually do anything except go
>> to their login pages.
>> 
>> So I expect that I can (a) always rely on a "user" attribute
>> being in the session and (b) I can use that as my monitor.
> 
> I'd check how it is wrapped before you do that. From memory, I'm
> not sure the same wrapper will always be used.

That "user" attribute is application-defined. I don't believe Tomcat
wraps any application objects that are stored in the session, does it?
Or did I misunderstand your statement?

- -chris
-----BEGIN PGP SIGNATURE-----
Comment: GPGTools - http://gpgtools.org
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iQJRBAEBCAA7FiEEMmKgYcQvxMe7tcJcHPApP6U8pFgFAlpiV6AdHGNocmlzQGNo
cmlzdG9waGVyc2NodWx0ei5uZXQACgkQHPApP6U8pFjo3Q//f26EdB+Xc5BP+P2x
HaYL4/U/re+ZobWVm1RDCl0MZtyouD8D6IugH+/G4HTDtHE2XB/M/8uin/wCfnbR
ay96rMgpJfbqjNjLf407dafIyWbrF5gwaefnpXvYurLr1XPOIaTbFA4Ky1wTlbMG
3xPtfLL8JbVFExINvLy/JWnKhigkKP76ANiT31rQC6VCA3CGNIFgFFogtI8FcSoD
Z3ZYbJJAGJcOUX5xQoVYa0kvtnHgtIlRan69gZIds6uZSskjjiUlc/Hbe/U0Tdlq
u01MM66cpoy6+tUnoIoy74jeNwiUu57q03WvUKI7DSxMBXW07aG2M2itfv9iqpYq
zQJZsWUal7eQc9p6jwPwpXDc0Swk/hTT1Ya81goBxLfnYAKplP8ujgzpQCc0e9Q+
TZ9hpAGEWYdkt3Jh0jfbUrYJmXjeEFxoPZIIZciRZx7cWoGpSp5luysF4dFSzaYU
PuhNSP1s7JY6b+4PE8sew71mOKuou2hwhjytbtt1Q4IBN9QPYJEA4AApy9yvhjmX
Y8Ow+3IZO0TuE6dnYcMzgB9L2qijc/8grsrZUXMZo4HIKdTxoO32aa+hMH1AOgrF
72Eppb+zhUzkss6CYTCN3mSq4ISSQ/zO7Io1vMT3ZpOX2fBkFwIsT2cYsYw8CGeJ
0tutnK+v4JYedEN4ttJsecfKLfk=
=tYJe
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: Thread-safety with sessions

Posted by Mark Thomas <ma...@apache.org>.
On 18/01/18 20:11, Christopher Schultz wrote:
> Olaf,
> 
> On 1/18/18 1:28 PM, Olaf Kock wrote:
> 
>> On 18.01.2018 06:37, Christopher Schultz wrote:
>>> -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256
>>>
>>> Mark,
>>>
>>> On 1/17/18 4:31 PM, Mark Thomas wrote:
>>>> On 17/01/18 17:05, Christopher Schultz wrote:
>>>>> All,
>>>>>
>>>>> I have a use-case related to caching where I need to make
>>>>> sure that an operation only happens one time with respect to
>>>>> an object in the session. Basically, I want to build a cache
>>>>> and put it into the session, but it needs to be thread-safe
>>>>> enough that two threads can't see the object isn't there,
>>>>> build such an object, and then put it into the session
>>>>> (thereby overwriting each other).
> 
>> ...
>>>>> Yeah, I figured that. Otherwise, multi-threading basically
>>>>> couldn't happen. I'm surprised the servlet spec doesn't
>>>>> formally guarantee this because practically speaking, it's a
>>>>> requirement.
>> ....
>>>> It should always be the same object. Looking at the code, I
>>>> don't see any obvious holes.
>>>>
>>>> For the record, relevant code includes: 
>>>> o.a.catalina.connector.Request.getSession() 
>>>> o.a.catalina.session.ManagerBase.createSession(String) 
>>>> o.a.catalina.session.StandardSession.getSession()
>>> Could this be something we could formally guarantee (via 
>>> documentation)? I'd like to lobby the servlet EG for the same 
>>> guarantee, but getting it done in Tomcat is more immediately
>>> valuable for me :)
>> I'm not sure that this can be done in an unambiguous way,
>> especially not from the spec side: Assuming that objects might be
>> serialized out to disk (for memory reason, unused in 15 minutes but
>> not yet expired, cluster distribution etc) this would lead to quite
>> a few disambiguities. You say that your object creation should
>> happen once per session - is that per session AND machine? If
>> creation of that object has side effects, or the object itself has
>> state, your scenario might only be safe as long as there's no
>> clustering / session replication involved, but can't be assumed
>> when it is.
> 
> I would think the language would be something like "session objects
> within the JVM shall be == equals to each other when requested from
> the container" but I didn't think about the "serializing to [eg] disk"
> scenario. If you have a session obtained from the container, then have
> a long-running request or websocket handler and the container
> meanwhile persists the session elsewhere, some other thread would
> almost certainly get an object that is != the session object in the
> other thread.
> 
> Hmm. I wonder if that is a problem, now, for Tomcat.
> 
> The container is only "maintaining" a single session object ob behalf
> of its worker threads... if an application retains a reference they
> can get out of sync. There is no mechanism for the application to tell
> the container "hey, I need this session for quite a while into the
> future" and so the container might not know it is "in use".
> 
> As for long-running HTTP requests, I believe Tomcat knows whether the
> session is "currently" in use be a long-running request.

org.apache.catalina.session.StandardSession.ACTIVITY_CHECK

> But I'm not
> sure about WebSocket... that may depend upon exactly how the
> websocket-based service actually uses the session and/or obtains it.

WebSocket is a another issue. It needs special handling as WebSocket
requests do not maintain the HTTP session. That was a deliberate
decision of the WebSocket EG.

> For me, it doesn't matter: I have no session-persistence (except
> across restarts), I have no clustering, and I have no websocket usage.
> So at this point, this is an academic exercise for me.
> 
>> I'd say your safest bet is to validate the current
>> implementation's behavior - e.g. your exact version of tomcat - and
>> validate that it doesn't change in future versions. Based on Mark's
>> comments it doesn't look like this is an area where tomcat's
>> implementation would change soon. I'd just not expect it to be
>> over-specified in the servlet spec.
> 
> Given all of the possible complications, here, I think you are
> probably correct. It does present a problem for anyone who wants to do
> things like ensure thread-safety between invocations of server-side
> code using a shared session.
> 
> It seems I'm not the only one curious about this:
> 
> https://stackoverflow.com/questions/9802165/is-synchronization-within-an
> -httpsession-feasible
> 
> http://yet-another-dev.blogspot.com/2009/08/synchronizing-httpsession.ht
> ml
> 
>> There's another argument that the servlet level might be the wrong
>> level for creation of expensive objects, and that it's better
>> delegated to the business layer. I can't judge that in your case,
>> but quite often I've seen the servlet layer overly used for
>> business problems. I don't like this a lot - your problem might be
>> trivial to solve one or two layers down.
> 
> Speaking of "expensive" objects, we do have a "user" object in the
> session. If the user isn't there, we throw all kinds of exceptions and
> don't let "users" actually do anything except go to their login pages.
> 
> So I expect that I can (a) always rely on a "user" attribute being in
> the session and (b) I can use that as my monitor.

I'd check how it is wrapped before you do that. From memory, I'm not
sure the same wrapper will always be used.

Mark

> As for where the "servlet level" or "business level" is the right
> place to do these things... the lines are blurred when using e.g. a
> web framework (e.g. Spring, Struts, etc.) since you are using objects
> provided by the "servlet layer" (e.g. HttpSession) though you may be
> using them through a particular interface -- like java.util.Map
> instead of HttpSession.
> 
> In these cases, there *must* be a wrapper object and you may or may
> not be using the same one across threads. So it seems like the object
> stored *in* the session will pretty much always be a safer monitor
> than pretty much anything else you could use.
> 
> The only reason this will work for me (100% bulletproof) is because
> the "user" object is guaranteed to be present already. If I needed to
> create a new object in the session to monitor, I'd need to either use
> a HttpSessionListener to insert a "session monitor" into every session
> -- not a bad idea -- or use a complicated series of synchronized
> blocks using other monitors to bootstrap things to be absolutely sure
> I didn't create (and use) two separate monitor objects in two
> different threads.
> 
> In this specific use-case I mentioned initially, a page is showing a
> handful of <img> elements each of which make a request back to the
> server for some dynamic data. All of that data benefits from a
> session-backed cache and, because all of the requests come at the same
> time from the client, there is great opportunity for race conditions.
> 
> Thanks,
> -chris
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: users-help@tomcat.apache.org
> 


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: Thread-safety with sessions

Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Olaf,

On 1/18/18 1:28 PM, Olaf Kock wrote:
> 
> On 18.01.2018 06:37, Christopher Schultz wrote:
>> -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256
>> 
>> Mark,
>> 
>> On 1/17/18 4:31 PM, Mark Thomas wrote:
>>> On 17/01/18 17:05, Christopher Schultz wrote:
>>>> All,
>>>> 
>>>> I have a use-case related to caching where I need to make
>>>> sure that an operation only happens one time with respect to
>>>> an object in the session. Basically, I want to build a cache
>>>> and put it into the session, but it needs to be thread-safe
>>>> enough that two threads can't see the object isn't there,
>>>> build such an object, and then put it into the session
>>>> (thereby overwriting each other).
> 
> ...
>>>> Yeah, I figured that. Otherwise, multi-threading basically
>>>> couldn't happen. I'm surprised the servlet spec doesn't
>>>> formally guarantee this because practically speaking, it's a
>>>> requirement.
> ....
>>> It should always be the same object. Looking at the code, I
>>> don't see any obvious holes.
>>> 
>>> For the record, relevant code includes: 
>>> o.a.catalina.connector.Request.getSession() 
>>> o.a.catalina.session.ManagerBase.createSession(String) 
>>> o.a.catalina.session.StandardSession.getSession()
>> Could this be something we could formally guarantee (via 
>> documentation)? I'd like to lobby the servlet EG for the same 
>> guarantee, but getting it done in Tomcat is more immediately
>> valuable for me :)
> I'm not sure that this can be done in an unambiguous way,
> especially not from the spec side: Assuming that objects might be
> serialized out to disk (for memory reason, unused in 15 minutes but
> not yet expired, cluster distribution etc) this would lead to quite
> a few disambiguities. You say that your object creation should
> happen once per session - is that per session AND machine? If
> creation of that object has side effects, or the object itself has
> state, your scenario might only be safe as long as there's no
> clustering / session replication involved, but can't be assumed
> when it is.

I would think the language would be something like "session objects
within the JVM shall be == equals to each other when requested from
the container" but I didn't think about the "serializing to [eg] disk"
scenario. If you have a session obtained from the container, then have
a long-running request or websocket handler and the container
meanwhile persists the session elsewhere, some other thread would
almost certainly get an object that is != the session object in the
other thread.

Hmm. I wonder if that is a problem, now, for Tomcat.

The container is only "maintaining" a single session object ob behalf
of its worker threads... if an application retains a reference they
can get out of sync. There is no mechanism for the application to tell
the container "hey, I need this session for quite a while into the
future" and so the container might not know it is "in use".

As for long-running HTTP requests, I believe Tomcat knows whether the
session is "currently" in use be a long-running request. But I'm not
sure about WebSocket... that may depend upon exactly how the
websocket-based service actually uses the session and/or obtains it.

For me, it doesn't matter: I have no session-persistence (except
across restarts), I have no clustering, and I have no websocket usage.
So at this point, this is an academic exercise for me.

> I'd say your safest bet is to validate the current
> implementation's behavior - e.g. your exact version of tomcat - and
> validate that it doesn't change in future versions. Based on Mark's
> comments it doesn't look like this is an area where tomcat's
> implementation would change soon. I'd just not expect it to be
> over-specified in the servlet spec.

Given all of the possible complications, here, I think you are
probably correct. It does present a problem for anyone who wants to do
things like ensure thread-safety between invocations of server-side
code using a shared session.

It seems I'm not the only one curious about this:

https://stackoverflow.com/questions/9802165/is-synchronization-within-an
- -httpsession-feasible

http://yet-another-dev.blogspot.com/2009/08/synchronizing-httpsession.ht
ml

> There's another argument that the servlet level might be the wrong
> level for creation of expensive objects, and that it's better
> delegated to the business layer. I can't judge that in your case,
> but quite often I've seen the servlet layer overly used for
> business problems. I don't like this a lot - your problem might be
> trivial to solve one or two layers down.

Speaking of "expensive" objects, we do have a "user" object in the
session. If the user isn't there, we throw all kinds of exceptions and
don't let "users" actually do anything except go to their login pages.

So I expect that I can (a) always rely on a "user" attribute being in
the session and (b) I can use that as my monitor.

As for where the "servlet level" or "business level" is the right
place to do these things... the lines are blurred when using e.g. a
web framework (e.g. Spring, Struts, etc.) since you are using objects
provided by the "servlet layer" (e.g. HttpSession) though you may be
using them through a particular interface -- like java.util.Map
instead of HttpSession.

In these cases, there *must* be a wrapper object and you may or may
not be using the same one across threads. So it seems like the object
stored *in* the session will pretty much always be a safer monitor
than pretty much anything else you could use.

The only reason this will work for me (100% bulletproof) is because
the "user" object is guaranteed to be present already. If I needed to
create a new object in the session to monitor, I'd need to either use
a HttpSessionListener to insert a "session monitor" into every session
- -- not a bad idea -- or use a complicated series of synchronized
blocks using other monitors to bootstrap things to be absolutely sure
I didn't create (and use) two separate monitor objects in two
different threads.

In this specific use-case I mentioned initially, a page is showing a
handful of <img> elements each of which make a request back to the
server for some dynamic data. All of that data benefits from a
session-backed cache and, because all of the requests come at the same
time from the client, there is great opportunity for race conditions.

Thanks,
- -chris
-----BEGIN PGP SIGNATURE-----
Comment: GPGTools - http://gpgtools.org
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iQJRBAEBCAA7FiEEMmKgYcQvxMe7tcJcHPApP6U8pFgFAlpg/3UdHGNocmlzQGNo
cmlzdG9waGVyc2NodWx0ei5uZXQACgkQHPApP6U8pFjFbBAAqntwxxdqI5IH6wbe
f6RcSzUhrd1Z3UuA/i4hTNyrFyArlcIVcbtG++BF8iLXATarKvuxO8lLGZU24b4X
R20TziHbPDd8rHHn8uH1FC27SCvAL4YmdayerbI/wvxoUEDfi0cf5B8LooQPi5aI
VJ7cEI6+PzttrFlWP1B7Vugpb/NRu4ax5UfpagqIpPGad++e3EbcNnbCEXxsxeTF
/inC0H7HutsHDmBp/9a8PjGdR7eVxxtb0kbRyx17hirBeGdPLc39Cb64u7m4ti5G
OnE5ISqIMSyd9a4hJ3LfKS2iG+18FFCV3Da453A3y7iI3zB9Yz+FjzCXYjIfAhvY
qSb9r74z/Tz+D1ISz67LJyFRxwj0SQI3stdjlpuuTQkKEIoBrusFLzCHKSxXUsPH
SR4nYt4/UvsZoQnrKkcfsEOglFNsWWsb89HXLR0ARfIsKkrVjya/QoF0TRVO1mfy
qypOHT/B8wmPhJKZ4smYpmoKPrf//INNfEK1BZJT9+mLNCFjl6NHptEJInmDAIqz
N/7IrFOqpsGEbJk6s7j7+xM6Ob54e3O9qjgVxkeo0aJEfZEoc6ndqIw92UFzs8dI
Nw77WEGD6JNo/WSP+F6a2GI4c899tHX9hh8+g6W5OLgb4YqHwfG/I0kQlIJLHFuQ
PNX+1/HMK/0/g5stfTraHoEXKu8=
=FzXm
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: Thread-safety with sessions

Posted by Olaf Kock <to...@olafkock.de>.
On 18.01.2018 06:37, Christopher Schultz wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA256
>
> Mark,
>
> On 1/17/18 4:31 PM, Mark Thomas wrote:
>> On 17/01/18 17:05, Christopher Schultz wrote:
>>> All,
>>>
>>> I have a use-case related to caching where I need to make sure
>>> that an operation only happens one time with respect to an object
>>> in the session. Basically, I want to build a cache and put it
>>> into the session, but it needs to be thread-safe enough that two
>>> threads can't see the object isn't there, build such an object,
>>> and then put it into the session (thereby overwriting each
>>> other).

...
>>> Yeah, I figured that. Otherwise, multi-threading basically couldn't
>>> happen. I'm surprised the servlet spec doesn't formally guarantee this
>>> because practically speaking, it's a requirement.
....
>> It should always be the same object. Looking at the code, I don't
>> see any obvious holes.
>>
>> For the record, relevant code includes:
>> o.a.catalina.connector.Request.getSession()
>> o.a.catalina.session.ManagerBase.createSession(String)
>> o.a.catalina.session.StandardSession.getSession()
> Could this be something we could formally guarantee (via
> documentation)? I'd like to lobby the servlet EG for the same
> guarantee, but getting it done in Tomcat is more immediately valuable
> for me :)
I'm not sure that this can be done in an unambiguous way, especially not 
from the spec side: Assuming that objects might be serialized out to 
disk (for memory reason, unused in 15 minutes but not yet expired, 
cluster distribution etc) this would lead to quite a few disambiguities. 
You say that your object creation should happen once per session - is 
that per session AND machine? If creation of that object has side 
effects, or the object itself has state, your scenario might only be 
safe as long as there's no clustering / session replication involved, 
but can't be assumed when it is.

I'd say your safest bet is to validate the current implementation's 
behavior - e.g. your exact version of tomcat - and validate that it 
doesn't change in future versions. Based on Mark's comments it doesn't 
look like this is an area where tomcat's implementation would change 
soon. I'd just not expect it to be over-specified in the servlet spec.

There's another argument that the servlet level might be the wrong level 
for creation of expensive objects, and that it's better delegated to the 
business layer. I can't judge that in your case, but quite often I've 
seen the servlet layer overly used for business problems. I don't like 
this a lot - your problem might be trivial to solve one or two layers down.

Olaf



---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: Thread-safety with sessions

Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Mark,

On 1/17/18 4:31 PM, Mark Thomas wrote:
> On 17/01/18 17:05, Christopher Schultz wrote:
>> All,
>> 
>> I have a use-case related to caching where I need to make sure
>> that an operation only happens one time with respect to an object
>> in the session. Basically, I want to build a cache and put it
>> into the session, but it needs to be thread-safe enough that two
>> threads can't see the object isn't there, build such an object,
>> and then put it into the session (thereby overwriting each
>> other).
>> 
>> This is not a theoretical problem... we are observing threads 
>> overwriting each other's caches in the session.
>> 
>> The good news is that I only have to deal with a single
>> container instance on a single server/JVM, so there is no concern
>> about session replication or anything else like that. I also
>> don't have to worry about the session being created/destroyed
>> around the same time.
>> 
>> The servlet API says that HttpServletRequest.getSession() returns
>> an HttpSession object (well, an object implementing HttpSession),
>> but makes no guarantees about its thread-safety (e.g. with
>> respect to getAttribute/setAttribute calls), so I cannot
>> guarantee anything about whether getAttribute/setAttribute calls
>> will be safe, though they would really have to be otherwise the
>> server would encounter all kinds of problems.
> 
> [get|set]Attribute operate on a ConcurrentHashMap so they are 
> thread-safe

Yeah, I figured that. Otherwise, multi-threading basically couldn't
happen. I'm surprised the servlet spec doesn't formally guarantee this
because practically speaking, it's a requirement.

> but you still need to co-ordinate if multiple threads are trying to
> write.

Of course.

>> That doesn't really matter because I need to do something like
>> this:
>> 
>> Cache myCache = null; synchronized (something) { myCache =
>> session.getAttribute("cacheKey"); if(null == myCache) { myCache =
>> new Cache(); session.setAttribute("cacheKey", cache); } }
>> 
>> If I don't synchronize on a consistent monitor, there is a race 
>> condition between the "get" and the "set" between threads using
>> the session.
>> 
>> So the question is "what should I use as the monitor?"
>> 
>> My first thought was that I should use the session object
>> itself:
>> 
>> Cache myCache = null; synchronized (session) { myCache =
>> session.getAttribute("cacheKey"); if(null == myCache) { myCache =
>> new Cache(); session.setAttribute("cacheKey", cache); } }
>> 
>> That would be the best option, since it's the object I actually
>> care about. However, I believe Tomcat sometimes (always) provides
>> wrapper objects around servlet-spec-defined objects and I'm
>> wondering if there are any guarantees about the HttpSession
>> object being consistent across threads?
>> 
>> That is, will Tomcat always give an identical object to two
>> different threads whose requests are associated with the same
>> session? Or are there cases where the two threads might get
>> different objects?
> 
> It should always be the same object. Looking at the code, I don't
> see any obvious holes.
> 
> For the record, relevant code includes: 
> o.a.catalina.connector.Request.getSession() 
> o.a.catalina.session.ManagerBase.createSession(String) 
> o.a.catalina.session.StandardSession.getSession()

Could this be something we could formally guarantee (via
documentation)? I'd like to lobby the servlet EG for the same
guarantee, but getting it done in Tomcat is more immediately valuable
for me :)

- -chris
-----BEGIN PGP SIGNATURE-----
Comment: GPGTools - http://gpgtools.org
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iQJRBAEBCAA7FiEEMmKgYcQvxMe7tcJcHPApP6U8pFgFAlpgsRkdHGNocmlzQGNo
cmlzdG9waGVyc2NodWx0ei5uZXQACgkQHPApP6U8pFhZCw//REi7247L3Z03DyRq
D/JTRgafh9yiuf5hMAT1TldPHmfSn0CS6dIA+BqNItCs1ESytgjufh3koqFwbIz0
YxAlXZf7A7Vy+tLdx+3GUHZJSdbgq7HmFnAX5ATnaLiXPYk/uLAHo1+fh1LKCvev
Jn1CNCmPItY8nFMR6L/qiOvhRIfjgktez66+tyns+ECRvgya4fMF10FoqK7rEbuP
P3M3UQ1AS6+fnf3BzkYbpjjkKT92nNwL1Nj0XFp8ucZgc3rmYGcfbtyneDfQc9VZ
NumB5T5r6cJ1hyZuEkT16CD95l1XYe7aPbbWDB7sL1DBnXPVgs8hCyRlyG7ggYmo
IYPIAqiAjsE8idLz+diUdr7TGe40qyfyQnFW8D2HCB34EnilieE6nUwNou1zTW3q
pzFS38s3ZN9xAMK+gesoQcrvKRZe1w1I1HFCISykK1pKbphMrg7ay6SEnyp4OFz5
LylyH0I1ybQeh64zSsFyftNO+LVL6W5uxigrr0lS9p4njP65WHtD1EXX899WlfBP
Cr3wLX9YOMv7I+t5jLxQktDrKmfk6MLA2cW1jbN18EdYv/IgEhmTgWiFXwbcUT02
d0fe0AtZ0HZjDc71GtZSA4TtrebjH+iRN7sTP8Ky1qcOiNjIcstay9MHbXrSyV5C
uSPruAvm1TksxFVIpKx5saCyOeI=
=fSBn
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: Thread-safety with sessions

Posted by Mark Thomas <ma...@apache.org>.
On 17/01/18 17:05, Christopher Schultz wrote:
> All,
> 
> I have a use-case related to caching where I need to make sure that an
> operation only happens one time with respect to an object in the
> session. Basically, I want to build a cache and put it into the
> session, but it needs to be thread-safe enough that two threads can't
> see the object isn't there, build such an object, and then put it into
> the session (thereby overwriting each other).
> 
> This is not a theoretical problem... we are observing threads
> overwriting each other's caches in the session.
> 
> The good news is that I only have to deal with a single container
> instance on a single server/JVM, so there is no concern about session
> replication or anything else like that. I also don't have to worry
> about the session being created/destroyed around the same time.
> 
> The servlet API says that HttpServletRequest.getSession() returns an
> HttpSession object (well, an object implementing HttpSession), but
> makes no guarantees about its thread-safety (e.g. with respect to
> getAttribute/setAttribute calls), so I cannot guarantee anything about
> whether getAttribute/setAttribute calls will be safe, though they
> would really have to be otherwise the server would encounter all kinds
> of problems.

[get|set]Attribute operate on a ConcurrentHashMap so they are
thread-safe but you still need to co-ordinate if multiple threads are
trying to write.

> That doesn't really matter because I need to do something like this:
> 
> Cache myCache = null;
> synchronized (something) {
>   myCache = session.getAttribute("cacheKey");
>   if(null == myCache) {
>     myCache = new Cache();
>     session.setAttribute("cacheKey", cache);
>   }
> }
> 
> If I don't synchronize on a consistent monitor, there is a race
> condition between the "get" and the "set" between threads using the
> session.
> 
> So the question is "what should I use as the monitor?"
> 
> My first thought was that I should use the session object itself:
> 
> Cache myCache = null;
> synchronized (session) {
>   myCache = session.getAttribute("cacheKey");
>   if(null == myCache) {
>     myCache = new Cache();
>     session.setAttribute("cacheKey", cache);
>   }
> }
> 
> That would be the best option, since it's the object I actually care
> about. However, I believe Tomcat sometimes (always) provides wrapper
> objects around servlet-spec-defined objects and I'm wondering if there
> are any guarantees about the HttpSession object being consistent
> across threads?
> 
> That is, will Tomcat always give an identical object to two different
> threads whose requests are associated with the same session? Or are
> there cases where the two threads might get different objects?

It should always be the same object. Looking at the code, I don't see
any obvious holes.

For the record, relevant code includes:
o.a.catalina.connector.Request.getSession()
o.a.catalina.session.ManagerBase.createSession(String)
o.a.catalina.session.StandardSession.getSession()

Mark

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: Thread-safety with sessions

Posted by Konstantin Kolinko <kn...@gmail.com>.
2018-01-17 20:05 GMT+03:00 Christopher Schultz <ch...@christopherschultz.net>:
>
> All,
>
> I have a use-case related to caching where I need to make sure that an
> operation only happens one time with respect to an object in the
> session. Basically, I want to build a cache and put it into the
> session, but it needs to be thread-safe enough that two threads can't
> see the object isn't there, build such an object, and then put it into
> the session (thereby overwriting each other).
>
[...]
>
> So the question is "what should I use as the monitor?"
>
> My first thought was that I should use the session object itself:
>
> Cache myCache = null;
> synchronized (session) {
>   myCache = session.getAttribute("cacheKey");
>   if(null == myCache) {
>     myCache = new Cache();
>     session.setAttribute("cacheKey", cache);
>   }
> }
>
> That would be the best option, since it's the object I actually care
> about. However, I believe Tomcat sometimes (always) provides wrapper
> objects around servlet-spec-defined objects and I'm wondering if there
> are any guarantees about the HttpSession object being consistent
> across threads?

I think that the best way is to implement a listener
https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSessionListener.html

There is "sessionCreated" event that can be used to create the cache
or any kind of a lock object for the cache (e.g. an AtomicReference),

See also other HttpSession* listeners in that package,
https://docs.oracle.com/javaee/7/api/javax/servlet/http/package-summary.html

Best regards,
Konstantin Kolinko

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org