0

I have this Spring webservice test code:

@RestController
@RequestMapping("/counter")
public class CounterController
{       
    @Autowired
    private Counter counter;    

    @RequestMapping(value = "/inc", method = GET)   
    public int inc() throws Exception {                     
        counter.incCounter();
        return counter.getCounter();
    }

    @RequestMapping(value = "/get", method = GET)   
    public int get() throws Exception {
        Thread.sleep(5000); 
        return counter.getCounter();                
    }

}

where Counter is a session scoped object

@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class Counter implements Serializable {  
    private static final long serialVersionUID = 9162936878293396831L;

    private int value;
    public int getCounter() {
        return value;
        }
    public void incCounter() {
        value += 1;
    }
}

The session configuration

@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds=1800)
public class HttpSessionConfig {    
    @Bean
    public JedisConnectionFactory connectionFactory() {
        return new JedisConnectionFactory();
    }    
    @Bean
    public HttpSessionStrategy httpSessionStrategy(){
        return new HeaderHttpSessionStrategy();
    }
}

As you can see the get() method sleeps 5 secons and returns the value of the counter. The problem is that if I call inc() many times during the execution of the get() all the counter changes are lost because when get() finishes returns the value of the counter that it has when started the execution. The weird problem is that get() when finishes persists the counter (It is a session object) and when this operation is done all the changes are lost. Does exist a way to prevent that functions that do not modify a session object not persist it?

Update: I think that the Spring code corroborates this wrong behavior. This snippet of code of the class ServletRequestAttributes shows that every session object that is accessed (regardless if the access is for read) is marked to be saved when the webservice operation finishes:

@Override
    public Object getAttribute(String name, int scope) {
        if (scope == SCOPE_REQUEST) {
            if (!isRequestActive()) {
                throw new IllegalStateException(
                        "Cannot ask for request attribute - request is not active anymore!");
            }
            return this.request.getAttribute(name);
        }
        else {
            HttpSession session = getSession(false);
            if (session != null) {
                try {
                    Object value = session.getAttribute(name);
                    if (value != null) {
                        this.sessionAttributesToUpdate.put(name, value);
                    }
                    return value;
                }
                catch (IllegalStateException ex) {
                    // Session invalidated - shouldn't usually happen.
                }
            }
            return null;
        }
    }

According to the Spring Session documentation:

Optimized Writes

The Session instances managed by RedisOperationsSessionRepository keeps track of the properties that have changed and only updates those. This means if an attribute is written once and read many times we only need to write that attribute once.

Or the documentation is wrong or I'm doing something wrong.

Plebios
  • 835
  • 1
  • 7
  • 17

2 Answers2

1

I think You did some mistakes while testing Your code. I have just tested it, and it works as expected.

I have used SoapUI, created 2 request's with the same JSESSIONID value in Cookie (same session).

Then I requested for /get, and meanwhile in second request window, i spammed /inc.

What /get returned was the number of /inc. (at the beggining value was 0 , than I have incremented it to 11 while /get was sleeping. Finally, /get returned 11).

I suggest You double check if nothing is messed up with Your session.

Edit: Your code with additional logs: (I've increased the sleeping time to 10000):

2016-04-06 11:56:10.977  INFO 7884 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 14 ms
2016-04-06 11:56:11.014  INFO 7884 --- [nio-8080-exec-1] c.p.controller.TestServiceController     : Before 10sec counter value: 0
2016-04-06 11:56:21.015  INFO 7884 --- [nio-8080-exec-1] c.p.controller.TestServiceController     : After 10sec counter value: 0
2016-04-06 11:56:36.955  INFO 7884 --- [nio-8080-exec-2] c.p.controller.TestServiceController     : Before 10sec counter value: 0
2016-04-06 11:56:46.956  INFO 7884 --- [nio-8080-exec-2] c.p.controller.TestServiceController     : After 10sec counter value: 0
2016-04-06 11:56:50.558  INFO 7884 --- [nio-8080-exec-3] c.p.controller.TestServiceController     : Incrementing counter value: 1
2016-04-06 11:56:53.494  INFO 7884 --- [nio-8080-exec-4] c.p.controller.TestServiceController     : Before 10sec counter value: 1
2016-04-06 11:57:03.496  INFO 7884 --- [nio-8080-exec-4] c.p.controller.TestServiceController     : After 10sec counter value: 1
2016-04-06 11:57:05.600  INFO 7884 --- [nio-8080-exec-5] c.p.controller.TestServiceController     : Before 10sec counter value: 1
2016-04-06 11:57:06.715  INFO 7884 --- [nio-8080-exec-6] c.p.controller.TestServiceController     : Incrementing counter value: 2
2016-04-06 11:57:06.869  INFO 7884 --- [nio-8080-exec-7] c.p.controller.TestServiceController     : Incrementing counter value: 3
2016-04-06 11:57:07.038  INFO 7884 --- [nio-8080-exec-8] c.p.controller.TestServiceController     : Incrementing counter value: 4
2016-04-06 11:57:07.186  INFO 7884 --- [nio-8080-exec-9] c.p.controller.TestServiceController     : Incrementing counter value: 5
2016-04-06 11:57:07.321  INFO 7884 --- [io-8080-exec-10] c.p.controller.TestServiceController     : Incrementing counter value: 6
2016-04-06 11:57:07.478  INFO 7884 --- [nio-8080-exec-1] c.p.controller.TestServiceController     : Incrementing counter value: 7
2016-04-06 11:57:07.641  INFO 7884 --- [nio-8080-exec-2] c.p.controller.TestServiceController     : Incrementing counter value: 8
2016-04-06 11:57:07.794  INFO 7884 --- [nio-8080-exec-3] c.p.controller.TestServiceController     : Incrementing counter value: 9
2016-04-06 11:57:07.967  INFO 7884 --- [nio-8080-exec-4] c.p.controller.TestServiceController     : Incrementing counter value: 10
2016-04-06 11:57:08.121  INFO 7884 --- [nio-8080-exec-6] c.p.controller.TestServiceController     : Incrementing counter value: 11
2016-04-06 11:57:15.602  INFO 7884 --- [nio-8080-exec-5] c.p.controller.TestServiceController     : After 10sec counter value: 11
patrykos91
  • 3,506
  • 2
  • 24
  • 30
  • The problems are not in testing. I'm using Spring session with Redis. Maybe my mistakes are in the Redis configuration – Plebios Apr 06 '16 at 10:03
  • Than its as You said in Redis, as the code that You provided as an example, is working. – patrykos91 Apr 06 '16 at 10:03
  • I didn't, Your question didn't mention anything connected with Redis before. I've just checked the Spring session-scope behavior. – patrykos91 Apr 06 '16 at 10:07
  • Can you show me your session configuration files? How do you configure the session scope? – Plebios Apr 06 '16 at 10:07
  • I did that in Spring Boot, so no additional configuration. But in classic Spring You have to add: org.springframework.web.context.request.RequestContextListener to Your web.xml file. Someone already mentioned that in previous comment I think – patrykos91 Apr 06 '16 at 10:10
  • If You use Spring Boot, or classic Spring with listener added to web.xml, @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) does the whole work. – patrykos91 Apr 06 '16 at 10:13
  • The sessions works fine but I have the concurrence problem. I do not configure using xml I use annotations. I put the Session configuration on the question – Plebios Apr 06 '16 at 10:14
  • When working with annotations, register the RequestContextListener bean : @Bean public RequestContextListener requestContextListener(){ return new RequestContextListener(); } – patrykos91 Apr 06 '16 at 10:17
  • Than in my opinion it's not a Spring problem. – patrykos91 Apr 06 '16 at 10:30
0

It seems that it is nothing to do with this problem, this is the expected behavior of Spring Session with session scoped beans. For me it 's a critical problem and I've decided forget distributed caches (Redis and Hazelcast) and use the MapSessionRepository

Plebios
  • 835
  • 1
  • 7
  • 17