KEYCLOAK-18073 avoid ModelDuplicateException during parallel starup of servers
This commit is contained in:
parent
eb631bf63b
commit
3aa06c2721
11 changed files with 219 additions and 97 deletions
|
@ -24,6 +24,7 @@ embed-server
|
||||||
/subsystem=keycloak-server/spi=deploymentState:add(default-provider=map)
|
/subsystem=keycloak-server/spi=deploymentState:add(default-provider=map)
|
||||||
/subsystem=keycloak-server/spi=deploymentState/provider=map:add(enabled=true,properties={resourcesVersionSeed=1JZ379bzyOCFA})
|
/subsystem=keycloak-server/spi=deploymentState/provider=map:add(enabled=true,properties={resourcesVersionSeed=1JZ379bzyOCFA})
|
||||||
/subsystem=keycloak-server/spi=user:add(default-provider=map)
|
/subsystem=keycloak-server/spi=user:add(default-provider=map)
|
||||||
|
/subsystem=keycloak-server/spi=dblock:add(default-provider=none)
|
||||||
|
|
||||||
## For dev and single-node purposes, these are set to "map".
|
## For dev and single-node purposes, these are set to "map".
|
||||||
## For clustered deployments, these should be "infinispan" as map storage does not support distributed storage yet
|
## For clustered deployments, these should be "infinispan" as map storage does not support distributed storage yet
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.models.dblock;
|
||||||
|
|
||||||
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.common.Profile;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||||
|
|
||||||
|
public class NoLockingDBLockProviderFactory implements DBLockProviderFactory, EnvironmentDependentProviderFactory {
|
||||||
|
|
||||||
|
public static final String PROVIDER_ID = "none";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTimeouts(long lockRecheckTimeMillis, long lockWaitTimeoutMillis) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DBLockProvider create(KeycloakSession session) {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Config.Scope config) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postInit(KeycloakSessionFactory factory) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return PROVIDER_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSupported() {
|
||||||
|
return Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final DBLockProvider INSTANCE = new DBLockProvider() {
|
||||||
|
@Override
|
||||||
|
public void waitForLock(DBLockProvider.Namespace lock) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void releaseLock() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DBLockProvider.Namespace getCurrentLock() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsForcedUnlock() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroyLockInfo() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#
|
||||||
|
# Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||||
|
# and other contributors as indicated by the @author tags.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
org.keycloak.models.dblock.NoLockingDBLockProviderFactory
|
|
@ -29,6 +29,8 @@ import org.keycloak.models.ModelDuplicateException;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserProvider;
|
import org.keycloak.models.UserProvider;
|
||||||
|
import org.keycloak.models.dblock.DBLockManager;
|
||||||
|
import org.keycloak.models.dblock.DBLockProvider;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.models.utils.PostMigrationEvent;
|
import org.keycloak.models.utils.PostMigrationEvent;
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
|
@ -119,12 +121,27 @@ public class KeycloakApplication extends Application {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void startup() {
|
protected void startup() {
|
||||||
this.sessionFactory = createSessionFactory();
|
KeycloakApplication.sessionFactory = createSessionFactory();
|
||||||
|
|
||||||
ExportImportManager exportImportManager = bootstrap();
|
ExportImportManager[] exportImportManager = new ExportImportManager[1];
|
||||||
|
|
||||||
if (exportImportManager.isRunExport()) {
|
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
||||||
exportImportManager.runExport();
|
@Override
|
||||||
|
public void run(KeycloakSession session) {
|
||||||
|
DBLockManager dbLockManager = new DBLockManager(session);
|
||||||
|
dbLockManager.checkForcedUnlock();
|
||||||
|
DBLockProvider dbLock = dbLockManager.getDBLock();
|
||||||
|
dbLock.waitForLock(DBLockProvider.Namespace.KEYCLOAK_BOOT);
|
||||||
|
try {
|
||||||
|
exportImportManager[0] = bootstrap();
|
||||||
|
} finally {
|
||||||
|
dbLock.releaseLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (exportImportManager[0].isRunExport()) {
|
||||||
|
exportImportManager[0].runExport();
|
||||||
}
|
}
|
||||||
|
|
||||||
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
||||||
|
|
|
@ -1188,6 +1188,7 @@
|
||||||
<systemPropertyVariables>
|
<systemPropertyVariables>
|
||||||
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
|
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
|
||||||
<keycloak.mapStorage.provider>concurrenthashmap</keycloak.mapStorage.provider>
|
<keycloak.mapStorage.provider>concurrenthashmap</keycloak.mapStorage.provider>
|
||||||
|
<keycloak.dblock.provider>none</keycloak.dblock.provider>
|
||||||
<keycloak.realm.provider>map</keycloak.realm.provider>
|
<keycloak.realm.provider>map</keycloak.realm.provider>
|
||||||
<keycloak.client.provider>map</keycloak.client.provider>
|
<keycloak.client.provider>map</keycloak.client.provider>
|
||||||
<keycloak.clientScope.provider>map</keycloak.clientScope.provider>
|
<keycloak.clientScope.provider>map</keycloak.clientScope.provider>
|
||||||
|
|
|
@ -43,6 +43,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"dblock": {
|
||||||
|
"provider": "${keycloak.dblock.provider:jpa}"
|
||||||
|
},
|
||||||
|
|
||||||
"realm": {
|
"realm": {
|
||||||
"provider": "${keycloak.realm.provider:jpa}"
|
"provider": "${keycloak.realm.provider:jpa}"
|
||||||
},
|
},
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
|
|
||||||
package org.keycloak.testsuite.model;
|
package org.keycloak.testsuite.model;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -27,22 +30,12 @@ import org.keycloak.models.dblock.DBLockManager;
|
||||||
import org.keycloak.models.dblock.DBLockProvider;
|
import org.keycloak.models.dblock.DBLockProvider;
|
||||||
import org.keycloak.models.dblock.DBLockProviderFactory;
|
import org.keycloak.models.dblock.DBLockProviderFactory;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
|
||||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.ModelTest;
|
|
||||||
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
@AuthServerContainerExclude(AuthServer.REMOTE)
|
@RequireProvider(value=DBLockProvider.class, only="jpa")
|
||||||
public class DBLockTest extends AbstractTestRealmKeycloakTest {
|
public class DBLockTest extends KeycloakModelTest {
|
||||||
|
|
||||||
private static final Logger log = Logger.getLogger(DBLockTest.class);
|
private static final Logger log = Logger.getLogger(DBLockTest.class);
|
||||||
|
|
||||||
|
@ -58,8 +51,7 @@ public class DBLockTest extends AbstractTestRealmKeycloakTest {
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void before() throws Exception {
|
public void before() throws Exception {
|
||||||
|
inComittedTransaction(1, (session , i) -> {
|
||||||
testingClient.server().run(session -> {
|
|
||||||
// Set timeouts for testing
|
// Set timeouts for testing
|
||||||
DBLockManager lockManager = new DBLockManager(session);
|
DBLockManager lockManager = new DBLockManager(session);
|
||||||
DBLockProviderFactory lockFactory = lockManager.getDBLockFactory();
|
DBLockProviderFactory lockFactory = lockManager.getDBLockFactory();
|
||||||
|
@ -67,15 +59,14 @@ public class DBLockTest extends AbstractTestRealmKeycloakTest {
|
||||||
|
|
||||||
// Drop lock table, just to simulate racing threads for create lock table and insert lock record into it.
|
// Drop lock table, just to simulate racing threads for create lock table and insert lock record into it.
|
||||||
lockManager.getDBLock().destroyLockInfo();
|
lockManager.getDBLock().destroyLockInfo();
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ModelTest
|
public void simpleLockTest() throws Exception {
|
||||||
public void simpleLockTest(KeycloakSession session) throws Exception {
|
inComittedTransaction(1, (session , i) -> {
|
||||||
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionLC) -> {
|
DBLockProvider dbLock = new DBLockManager(session).getDBLock();
|
||||||
DBLockProvider dbLock = new DBLockManager(sessionLC).getDBLock();
|
|
||||||
dbLock.waitForLock(DBLockProvider.Namespace.DATABASE);
|
dbLock.waitForLock(DBLockProvider.Namespace.DATABASE);
|
||||||
try {
|
try {
|
||||||
Assert.assertEquals(DBLockProvider.Namespace.DATABASE, dbLock.getCurrentLock());
|
Assert.assertEquals(DBLockProvider.Namespace.DATABASE, dbLock.getCurrentLock());
|
||||||
|
@ -83,15 +74,15 @@ public class DBLockTest extends AbstractTestRealmKeycloakTest {
|
||||||
dbLock.releaseLock();
|
dbLock.releaseLock();
|
||||||
}
|
}
|
||||||
Assert.assertNull(dbLock.getCurrentLock());
|
Assert.assertNull(dbLock.getCurrentLock());
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ModelTest
|
public void simpleNestedLockTest() throws Exception {
|
||||||
public void simpleNestedLockTest(KeycloakSession session) throws Exception {
|
inComittedTransaction(1, (session , i) -> {
|
||||||
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionLC) -> {
|
|
||||||
// first session lock DATABASE
|
// first session lock DATABASE
|
||||||
DBLockProvider dbLock1 = new DBLockManager(sessionLC).getDBLock();
|
DBLockProvider dbLock1 = new DBLockManager(session).getDBLock();
|
||||||
dbLock1.waitForLock(DBLockProvider.Namespace.DATABASE);
|
dbLock1.waitForLock(DBLockProvider.Namespace.DATABASE);
|
||||||
try {
|
try {
|
||||||
Assert.assertEquals(DBLockProvider.Namespace.DATABASE, dbLock1.getCurrentLock());
|
Assert.assertEquals(DBLockProvider.Namespace.DATABASE, dbLock1.getCurrentLock());
|
||||||
|
@ -111,41 +102,84 @@ public class DBLockTest extends AbstractTestRealmKeycloakTest {
|
||||||
dbLock1.releaseLock();
|
dbLock1.releaseLock();
|
||||||
}
|
}
|
||||||
Assert.assertNull(dbLock1.getCurrentLock());
|
Assert.assertNull(dbLock1.getCurrentLock());
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ModelTest
|
public void testLockConcurrentlyGeneral() throws Exception {
|
||||||
public void testLockConcurrentlyGeneral(KeycloakSession session) throws Exception {
|
inComittedTransaction(1, (session , i) -> {
|
||||||
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionLC) -> {
|
testLockConcurrentlyInternal(session, DBLockProvider.Namespace.DATABASE);
|
||||||
testLockConcurrentlyInternal(sessionLC, DBLockProvider.Namespace.DATABASE);
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ModelTest
|
public void testLockConcurrentlyOffline() throws Exception {
|
||||||
public void testLockConcurrentlyOffline(KeycloakSession session) throws Exception {
|
inComittedTransaction(1, (session , i) -> {
|
||||||
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionLC) -> {
|
testLockConcurrentlyInternal(session, DBLockProvider.Namespace.OFFLINE_SESSIONS);
|
||||||
testLockConcurrentlyInternal(sessionLC, DBLockProvider.Namespace.OFFLINE_SESSIONS);
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ModelTest
|
public void testTwoLocksCurrently() throws Exception {
|
||||||
public void testTwoLocksCurrently(KeycloakSession session) throws Exception {
|
inComittedTransaction(1, (session , i) -> {
|
||||||
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionLC) -> {
|
testTwoLocksCurrentlyInternal(session, DBLockProvider.Namespace.DATABASE, DBLockProvider.Namespace.OFFLINE_SESSIONS);
|
||||||
testTwoLocksCurrentlyInternal(sessionLC, DBLockProvider.Namespace.DATABASE, DBLockProvider.Namespace.OFFLINE_SESSIONS);
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ModelTest
|
public void testTwoNestedLocksCurrently() throws Exception {
|
||||||
public void testTwoNestedLocksCurrently(KeycloakSession session) throws Exception {
|
inComittedTransaction(1, (session , i) -> {
|
||||||
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionLC) -> {
|
testTwoNestedLocksCurrentlyInternal(session, DBLockProvider.Namespace.KEYCLOAK_BOOT, DBLockProvider.Namespace.DATABASE);
|
||||||
testTwoNestedLocksCurrentlyInternal(sessionLC, DBLockProvider.Namespace.KEYCLOAK_BOOT, DBLockProvider.Namespace.DATABASE);
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void testLockConcurrentlyInternal(KeycloakSession sessionLC, DBLockProvider.Namespace lock) {
|
||||||
|
long startupTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
final Semaphore semaphore = new Semaphore();
|
||||||
|
final KeycloakSessionFactory sessionFactory = sessionLC.getKeycloakSessionFactory();
|
||||||
|
|
||||||
|
List<Thread> threads = new LinkedList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < THREADS_COUNT; i++) {
|
||||||
|
Thread thread = new Thread(() -> {
|
||||||
|
for (int j = 0; j < ITERATIONS_PER_THREAD; j++) {
|
||||||
|
try {
|
||||||
|
KeycloakModelUtils.runJobInTransaction(sessionFactory, session1 ->
|
||||||
|
lock(session1, lock, semaphore));
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
semaphore.setException(e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
threads.add(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
try {
|
||||||
|
thread.join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long took = (System.currentTimeMillis() - startupTime);
|
||||||
|
log.infof("DBLockTest executed in %d ms with total counter %d. THREADS_COUNT=%d, ITERATIONS_PER_THREAD=%d", took, semaphore.getTotal(), THREADS_COUNT, ITERATIONS_PER_THREAD);
|
||||||
|
|
||||||
|
Assert.assertEquals(THREADS_COUNT * ITERATIONS_PER_THREAD, semaphore.getTotal());
|
||||||
|
Assert.assertNull(semaphore.getException());
|
||||||
|
}
|
||||||
|
|
||||||
private void testTwoLocksCurrentlyInternal(KeycloakSession sessionLC, DBLockProvider.Namespace lock1, DBLockProvider.Namespace lock2) {
|
private void testTwoLocksCurrentlyInternal(KeycloakSession sessionLC, DBLockProvider.Namespace lock1, DBLockProvider.Namespace lock2) {
|
||||||
final Semaphore semaphore = new Semaphore();
|
final Semaphore semaphore = new Semaphore();
|
||||||
final KeycloakSessionFactory sessionFactory = sessionLC.getKeycloakSessionFactory();
|
final KeycloakSessionFactory sessionFactory = sessionLC.getKeycloakSessionFactory();
|
||||||
|
@ -218,48 +252,6 @@ public class DBLockTest extends AbstractTestRealmKeycloakTest {
|
||||||
Assert.assertNull(semaphore.getException());
|
Assert.assertNull(semaphore.getException());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testLockConcurrentlyInternal(KeycloakSession sessionLC, DBLockProvider.Namespace lock) {
|
|
||||||
long startupTime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
final Semaphore semaphore = new Semaphore();
|
|
||||||
final KeycloakSessionFactory sessionFactory = sessionLC.getKeycloakSessionFactory();
|
|
||||||
|
|
||||||
List<Thread> threads = new LinkedList<>();
|
|
||||||
|
|
||||||
for (int i = 0; i < THREADS_COUNT; i++) {
|
|
||||||
Thread thread = new Thread(() -> {
|
|
||||||
for (int j = 0; j < ITERATIONS_PER_THREAD; j++) {
|
|
||||||
try {
|
|
||||||
KeycloakModelUtils.runJobInTransaction(sessionFactory, session1 ->
|
|
||||||
lock(session1, lock, semaphore));
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
semaphore.setException(e);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
threads.add(thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Thread thread : threads) {
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
for (Thread thread : threads) {
|
|
||||||
try {
|
|
||||||
thread.join();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long took = (System.currentTimeMillis() - startupTime);
|
|
||||||
log.infof("DBLockTest executed in %d ms with total counter %d. THREADS_COUNT=%d, ITERATIONS_PER_THREAD=%d", took, semaphore.getTotal(), THREADS_COUNT, ITERATIONS_PER_THREAD);
|
|
||||||
|
|
||||||
Assert.assertEquals(THREADS_COUNT * ITERATIONS_PER_THREAD, semaphore.getTotal());
|
|
||||||
Assert.assertNull(semaphore.getException());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void lock(KeycloakSession session, DBLockProvider.Namespace lock, Semaphore semaphore) {
|
private void lock(KeycloakSession session, DBLockProvider.Namespace lock, Semaphore semaphore) {
|
||||||
DBLockProvider dbLock = new DBLockManager(session).getDBLock();
|
DBLockProvider dbLock = new DBLockManager(session).getDBLock();
|
||||||
dbLock.waitForLock(lock);
|
dbLock.waitForLock(lock);
|
||||||
|
@ -287,15 +279,11 @@ public class DBLockTest extends AbstractTestRealmKeycloakTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure just one thread is allowed to run at the same time
|
// Ensure just one thread is allowed to run at the same time
|
||||||
private class Semaphore {
|
private class Semaphore {
|
||||||
|
|
||||||
private AtomicInteger counter = new AtomicInteger(0);
|
private final AtomicInteger counter = new AtomicInteger(0);
|
||||||
private AtomicInteger totalIncreases = new AtomicInteger(0);
|
private final AtomicInteger totalIncreases = new AtomicInteger(0);
|
||||||
|
|
||||||
private volatile Exception exception = null;
|
private volatile Exception exception = null;
|
||||||
|
|
||||||
|
@ -332,8 +320,4 @@ public class DBLockTest extends AbstractTestRealmKeycloakTest {
|
||||||
return totalIncreases.get();
|
return totalIncreases.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,7 @@ import org.junit.rules.TestWatcher;
|
||||||
import org.junit.runner.Description;
|
import org.junit.runner.Description;
|
||||||
import org.junit.runners.model.Statement;
|
import org.junit.runners.model.Statement;
|
||||||
import org.keycloak.models.DeploymentStateProviderFactory;
|
import org.keycloak.models.DeploymentStateProviderFactory;
|
||||||
|
import org.keycloak.models.dblock.DBLockSpi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base of testcases that operate on session level. The tests derived from this class
|
* Base of testcases that operate on session level. The tests derived from this class
|
||||||
|
@ -198,6 +199,7 @@ public abstract class KeycloakModelTest {
|
||||||
.add(ClientSpi.class)
|
.add(ClientSpi.class)
|
||||||
.add(ComponentFactorySpi.class)
|
.add(ComponentFactorySpi.class)
|
||||||
.add(ClusterSpi.class)
|
.add(ClusterSpi.class)
|
||||||
|
.add(DBLockSpi.class)
|
||||||
.add(EventStoreSpi.class)
|
.add(EventStoreSpi.class)
|
||||||
.add(ExecutorsSpi.class)
|
.add(ExecutorsSpi.class)
|
||||||
.add(GroupSpi.class)
|
.add(GroupSpi.class)
|
||||||
|
|
|
@ -30,7 +30,6 @@ import org.keycloak.models.session.UserSessionPersisterSpi;
|
||||||
import org.keycloak.migration.MigrationProviderFactory;
|
import org.keycloak.migration.MigrationProviderFactory;
|
||||||
import org.keycloak.migration.MigrationSpi;
|
import org.keycloak.migration.MigrationSpi;
|
||||||
import org.keycloak.testsuite.model.KeycloakModelParameters;
|
import org.keycloak.testsuite.model.KeycloakModelParameters;
|
||||||
import org.keycloak.models.dblock.DBLockSpi;
|
|
||||||
import org.keycloak.models.jpa.JpaClientProviderFactory;
|
import org.keycloak.models.jpa.JpaClientProviderFactory;
|
||||||
import org.keycloak.models.jpa.JpaClientScopeProviderFactory;
|
import org.keycloak.models.jpa.JpaClientScopeProviderFactory;
|
||||||
import org.keycloak.models.jpa.JpaGroupProviderFactory;
|
import org.keycloak.models.jpa.JpaGroupProviderFactory;
|
||||||
|
@ -53,7 +52,6 @@ public class Jpa extends KeycloakModelParameters {
|
||||||
|
|
||||||
static final Set<Class<? extends Spi>> ALLOWED_SPIS = ImmutableSet.<Class<? extends Spi>>builder()
|
static final Set<Class<? extends Spi>> ALLOWED_SPIS = ImmutableSet.<Class<? extends Spi>>builder()
|
||||||
// jpa-specific
|
// jpa-specific
|
||||||
.add(DBLockSpi.class)
|
|
||||||
.add(JpaConnectionSpi.class)
|
.add(JpaConnectionSpi.class)
|
||||||
.add(JpaUpdaterSpi.class)
|
.add(JpaUpdaterSpi.class)
|
||||||
.add(LiquibaseConnectionSpi.class)
|
.add(LiquibaseConnectionSpi.class)
|
||||||
|
@ -105,6 +103,7 @@ public class Jpa extends KeycloakModelParameters {
|
||||||
.spi("user").defaultProvider("jpa")
|
.spi("user").defaultProvider("jpa")
|
||||||
.spi("realm").defaultProvider("jpa")
|
.spi("realm").defaultProvider("jpa")
|
||||||
.spi("deploymentState").defaultProvider("jpa")
|
.spi("deploymentState").defaultProvider("jpa")
|
||||||
|
.spi("dblock").defaultProvider("jpa")
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.keycloak.authorization.store.StoreFactorySpi;
|
||||||
import org.keycloak.models.DeploymentStateSpi;
|
import org.keycloak.models.DeploymentStateSpi;
|
||||||
import org.keycloak.models.UserLoginFailureSpi;
|
import org.keycloak.models.UserLoginFailureSpi;
|
||||||
import org.keycloak.models.UserSessionSpi;
|
import org.keycloak.models.UserSessionSpi;
|
||||||
|
import org.keycloak.models.dblock.NoLockingDBLockProviderFactory;
|
||||||
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory;
|
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory;
|
||||||
import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
|
import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
|
||||||
import org.keycloak.models.map.loginFailure.MapUserLoginFailureProviderFactory;
|
import org.keycloak.models.map.loginFailure.MapUserLoginFailureProviderFactory;
|
||||||
|
@ -64,6 +65,7 @@ public class Map extends KeycloakModelParameters {
|
||||||
.add(MapUserProviderFactory.class)
|
.add(MapUserProviderFactory.class)
|
||||||
.add(MapUserSessionProviderFactory.class)
|
.add(MapUserSessionProviderFactory.class)
|
||||||
.add(MapUserLoginFailureProviderFactory.class)
|
.add(MapUserLoginFailureProviderFactory.class)
|
||||||
|
.add(NoLockingDBLockProviderFactory.class)
|
||||||
|
|
||||||
.add(MapStorageProviderFactory.class)
|
.add(MapStorageProviderFactory.class)
|
||||||
.build();
|
.build();
|
||||||
|
@ -85,6 +87,7 @@ public class Map extends KeycloakModelParameters {
|
||||||
.spi("user").defaultProvider(MapUserProviderFactory.PROVIDER_ID)
|
.spi("user").defaultProvider(MapUserProviderFactory.PROVIDER_ID)
|
||||||
.spi(UserSessionSpi.NAME).defaultProvider(MapUserSessionProviderFactory.PROVIDER_ID)
|
.spi(UserSessionSpi.NAME).defaultProvider(MapUserSessionProviderFactory.PROVIDER_ID)
|
||||||
.spi(UserLoginFailureSpi.NAME).defaultProvider(MapUserLoginFailureProviderFactory.PROVIDER_ID)
|
.spi(UserLoginFailureSpi.NAME).defaultProvider(MapUserLoginFailureProviderFactory.PROVIDER_ID)
|
||||||
|
.spi("dblock").defaultProvider(NoLockingDBLockProviderFactory.PROVIDER_ID)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"dblock": {
|
||||||
|
"provider": "${keycloak.dblock.provider:jpa}"
|
||||||
|
},
|
||||||
|
|
||||||
"realm": {
|
"realm": {
|
||||||
"provider": "${keycloak.realm.provider:jpa}"
|
"provider": "${keycloak.realm.provider:jpa}"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue