Skip to content

Commit

Permalink
ISPN-14477 Tests for concurrent Spring session access
Browse files Browse the repository at this point in the history
Added more tests to cover the change tracking in `InfinispanSession`. Specifically, ensure that the session attributes, max inactive duration and last accessed time are updated in the Infinispan cache if they are changed. Also, added tests for the various FlushMode and SaveMode settings.

Fixed a `NullPointerException` in `SessionUpdateRemappingFunctionProtoAdapter` which occurred when a session attribute was removed, i.e. the `SessionAttribute` value was null.

Fixed a serialisation issue `SessionUpdateRemappingFunctionProtoAdapter` which caused the last accessed time to be set to the epoch value if the session's last accessed time hadn't been changed.
  • Loading branch information
thelateperseus authored and karesti committed Feb 16, 2023
1 parent 22e6f89 commit ac0e017
Show file tree
Hide file tree
Showing 10 changed files with 360 additions and 24 deletions.
Expand Up @@ -4,6 +4,8 @@
import java.time.Instant;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import org.infinispan.commons.marshall.ProtoStreamTypeIds;
Expand Down Expand Up @@ -33,11 +35,15 @@ static SessionUpdateRemappingFunction createFunction(Collection<SessionAttribute
if (maxInactiveSeconds != null) {
function.setMaxInactiveInterval(Duration.ofSeconds(maxInactiveSeconds));
}
function.setDelta(attributes.stream().collect(Collectors.toMap(SessionAttribute::getName, SessionAttribute::getValue)));
Map<String,Object> delta = new HashMap<>();
for (SessionAttribute attribute : attributes) {
delta.put(attribute.getName(), attribute.getValue());
}
function.setDelta(delta);
return function;
}

@ProtoField(number = 1, defaultValue = "0")
@ProtoField(number = 1)
Instant getLastAccessedTime(SessionUpdateRemappingFunction function) {
return function.getLastAccessedTime();
}
Expand Down
Expand Up @@ -9,13 +9,16 @@

import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

import org.infinispan.commons.dataconversion.MediaType;
import org.infinispan.spring.common.provider.SpringCache;
import org.infinispan.spring.common.session.AbstractInfinispanSessionRepository.InfinispanSession;
import org.infinispan.test.AbstractInfinispanTest;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.FlushMode;
import org.springframework.session.SaveMode;
import org.testng.annotations.Test;

@Test(groups = "functional")
Expand Down Expand Up @@ -61,13 +64,102 @@ public void testCreatingSession() throws Exception {
}

@Test
public void testSavingSession() throws Exception {
public void testSavingNewSession() throws Exception {
//given
InfinispanSession session = sessionRepository.createSession();
Instant lastAccessedTime = session.getLastAccessedTime();
Duration maxInactiveInterval = session.getMaxInactiveInterval();

//when
sessionRepository.save(session);

//then
InfinispanSession savedSession = sessionRepository.findById(session.getId());
assertNotNull(savedSession);
assertTrue(savedSession.getAttributeNames().isEmpty());
assertEquals(
lastAccessedTime.truncatedTo(ChronoUnit.MILLIS),
savedSession.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS));
assertEquals(maxInactiveInterval, savedSession.getMaxInactiveInterval());
}

@Test
public void testSavingUnchangedSession() throws Exception {
//given
InfinispanSession session = sessionRepository.createSession();
Instant lastAccessedTime = session.getLastAccessedTime();
Duration maxInactiveInterval = session.getMaxInactiveInterval();
sessionRepository.save(session);

//when
session = sessionRepository.findById(session.getId());
sessionRepository.save(session);

//then
InfinispanSession updatedSession = sessionRepository.findById(session.getId());
assertTrue(updatedSession.getAttributeNames().isEmpty());
assertEquals(
lastAccessedTime.truncatedTo(ChronoUnit.MILLIS),
updatedSession.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS));
assertEquals(maxInactiveInterval, updatedSession.getMaxInactiveInterval());
}

@Test
public void testSavingSessionWithUpdatedMaxInactiveInterval() throws Exception {
//given
InfinispanSession session = sessionRepository.createSession();
sessionRepository.save(session);

//when
session = sessionRepository.findById(session.getId());
Duration newMaxInactiveInterval = Duration.ofSeconds(123);
session.setMaxInactiveInterval(newMaxInactiveInterval);
sessionRepository.save(session);

//then
InfinispanSession savedSession = sessionRepository.findById(session.getId());
assertEquals(newMaxInactiveInterval, savedSession.getMaxInactiveInterval());
}

@Test
public void testSavingSessionWithUpdatedLastAccessedTime() throws Exception {
//given
InfinispanSession session = sessionRepository.createSession();
sessionRepository.save(session);

//when
session = sessionRepository.findById(session.getId());
Instant newLastAccessedTime = Instant.now().minusSeconds(300);
session.setLastAccessedTime(newLastAccessedTime);
sessionRepository.save(session);

//then
InfinispanSession savedSession = sessionRepository.findById(session.getId());
assertEquals(
newLastAccessedTime.truncatedTo(ChronoUnit.MILLIS),
savedSession.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS));
}

@Test
public void testSavingSessionWithUpdatedAttributes() throws Exception {
//given
InfinispanSession session = sessionRepository.createSession();
session.setAttribute("changed", "oldValue");
session.setAttribute("removed", "existingValue");
sessionRepository.save(session);

//when
session = sessionRepository.findById(session.getId());
session.setAttribute("added", "addedValue");
session.setAttribute("changed", "newValue");
session.removeAttribute("removed");
sessionRepository.save(session);

//then
assertNotNull(sessionRepository.findById(session.getId()));
InfinispanSession savedSession = sessionRepository.findById(session.getId());
assertEquals(savedSession.getAttribute("added"), "addedValue");
assertEquals(savedSession.getAttribute("changed"), "newValue");
assertNull(savedSession.getAttribute("removed"));
}

@Test
Expand Down Expand Up @@ -174,21 +266,91 @@ public void testConcurrentSessionAccess() {
InfinispanSession session = sessionRepository.createSession();
// setLastAccessedTime will be called by Spring Session's SessionRepositoryRequestWrapper.getSession
session.setLastAccessedTime(Instant.now());
session.setAttribute("testAttribute", "oldValue");
sessionRepository.save(session);

//when
InfinispanSession slowRequestSession = sessionRepository.findById(session.getId());
slowRequestSession.setLastAccessedTime(Instant.now());
slowRequestSession.getAttribute("testAttribute");
InfinispanSession fastRequestSession = sessionRepository.findById(session.getId());
fastRequestSession.setLastAccessedTime(Instant.now());
fastRequestSession.setAttribute("testAttribute", "testValue");
fastRequestSession.setAttribute("testAttribute", "fastValue");
sessionRepository.save(fastRequestSession);
sessionRepository.save(slowRequestSession);

//then
assertNotSame(slowRequestSession, fastRequestSession);
InfinispanSession updatedSession = sessionRepository.findById(session.getId());
assertEquals("testValue", updatedSession.getAttribute("testAttribute"));
assertEquals("fastValue", updatedSession.getAttribute("testAttribute"));
}

@Test
public void testConcurrentSessionAccessWithSaveModeOnGetAttribute() {
//given
sessionRepository.setSaveMode(SaveMode.ON_GET_ATTRIBUTE);
InfinispanSession session = sessionRepository.createSession();
// setLastAccessedTime will be called by Spring Session's SessionRepositoryRequestWrapper.getSession
session.setLastAccessedTime(Instant.now());
session.setAttribute("testAttribute", "oldValue");
sessionRepository.save(session);

//when
InfinispanSession slowRequestSession = sessionRepository.findById(session.getId());
slowRequestSession.setLastAccessedTime(Instant.now());
slowRequestSession.getAttribute("testAttribute");
InfinispanSession fastRequestSession = sessionRepository.findById(session.getId());
fastRequestSession.setLastAccessedTime(Instant.now());
fastRequestSession.setAttribute("testAttribute", "fastValue");
sessionRepository.save(fastRequestSession);
sessionRepository.save(slowRequestSession);

//then
assertNotSame(slowRequestSession, fastRequestSession);
InfinispanSession updatedSession = sessionRepository.findById(session.getId());
assertEquals("oldValue", updatedSession.getAttribute("testAttribute"));
}

@Test
public void testConcurrentSessionAccessWithSaveModeAlways() {
//given
sessionRepository.setSaveMode(SaveMode.ALWAYS);
InfinispanSession session = sessionRepository.createSession();
// setLastAccessedTime will be called by Spring Session's SessionRepositoryRequestWrapper.getSession
session.setLastAccessedTime(Instant.now());
session.setAttribute("testAttribute", "oldValue");
sessionRepository.save(session);

//when
InfinispanSession slowRequestSession = sessionRepository.findById(session.getId());
slowRequestSession.setLastAccessedTime(Instant.now());
InfinispanSession fastRequestSession = sessionRepository.findById(session.getId());
fastRequestSession.setLastAccessedTime(Instant.now());
fastRequestSession.setAttribute("testAttribute", "fastValue");
sessionRepository.save(fastRequestSession);
sessionRepository.save(slowRequestSession);

//then
assertNotSame(slowRequestSession, fastRequestSession);
InfinispanSession updatedSession = sessionRepository.findById(session.getId());
assertEquals("oldValue", updatedSession.getAttribute("testAttribute"));
}

@Test
public void testFlushModeImmediate() throws Exception {
//given
sessionRepository.setFlushMode(FlushMode.IMMEDIATE);
InfinispanSession existingSession = sessionRepository.createSession();

//when
InfinispanSession newSession = sessionRepository.createSession();
existingSession.setAttribute("testAttribute", "testValue");

//then
InfinispanSession savedNewSession = sessionRepository.findById(newSession.getId());
assertNotNull(savedNewSession);
InfinispanSession savedExistingSession = sessionRepository.findById(existingSession.getId());
assertEquals("testValue", savedExistingSession.getAttribute("testAttribute"));
}

protected void addEmptySessionWithPrincipal(AbstractInfinispanSessionRepository sessionRepository, String principalName) {
Expand Down
Expand Up @@ -80,8 +80,8 @@ public void testCreatingSession() throws Exception {
}

@Override
public void testSavingSession() throws Exception {
super.testSavingSession();
public void testSavingNewSession() throws Exception {
super.testSavingNewSession();
}

@Override
Expand Down
Expand Up @@ -95,8 +95,8 @@ public void testCreatingSession() throws Exception {
}

@Override
public void testSavingSession() throws Exception {
super.testSavingSession();
public void testSavingNewSession() throws Exception {
super.testSavingNewSession();
}

@Override
Expand Down
Expand Up @@ -11,6 +11,8 @@
import java.time.Instant;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
Expand All @@ -33,11 +35,15 @@ static SessionUpdateRemappingFunction createFunction(Collection<SessionAttribute
if (maxInactiveSeconds != null) {
function.setMaxInactiveInterval(Duration.ofSeconds(maxInactiveSeconds));
}
function.setDelta(attributes.stream().collect(Collectors.toMap(SessionAttribute::getName, SessionAttribute::getValue)));
Map<String,Object> delta = new HashMap<>();
for (SessionAttribute attribute : attributes) {
delta.put(attribute.getName(), attribute.getValue());
}
function.setDelta(delta);
return function;
}

@ProtoField(number = 1, defaultValue = "0")
@ProtoField(number = 1)
Instant getLastAccessedTime(SessionUpdateRemappingFunction function) {
return function.getLastAccessedTime();
}
Expand Down

0 comments on commit ac0e017

Please sign in to comment.