Did you actually create a transaction manager bean anywhere (I didn't see one when I had a quick look)?
Hi,@dsyer I have created PlatformTransactionManager in com.selonj.getstarted.oauth2.config.OAuth2ServerConfig.AuthorizationServerConfig class,and I debug the PlatformTransactionManager and it works,but the test failed
@dsyer锛宒id my issue solve in 2.0.9 version by simple configuration via transaction management ?
That's what I thought. I haven't had a chance to try your test yet.
@dsyer ,you can clone https://github.com/selonj/getstarted-spring-oauth2.git in branch avoid_multi_accestoken_in_database,help me look at the issue.and I used mysql for test purpose
We're experiencing the same problem.
We're using the following spring versions:
The @EnableTransactionManagement is configured to use datasource transaction manager like the following:
@Bean
@Autowired
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
We haven't created a unique constraint on the authentication_id field yet
To reproduce the issue we used two concurrent shell scripts that execute:
watch -n1 "curl 'http://[host]:[port]/oauth/token?grant_type=password&username=[user]&password=[password]' -X POST -H 'Authorization: Basic XXXXXXX==' -vv"
Could you please help to fix the issue in a proper way because from our perspective it doesn't seem to be a good solution to handle exceptions caused by unique constraint violation on the authentication_id field?
I managed to solve this issue.
The root cause of the problem is in "check then act" logic of the default org.springframework.security.oauth2.provider.token.store.JdbcTokenStore implementation.
The following steps take place when querying for an access token:
A race condition occurs when more then one transaction requests for the same access token.
In order to reproduce this issue, as mentioned in my previous post, I used two concurrent shell scripts that execute the following command:
watch -n1 "curl 'http://[host]:[port]/oauth/token?grant_type=password&username=[user]&password=[password]' -X POST -H 'Authorization: Basic XXXXXXX==' -vv"
Seemingly, the issue is likely to be solved by introducing a row-level lock in the underling RDBMS by substituting the default SELECT statement with a SELECT FOR UPDATE one in the default org.springframework.security.oauth2.provider.token.store.JdbcTokenStore implementation, but it is not the case for the Postgresql implementation.
Because in case of two concurrent transactions one of them succeeds in locking, another is blocked on the same row. According to the "check then act" logic of the org.springframework.security.oauth2.provider.token.store.JdbcTokenStore described above the winning transaction at first removes the access token, then re-inserts it back and finally releases the write lock. The blocked transaction is unlocked but due to PosrgeSQL implementation it doesn't see the newly inserted token and proceeds with creating a new token. As a result a constrain violation exception occurs. More on the default read committed transaction isolation level
So the steps to solve the issue are:
We still have the same behavior under load with the JdbTokenStore. I will try your approach ... thx Rico
Closing this as questions are better suited on Stack Overflow. We prefer to use GitHub issues for bugs and enhancements.
@jgrandja why did you close the issue? it鈥檚 not a question it鈥檚 a bug isn鈥檛 it? I provided a solid reasoning on why it takes place.
@mpryahin It seems you solved it as detailed in this comment?
I'm not sure this is a bug but rather a database-level configuration and/or overall architecture setup. When there are multiple requests (from different application instances) querying the database via JdbcTokenStore, transaction management needs to be configured on the application side and likely at the database level.
If you still feel this is a bug then can you please provide a minimal sample that reproduces the issue and/or specific details in the code that you feel should be fixed.
@jgrandja thanks for taking the time to reply, you're right, I somehow overlooked my latest comment made quite a long time ago, sorry for bothering 馃檪
Most helpful comment
I managed to solve this issue.
The root cause of the problem is in "check then act" logic of the default org.springframework.security.oauth2.provider.token.store.JdbcTokenStore implementation.
The following steps take place when querying for an access token:
A race condition occurs when more then one transaction requests for the same access token.
In order to reproduce this issue, as mentioned in my previous post, I used two concurrent shell scripts that execute the following command:
Seemingly, the issue is likely to be solved by introducing a row-level lock in the underling RDBMS by substituting the default SELECT statement with a SELECT FOR UPDATE one in the default org.springframework.security.oauth2.provider.token.store.JdbcTokenStore implementation, but it is not the case for the Postgresql implementation.
Because in case of two concurrent transactions one of them succeeds in locking, another is blocked on the same row. According to the "check then act" logic of the org.springframework.security.oauth2.provider.token.store.JdbcTokenStore described above the winning transaction at first removes the access token, then re-inserts it back and finally releases the write lock. The blocked transaction is unlocked but due to PosrgeSQL implementation it doesn't see the newly inserted token and proceeds with creating a new token. As a result a constrain violation exception occurs. More on the default read committed transaction isolation level
So the steps to solve the issue are: