Add RedisFlushMode

This commit allows Redis support to flush lazily (already
supported) or immediately (new).

Fixes gh-273
This commit is contained in:
Rob Winch
2016-02-10 08:41:49 -06:00
parent 5f23b3c272
commit e0bbbbcd45
8 changed files with 317 additions and 3 deletions

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.session.data.redis;
import org.springframework.session.SessionRepository;
/**
* Specifies when to write to the backing Redis instance.
*
* @author Rob Winch
* @since 1.1
*/
public enum RedisFlushMode {
/**
* Only writes to Redis when
* {@link SessionRepository#save(org.springframework.session.Session)} is
* invoked. In a web environment this is typically done as soon as the HTTP
* response is committed.
*/
ON_SAVE,
/**
* Writes to Redis as soon as possible. For example
* {@link SessionRepository#createSession()} will write the session to
* Redis. Another example is that setting an attribute on the session will
* also write to Redis immediately.
*/
IMMEDIATE
}

View File

@@ -304,6 +304,8 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
private RedisSerializer<Object> defaultSerializer = new JdkSerializationRedisSerializer();
private RedisFlushMode redisFlushMode = RedisFlushMode.ON_SAVE;
/**
* Allows creating an instance and uses a default {@link RedisOperations} for both managing the session and the expirations.
*
@@ -360,6 +362,14 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
this.defaultSerializer = defaultSerializer;
}
/**
* @param redisFlushMode
*/
public void setRedisFlushMode(RedisFlushMode redisFlushMode) {
Assert.notNull(redisFlushMode, "redisFlushMode cannot be null");
this.redisFlushMode = redisFlushMode;
}
public void save(RedisSession session) {
session.saveDelta();
if(session.isNew()) {
@@ -636,6 +646,7 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
delta.put(MAX_INACTIVE_ATTR, getMaxInactiveIntervalInSeconds());
delta.put(LAST_ACCESSED_ATTR, getLastAccessedTime());
this.isNew = true;
flushImmediateIfNecessary();
}
/**
@@ -656,6 +667,7 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
public void setLastAccessedTime(long lastAccessedTime) {
cached.setLastAccessedTime(lastAccessedTime);
delta.put(LAST_ACCESSED_ATTR, getLastAccessedTime());
flushImmediateIfNecessary();
}
public boolean isExpired() {
@@ -681,6 +693,7 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
public void setMaxInactiveIntervalInSeconds(int interval) {
cached.setMaxInactiveIntervalInSeconds(interval);
delta.put(MAX_INACTIVE_ATTR, getMaxInactiveIntervalInSeconds());
flushImmediateIfNecessary();
}
public int getMaxInactiveIntervalInSeconds() {
@@ -699,11 +712,19 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
public void setAttribute(String attributeName, Object attributeValue) {
cached.setAttribute(attributeName, attributeValue);
delta.put(getSessionAttrNameKey(attributeName), attributeValue);
flushImmediateIfNecessary();
}
public void removeAttribute(String attributeName) {
cached.removeAttribute(attributeName);
delta.put(getSessionAttrNameKey(attributeName), null);
flushImmediateIfNecessary();
}
private void flushImmediateIfNecessary() {
if(redisFlushMode == RedisFlushMode.IMMEDIATE) {
saveDelta();
}
}
/**

View File

@@ -22,7 +22,9 @@ import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.session.SessionRepository;
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
import org.springframework.session.data.redis.RedisFlushMode;
/**
* Add this annotation to an {@code @Configuration} class to expose the
@@ -76,4 +78,22 @@ public @interface EnableRedisHttpSession {
* @return the unique namespace for keys
*/
String redisNamespace() default "";
/**
* <p>
* Sets the flush mode for the Redis sessions. The default is IMMEDIATE
* which only updates the backing Redis when
* {@link SessionRepository#save(org.springframework.session.Session)} is
* invoked. In a web environment this happens just before the HTTP resposne
* is committed.
* </p>
* <p>
* Setting the value to IMMEDIATE will ensure that the any updates to the
* Session are immediately written to the Redis instance.
* </p>
*
* @return the {@link RedisFlushMode} to use
* @since 1.1
*/
RedisFlushMode redisFlushMode() default RedisFlushMode.ON_SAVE;
}

View File

@@ -37,10 +37,12 @@ import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
import org.springframework.session.data.redis.RedisFlushMode;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction;
import org.springframework.session.data.redis.config.ConfigureRedisAction;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
@@ -63,6 +65,8 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
private String redisNamespace = "";
private RedisFlushMode redisFlushMode = RedisFlushMode.ON_SAVE;
private RedisSerializer<Object> defaultRedisSerializer;
@Bean
@@ -102,6 +106,8 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
if(StringUtils.hasText(redisNamespace)) {
sessionRepository.setRedisKeyNamespace(redisNamespace);
}
sessionRepository.setRedisFlushMode(redisFlushMode);
return sessionRepository;
}
@@ -113,6 +119,11 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
this.redisNamespace = namespace;
}
public void setRedisFlushMode(RedisFlushMode redisFlushMode) {
Assert.notNull(redisFlushMode, "redisFlushMode cannot be null");
this.redisFlushMode = redisFlushMode;
}
private String getRedisNamespace() {
if(StringUtils.hasText(this.redisNamespace)) {
return this.redisNamespace;
@@ -126,6 +137,7 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
AnnotationAttributes enableAttrs = AnnotationAttributes.fromMap(enableAttrMap);
maxInactiveIntervalInSeconds = enableAttrs.getNumber("maxInactiveIntervalInSeconds");
this.redisNamespace = enableAttrs.getString("redisNamespace");
this.redisFlushMode = enableAttrs.getEnum("redisFlushMode");
}
@Bean