Optimize JDBC session cleanup SQL statement

This commit improves session cleanup handling in  `JdbcOperationsSessionRepository#cleanUpExpiredSessions` by optimizing the used SQL statement. This is done by calculating the session expiry time when persisting the session, which in turn allows the cleanup SQL statement to be more index-friendly.

Closes gh-847
This commit is contained in:
Vedran Pavic
2017-09-11 09:01:58 +02:00
committed by Rob Winch
parent 1e46630467
commit 188e5ba4e0
12 changed files with 50 additions and 32 deletions

View File

@@ -97,20 +97,22 @@ import org.springframework.util.StringUtils;
* <pre class="code">
* CREATE TABLE SPRING_SESSION (
* PRIMARY_ID CHAR(36) NOT NULL,
* SESSION_ID CHAR(36),
* SESSION_ID CHAR(36) NOT NULL,
* CREATION_TIME BIGINT NOT NULL,
* LAST_ACCESS_TIME BIGINT NOT NULL,
* MAX_INACTIVE_INTERVAL INT NOT NULL,
* EXPIRY_TIME BIGINT NOT NULL,
* PRINCIPAL_NAME VARCHAR(100),
* CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
* );
*
* CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (LAST_ACCESS_TIME);
* CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
* CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (EXPIRY_TIME);
*
* CREATE TABLE SPRING_SESSION_ATTRIBUTES (
* SESSION_PRIMARY_ID CHAR(36) NOT NULL,
* ATTRIBUTE_NAME VARCHAR(200),
* ATTRIBUTE_BYTES BYTEA,
* ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
* ATTRIBUTE_BYTES BYTEA NOT NULL,
* CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
* CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
* );
@@ -138,8 +140,8 @@ public class JdbcOperationsSessionRepository implements
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
private static final String CREATE_SESSION_QUERY =
"INSERT INTO %TABLE_NAME%(PRIMARY_ID, SESSION_ID, CREATION_TIME, LAST_ACCESS_TIME, MAX_INACTIVE_INTERVAL, PRINCIPAL_NAME) " +
"VALUES (?, ?, ?, ?, ?, ?)";
"INSERT INTO %TABLE_NAME%(PRIMARY_ID, SESSION_ID, CREATION_TIME, LAST_ACCESS_TIME, MAX_INACTIVE_INTERVAL, EXPIRY_TIME, PRINCIPAL_NAME) " +
"VALUES (?, ?, ?, ?, ?, ?, ?)";
private static final String CREATE_SESSION_ATTRIBUTE_QUERY =
"INSERT INTO %TABLE_NAME%_ATTRIBUTES(SESSION_PRIMARY_ID, ATTRIBUTE_NAME, ATTRIBUTE_BYTES) " +
@@ -152,7 +154,7 @@ public class JdbcOperationsSessionRepository implements
"WHERE S.SESSION_ID = ?";
private static final String UPDATE_SESSION_QUERY =
"UPDATE %TABLE_NAME% SET SESSION_ID = ?, LAST_ACCESS_TIME = ?, MAX_INACTIVE_INTERVAL = ?, PRINCIPAL_NAME = ? " +
"UPDATE %TABLE_NAME% SET SESSION_ID = ?, LAST_ACCESS_TIME = ?, MAX_INACTIVE_INTERVAL = ?, EXPIRY_TIME = ?, PRINCIPAL_NAME = ? " +
"WHERE PRIMARY_ID = ?";
private static final String UPDATE_SESSION_ATTRIBUTE_QUERY =
@@ -175,9 +177,9 @@ public class JdbcOperationsSessionRepository implements
"LEFT OUTER JOIN %TABLE_NAME%_ATTRIBUTES SA ON S.PRIMARY_ID = SA.SESSION_PRIMARY_ID " +
"WHERE S.PRINCIPAL_NAME = ?";
private static final String DELETE_SESSIONS_BY_LAST_ACCESS_TIME_QUERY =
private static final String DELETE_SESSIONS_BY_EXPIRY_TIME_QUERY =
"DELETE FROM %TABLE_NAME% " +
"WHERE MAX_INACTIVE_INTERVAL < (? - LAST_ACCESS_TIME) / 1000";
"WHERE EXPIRY_TIME < ?";
private static final Log logger = LogFactory
.getLog(JdbcOperationsSessionRepository.class);
@@ -211,7 +213,7 @@ public class JdbcOperationsSessionRepository implements
private String listSessionsByPrincipalNameQuery;
private String deleteSessionsByLastAccessTimeQuery;
private String deleteSessionsByExpiryTimeQuery;
/**
* If non-null, this value is used to override the default value for
@@ -333,11 +335,11 @@ public class JdbcOperationsSessionRepository implements
/**
* Set the custom SQL query used to delete the sessions by last access time.
* @param deleteSessionsByLastAccessTimeQuery the SQL query string
* @param deleteSessionsByExpiryTimeQuery the SQL query string
*/
public void setDeleteSessionsByLastAccessTimeQuery(String deleteSessionsByLastAccessTimeQuery) {
Assert.hasText(deleteSessionsByLastAccessTimeQuery, "Query must not be empty");
this.deleteSessionsByLastAccessTimeQuery = deleteSessionsByLastAccessTimeQuery;
public void setDeleteSessionsByExpiryTimeQuery(String deleteSessionsByExpiryTimeQuery) {
Assert.hasText(deleteSessionsByExpiryTimeQuery, "Query must not be empty");
this.deleteSessionsByExpiryTimeQuery = deleteSessionsByExpiryTimeQuery;
}
/**
@@ -385,7 +387,8 @@ public class JdbcOperationsSessionRepository implements
ps.setLong(3, session.getCreationTime().toEpochMilli());
ps.setLong(4, session.getLastAccessedTime().toEpochMilli());
ps.setInt(5, (int) session.getMaxInactiveInterval().getSeconds());
ps.setString(6, session.getPrincipalName());
ps.setLong(6, session.getExpiryTime().toEpochMilli());
ps.setString(7, session.getPrincipalName());
});
if (!session.getAttributeNames().isEmpty()) {
final List<String> attributeNames = new ArrayList<>(session.getAttributeNames());
@@ -421,8 +424,9 @@ public class JdbcOperationsSessionRepository implements
ps.setString(1, session.getId());
ps.setLong(2, session.getLastAccessedTime().toEpochMilli());
ps.setInt(3, (int) session.getMaxInactiveInterval().getSeconds());
ps.setString(4, session.getPrincipalName());
ps.setString(5, session.primaryKey);
ps.setLong(4, session.getExpiryTime().toEpochMilli());
ps.setString(5, session.getPrincipalName());
ps.setString(6, session.primaryKey);
});
}
Map<String, Object> delta = session.getDelta();
@@ -524,7 +528,7 @@ public class JdbcOperationsSessionRepository implements
public void cleanUpExpiredSessions() {
int deletedCount = this.transactionOperations.execute(transactionStatus ->
JdbcOperationsSessionRepository.this.jdbcOperations.update(
JdbcOperationsSessionRepository.this.deleteSessionsByLastAccessTimeQuery,
JdbcOperationsSessionRepository.this.deleteSessionsByExpiryTimeQuery,
System.currentTimeMillis()));
if (logger.isDebugEnabled()) {
@@ -571,8 +575,8 @@ public class JdbcOperationsSessionRepository implements
this.deleteSessionQuery = getQuery(DELETE_SESSION_QUERY);
this.listSessionsByPrincipalNameQuery =
getQuery(LIST_SESSIONS_BY_PRINCIPAL_NAME_QUERY);
this.deleteSessionsByLastAccessTimeQuery =
getQuery(DELETE_SESSIONS_BY_LAST_ACCESS_TIME_QUERY);
this.deleteSessionsByExpiryTimeQuery =
getQuery(DELETE_SESSIONS_BY_EXPIRY_TIME_QUERY);
}
private void serialize(PreparedStatement ps, int paramIndex, Object attributeValue)
@@ -643,6 +647,10 @@ public class JdbcOperationsSessionRepository implements
return PRINCIPAL_NAME_RESOLVER.resolvePrincipal(this);
}
Instant getExpiryTime() {
return getLastAccessedTime().plus(getMaxInactiveInterval());
}
public String getId() {
return this.delegate.getId();
}

View File

@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,

View File

@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,

View File

@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,

View File

@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,

View File

@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
) ENGINE=InnoDB;
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,

View File

@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME NUMBER(19,0) NOT NULL,
LAST_ACCESS_TIME NUMBER(19,0) NOT NULL,
MAX_INACTIVE_INTERVAL NUMBER(10,0) NOT NULL,
EXPIRY_TIME NUMBER(19,0) NOT NULL,
PRINCIPAL_NAME VARCHAR2(100 CHAR),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,

View File

@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,

View File

@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME INTEGER NOT NULL,
LAST_ACCESS_TIME INTEGER NOT NULL,
MAX_INACTIVE_INTERVAL INTEGER NOT NULL,
EXPIRY_TIME INTEGER NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,

View File

@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,

View File

@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
) LOCK DATAROWS;
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,

View File

@@ -274,7 +274,7 @@ public class JdbcOperationsSessionRepositoryTests {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");
this.repository.setDeleteSessionsByLastAccessTimeQuery(null);
this.repository.setDeleteSessionsByExpiryTimeQuery(null);
}
@Test
@@ -282,7 +282,7 @@ public class JdbcOperationsSessionRepositoryTests {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");
this.repository.setDeleteSessionsByLastAccessTimeQuery(" ");
this.repository.setDeleteSessionsByExpiryTimeQuery(" ");
}
@Test