Fix RedisSessionExpirationPolicy to properly cleanup expired sessions

Previously forcibly cleaning up sessions was not working. All the cleanup
was done by Redis expiration. This meant that sessions would be kept alive
until Redis cleaned them up (non deterministic).

This commit resolves the mapping of expiration to session ids.

Fixes gh-169
This commit is contained in:
Rob Winch
2015-04-15 15:25:50 -05:00
parent f711876347
commit 23afc1b354
3 changed files with 128 additions and 20 deletions

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2002-2015 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 static org.mockito.Mockito.*;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.session.MapSession;
/**
* @author Rob Winch
*/
@RunWith(MockitoJUnitRunner.class)
@SuppressWarnings({"rawtypes","unchecked"})
public class RedisSessionExpirationPolicyTests {
// Wed Apr 15 10:28:32 CDT 2015
final static Long NOW = 1429111712346L;
// Wed Apr 15 10:27:32 CDT 2015
final static Long ONE_MINUTE_AGO = 1429111652346L;
@Mock
RedisOperations sessionRedisOperations;
@Mock
BoundSetOperations setOperations;
@Mock
BoundHashOperations hashOperations;
RedisSessionExpirationPolicy policy;
private MapSession session;
@Before
public void setup() {
policy = new RedisSessionExpirationPolicy(sessionRedisOperations);
session = new MapSession();
session.setLastAccessedTime(1429116694665L);
session.setId("12345");
when(sessionRedisOperations.boundSetOps(anyString())).thenReturn(setOperations);
when(sessionRedisOperations.boundHashOps(anyString())).thenReturn(hashOperations);
}
// gh-169
@Test
public void onExpirationUpdatedRemovesOriginalExpirationTimeRoundedUp() throws Exception {
long originalExpirationTimeInMs = ONE_MINUTE_AGO;
long originalRoundedToNextMinInMs = RedisSessionExpirationPolicy.roundUpToNextMinute(originalExpirationTimeInMs);
String originalExpireKey = policy.getExpirationKey(originalRoundedToNextMinInMs);
policy.onExpirationUpdated(originalExpirationTimeInMs, session);
// verify the original is removed
verify(sessionRedisOperations).boundSetOps(originalExpireKey);
verify(setOperations).remove(session.getId());
}
@Test
public void onExpirationUpdatedAddsExpirationTimeRoundedUp() throws Exception {
long expirationTimeInMs = RedisSessionExpirationPolicy.expiresInMillis(session);
long expirationRoundedUpInMs = RedisSessionExpirationPolicy.roundUpToNextMinute(expirationTimeInMs);
String expectedExpireKey = policy.getExpirationKey(expirationRoundedUpInMs);
policy.onExpirationUpdated(null, session);
verify(sessionRedisOperations).boundSetOps(expectedExpireKey);
verify(setOperations).add(session.getId());
verify(setOperations).expire(session.getMaxInactiveIntervalInSeconds() + 60, TimeUnit.SECONDS);
}
@Test
public void onExpirationUpdatedSetExpireSession() throws Exception {
String sessionKey = policy.getSessionKey(session.getId());
policy.onExpirationUpdated(null, session);
verify(sessionRedisOperations).boundHashOps(sessionKey);
verify(hashOperations).expire(session.getMaxInactiveIntervalInSeconds(), TimeUnit.SECONDS);
}
}