Plebios
Plebios

Reputation: 835

Spring REST webservice and session persistence

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.

Upvotes: 0

Views: 880

Answers (2)

Plebios
Plebios

Reputation: 835

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

Upvotes: 0

patrykos91
patrykos91

Reputation: 3686

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

Upvotes: 1

Related Questions