@SessionAttributes stores the session object specified by the following attributes in the Model for each method of the annotated Controller.
- names
- types
The process is implemented in SessionAttributesHandler#retrieveAttributes(WebRequest), but sometimes it is not reflected in the Model because it only looks at knownAttributeNames
.
The object specified in types
is not initially defined in knownAttributeNames
, but is added to knownAttributeNames
when DI by a controller method.
Instances of SessionAttributesHandler
are cached, so information added to knownAttributeNames
is preserved, but it is only valid within the same VM since it is not stored in the session.
Therefore, if different methods of the same Controller are executed on different servers, the expected behavior will not occur because the knownAttributeNames
state will be different.
e.g.) If handle1 and handle2 below are executed on different servers, Pet is not stored in handle2's model.
@Controller
@SessionAttributes(types = { Pet.class })
public class EditPetForm {
// ...
@PostMapping("...")
public String handle1(Pet pet, BindingResult errors) {
}
@PostMapping("...")
public String handle2(Model model) {
}
}
Defining the above in names
instead of types
will result in the expected behavior on different servers.
I do not think this behavior is what is expected.
Note that this problem does not occur when SessionAttributesHandler
is the same on all servers.
The problem is easily missed because it occurs immediately after a server restart when the SessionAttributesHandler
cache is different.
Comment From: snicoll
Therefore, if different methods of the same Controller are executed on different servers, the expected behavior will not occur because the knownAttributeNames state will be different.
I am confused. If you want to share the session between different servers, you should be using Spring Session and make sure that its state is shared accordingly. With that said, can you share a small sample that shows the problem you've described, with proper replication of the session (Spring Session or otherwise).
Comment From: yu-shiba
@snicoll Sorry for the poor explanation.
This issue assumes that we are using Spring Session for session management and using an external for Session Store. (I am using Redis in my environment). Also, what makes this issue confusing is,
- the relevant data exists in the Session Store
- it only occurs when
types
are specified in@SessionAttributes
- the difference in
knownAttributeNames
is absorbed over time
3 is particularly troublesome, making it difficult to notice problems in the production environment.
With that said, can you share a small sample that shows the problem you've described, with proper replication of the session (Spring Session or otherwise).
Correct. I'll try to make a sample.
Comment From: yu-shiba
@snicoll I made a sample. We will need to restart the application during the process. https://github.com/yu-shiba/spring-framework-reproduce-30463
Comment From: snicoll
Thanks for the sample. I want to acknowledge I've seen the sample but haven't had the time to dig into it. Perhaps this rings a bell for you @rstoyanchev?
Comment From: jhoeller
I've implemented this through storing a copy of the known attribute names in the actual session on storeAttributes
, restoring them in retrieveAttributes
. Such execution of closely related handler methods from the same class on different servers has not been a first-class scenario before but should work consistently as of 6.1.4 now.
Comment From: yu-shiba
Thank you for everyone's support. With this, we can deploy the application using types with confidence.