Skip to content

Saving JDBC Session Attributes is not thread-safe  #1216

Closed
@bonhamcm

Description

@bonhamcm

Spring Framework 5.0.9.RELEASE
Spring Boot 2.0.5.RELEASE
Spring Session 2.0.6.RELEASE
PostgreSQL 9.3.20
PostgreSQL JDBC Driver 42.2.5

I'm experiencing the same error described in #1213 but I am using Spring Session 2.0.6.RELEASE which has the fix for #1031 / #1151:

org.springframework.dao.DuplicateKeyException: PreparedStatementCallback; SQL [INSERT INTO SPRING_SESSION_ATTRIBUTES(SESSION_PRIMARY_ID, ATTRIBUTE_NAME, ATTRIBUTE_BYTES) SELECT PRIMARY_ID, ?, ? FROM SPRING_SESSION WHERE SESSION_ID = ?ERROR: duplicate key value violates unique constraint "spring_session_attributes_pk"
  Detail: Key (session_primary_id, attribute_name)=(b020c2e9-5470-4901-b89b-0531b61cf108, survey) already exists.; nested exception is org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "spring_session_attributes_pk"
  Detail: Key (session_primary_id, attribute_name)=(b020c2e9-5470-4901-b89b-0531b61cf108, survey) already exists.
        at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:242)
        at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
        at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1402)
        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:620)
        at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:850)
        at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:905)
        at org.springframework.session.jdbc.JdbcOperationsSessionRepository.insertSessionAttributes(JdbcOperationsSessionRepository.java:521)
        at org.springframework.session.jdbc.JdbcOperationsSessionRepository.access$300(JdbcOperationsSessionRepository.java:131)
        at org.springframework.session.jdbc.JdbcOperationsSessionRepository$2.doInTransactionWithoutResult(JdbcOperationsSessionRepository.java:416)
        at org.springframework.transaction.support.TransactionCallbackWithoutResult.doInTransaction(TransactionCallbackWithoutResult.java:36)
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
        at org.springframework.session.jdbc.JdbcOperationsSessionRepository.save(JdbcOperationsSessionRepository.java:395)
        at org.springframework.session.jdbc.JdbcOperationsSessionRepository.save(JdbcOperationsSessionRepository.java:131)
        at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.commitSession(SessionRepositoryFilter.java:234)
        at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.access$100(SessionRepositoryFilter.java:197)
        at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryResponseWrapper.onResponseCommitted(SessionRepositoryFilter.java:185)
        at org.springframework.session.web.http.OnCommittedResponseWrapper.doOnResponseCommitted(OnCommittedResponseWrapper.java:227)
        at org.springframework.session.web.http.OnCommittedResponseWrapper.access$000(OnCommittedResponseWrapper.java:38)
        at org.springframework.session.web.http.OnCommittedResponseWrapper$SaveContextPrintWriter.flush(OnCommittedResponseWrapper.java:249)
        at org.springframework.security.web.util.OnCommittedResponseWrapper$SaveContextPrintWriter.flush(OnCommittedResponseWrapper.java:269)
        at org.apache.jasper.runtime.JspWriterImpl.flush(JspWriterImpl.java:172)
        ...
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)
Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "spring_session_attributes_pk"
  Detail: Key (session_primary_id, attribute_name)=(b020c2e9-5470-4901-b89b-0531b61cf108, survey) already exists.
        at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2440)
        at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2183)
        at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:308)
        at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:441)
        at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:365)
        at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:143)
        at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:120)
        at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)
        at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java)
        at org.springframework.jdbc.core.JdbcTemplate.lambda$update$0(JdbcTemplate.java:855)
        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:605)
        ... 141 common frames omitted

It appears that the JdbcOperationsSessionRepository$JdbcSession.delta HashMap which is used to determine if the session attributes should be inserted or updated is not thread-safe, thereby causing this issue. Using a ConcurrentHashMap could be a simple fix for this issue.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions