cdi icon indicating copy to clipboard operation
cdi copied to clipboard

New Scope SessionCacheScoped

Open beikov opened this issue 4 years ago • 11 comments

I would like to propose a new scope called @SessionCacheScoped with an optional cache name. The purpose of the scope is, that data is associated with a session without using the HttpSession as storage. I often associate some data that I loaded from the database with a user session and using @SessionScoped is usually fine, but in a clustered setup this becomes a problem due to session replication overhead. To avoid these issue, I would like to put the beans into a local cache instead which is associated with the session.

One might think that this kind of scope should be provided by JCache(JSR 107), but since it is not part of Jakarta EE (yet), I wanted to post it here and maybe start a discussion about the possible inclusion of the JCache API.

beikov avatar Oct 15 '20 10:10 beikov

The purpose of the scope is, that data is associated with a session without using the HttpSession as storage.

So, would the scope still activate/deactivate based on session creation/timeout? In that case the lifecycle is basically identical to that of session scope but with some simple map backing it instead of HTTP session. Is that correct?

... but in a clustered setup this becomes a problem due to session replication overhead.

I don't see how the new scope you are proposing solves the issue of clustered setup? You will still need to propagate those beans in a cluster because otherwise you break any fail-over scenario. But to do that, you still need to have some replication which gets you back to square one.

I don't know your application, so this may be far fetched, but perhaps you could circumvent some of these issues by either not storing all information within the session bean, or using transient references?

manovotn avatar Oct 15 '20 12:10 manovotn

This new scope should be defined in the JCache spec.

mkouba avatar Oct 15 '20 13:10 mkouba

So, would the scope still activate/deactivate based on session creation/timeout? In that case the lifecycle is basically identical to that of session scope but with some simple map backing it instead of HTTP session. Is that correct?

Yeah, kind of. The storage would not be a "simple map" but a local JCache cache. The scope would use the bean name + some session identifier as cache key for beans.

I don't see how the new scope you are proposing solves the issue of clustered setup? You will still need to propagate those beans in a cluster because otherwise you break any fail-over scenario. But to do that, you still need to have some replication which gets you back to square one.

It solves the issue, by simply not replicating these beans. These beans in my case are provided through named producers. If a failover happens, it's no big deal if the producer is called again on a different node, as the data is transient. I still would like to associate it with the session though to have the same lifecycle.

beikov avatar Oct 15 '20 13:10 beikov

It solves the issue, by simply not replicating these beans.

Ok, now I see what you mean.

Yeah, kind of. The storage would not be a "simple map" but a local JCache cache. The scope would use the bean name + some session identifier as cache key for beans.

I am with @mkouba on this - it should land in JCache specification. Just like @Transactional sits in JTA and @ViewScoped in JSF.

manovotn avatar Oct 15 '20 13:10 manovotn

@mkouba, @manovotn

I am with @mkouba on this - it should land in JCache specification. Just like @Transactional sits in JTA and @ViewScoped in JSF.

Wait. In the sphere of JCache the concept of a "Session" is totally undefined.

@Transactional sits in JTA because it makes sense that the JTA team defines the semantics of such an annotation.

Should the JCache team define the semantics of an annotation for a web application? Or even do an implementation?

cruftex avatar Oct 15 '20 20:10 cruftex

AFAIU JSR107 depends on CDI and CDI defines the session scope. So if JCache defines a scope that relies on that session scope as defined by CDI, this should be fine, no?

beikov avatar Oct 15 '20 21:10 beikov

AFAIU JSR107 depends on CDI and CDI defines the session scope. So if JCache defines a scope that relies on that session scope as defined by CDI, this should be fine, no?

No it does not. The annotations that are defined in JCache are separate from injection. You can use them in Spring as well.

Also, in my opinion and in retrospective, defining the annotations within the same standard umbrella was a mistake. The annotations did not get much attention and vetting before the standard was released.

cruftex avatar Oct 15 '20 21:10 cruftex

@beikov

To avoid these issue, I would like to put the beans into a local cache instead which is associated with the session.

Why are you not just doing this? Just put the stuff into a local cache of your preference and use the session id as key. You only need a standard, where you must interact with the application server. For your purpose the session id seems sufficient.

Let us think not as a performance guy, but as a standard guy:

What does "local" mean w.r.t. the CDI standard? What would be the guaranteed / expected semantics of a @SessionCacheScoped annotation?

Answer: There is no local, AFAIK Since a cache may or may not store a value. The semantics would be, on a logic level: A valid implementation may always inject null or do the same as @SessionScoped or anything in between.

To be really useful more needs to be done:

  • How to refer to a (local) cache, or, can a cache name/cache reference and cache key be inferred
  • Define the concept of a local cache in the caching standard. Right now, in JCache you can basically specify an implementation explicitly or go with one default implementation. If you have the concept "local" and "not-local" you need to address a cache for each purpose in a general way.
  • Define how to resolve and configure a cache. JCache does not define how to specify a cache capacity or any external configuration or central cache configuration

For your use case to make sense I guess you are assuming sticky sessions across servers. Is it supposed to be local, only when sticky sessions are used?

cruftex avatar Oct 15 '20 22:10 cruftex

Why are you not just doing this? Just put the stuff into a local cache of your preference and use the session id as key. You only need a standard, where you must interact with the application server. For your purpose the session id seems sufficient.

Of course I can implement all of this myself through standard means. I can write a portable extension that adds a context which implements the scope like I described with JCache, but I would prefer if this were something that is provided by the platfrom if possible. Like I wrote, I also wanted to start the discussion about including JCache into Jakarata with this use case.

Currently, I have code like this:

@Produces
@Named("someBean")
@SessionScoped // I want @SessionCacheScoped
public List<SelectItem> getItems() {
  // load from database while using some user session attribute
}

If a bean is not available in the cache, the producer would be called to produce it and then put it into the cache. The only way to implement this differently, that I can think of, is to use @RequestScoped and implement the caching in the method or maybe through an interceptor, although I'm not sure if interceptors are applied to producer methods.

Since a cache may or may not store a value. The semantics would be, on a logic level: A valid implementation may always inject null or do the same as @SessionScoped or anything in between.

Sure, but to be useful it should store the value 😄

How to refer to a (local) cache, or, can a cache name/cache reference and cache key be inferred

My idea was to add an attribute to @SessionCacheScoped(cacheName = "someCache") and use a default cache if a name is not given.

Define the concept of a local cache in the caching standard. Right now, in JCache you can basically specify an implementation explicitly or go with one default implementation. If you have the concept "local" and "not-local" you need to address a cache for each purpose in a general way.

I thought that JCache was all about "just" local caches and that providers can implement caches as distributed ones as well, but maybe I misread the spec.

Define how to resolve and configure a cache. JCache does not define how to specify a cache capacity or any external configuration or central cache configuration

Resolving of a cache should be done by looking up a Cache by name in the CacheManager. The configuration is a different topic. I usually use Infinispan and configure it through the vendor specific format, but expose the caches through the JCache API. Not sure if JCache really needs to unify cache configuration.

For your use case to make sense I guess you are assuming sticky sessions across servers.

Sticky sessions would reduce the database load, but aren't strictly required.

Is it supposed to be local, only when sticky sessions are used?

This should not matter. It's up to the user to configure the cache. Some maybe want the cache to be just local, others might want cluster invalidations, but that is part of the cache configuration.

beikov avatar Oct 16 '20 07:10 beikov

What does "local" mean w.r.t. the CDI standard?

Maybe this should be in the Servlet spec, as this is the part that knows specifically about distributed items in the HTTP session. See https://jakarta.ee/specifications/servlet/5.0/jakarta-servlet-spec-5.0.html#distributed-environments

The fact that the current SessionScoped is in the CDI spec is IMHO a historical mistake. As mentioned above, @transactional sits in ~~JTA~~ Transactions and @ViewScoped in ~~JSF~~ Faces.

My idea was to add an attribute to @SessionCacheScoped(cacheName = "someCache") and use a default cache if a name is not given.

Unfortunately scopes can not have attributes officially:

https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#defining_new_scope_type

(But Faces violates this too, and works everywhere)

Otherwise perhaps @SessionScoped(distributable=false) could have been an option, together with an addition to the servlet container to mark single items in the session as not distributable.

arjantijms avatar Nov 09 '20 23:11 arjantijms

Well, I guess it depends how you want to use the scope. I'd consider a @SessionScoped bean, regardless if distributed, not to vanish suddenly because of memory pressure. Whereas a @SessionCacheScoped bean should IMO have the option be evicted from the backing cache if the JVM is low on memory, but that should be handled by the cache implemention.

beikov avatar Nov 10 '20 09:11 beikov