Replies: 2 comments 14 replies
-
Hi @sguilhen, looking at the code option 2 seems to be the smallest change. Looking a bit more closer at the implementation I did for LDAP, it is strange that the code in the storage provider already decides if a new transaction should be created or an existing should be used, and some other code would decide if it should be enlisted. Also, that other code would decide if it would be enlist() or enlistAfterCompletion().
Therefore I would propose the following change: the one who creates the transaction should also enlist it with the session, in this case the one who calls factory.createTransaction() would then also enlist it with the session. WDYT? |
Beta Was this translation helpful? Give feedback.
-
Attached is the diagram of the setup that we agreed on in the call:
|
Beta Was this translation helpful? Give feedback.
-
I've finished the implementation of the user login failures JPA storage and while testing it I came across this error in tests that remove more than one user via
UserManager
:UserSessionProviderTest is a good example of a test that causes this error. Basically this is what happens:
UserManager.removeUser
to remove more than one user as part of the same transaction.UserRemovedEvent
is generated and is handled by the MapUserLoginFailureProviderFactory.MapUserLoginFailureProvider
, which on creation callsstore.createTransaction(session)
.jpa-map-tx-modelType.hashCode
, returning the previoustx
if one is found in the session or creating a newtx
otherwise.So when removing the first user, a new transaction is created for the user login failure and enlisted. From that point on, every time the
MapUserLoginFailureProviderFactory
handles the removal event, it creates aMapUserLoginFailureProvider
that in turn fetches the previously createdtx
from the session and enlists it again.When the overall transaction is committed, the user login failure transaction is committed multiple times because it has been enlisted more than once while handling the user removal event, which causes the exception described above.
I could think of a few ways to get around it:
removeUser
is called multiple times in the sameUserManager
only by the tests. Our endpoints offer no bulk user removal, so it always processes one removal per transaction. That means we can probably adjust the tests to, for example, remove the created users calling the endpoint directly for each user (adminClient.realm("test").users().get(userId1).remove();
), but this requires keeping track o the ids of the users created in each test.enlist
methods inDefaultKeycloakTransactionManager
check if the same instance has already been enlisted - this would prevent multiplecommit()
calls to the same transaction object.MapUserLoginFailureProvider
's constructor so that it somehow checks if thetx
already exists in the session before callingenlistAfterComplete
. This would ensure that only the provider that created the transaction that is currently active in the session gets to enlist it.I'm currently leaning towards option 2 and make the
enlist
methods check if the list of transactions already contain the transaction being enlisted to avoid enlisting it twice. Option 1 works but will require many changes to all the tests that remove users. Option 3 is feasible although I would need to investigate a viable option for looking up the session because the key is constructed by theJpaMapStorageProvider
and is not available to the user login failure provider.Beta Was this translation helpful? Give feedback.
All reactions