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/provider=map:add(enabled=true,properties={resourcesVersionSeed=1JZ379bzyOCFA})
|
||||
/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 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.UserModel;
|
||||
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.PostMigrationEvent;
|
||||
import org.keycloak.models.utils.RepresentationToModel;
|
||||
|
@ -119,12 +121,27 @@ public class KeycloakApplication extends Application {
|
|||
}
|
||||
|
||||
protected void startup() {
|
||||
this.sessionFactory = createSessionFactory();
|
||||
KeycloakApplication.sessionFactory = createSessionFactory();
|
||||
|
||||
ExportImportManager exportImportManager = bootstrap();
|
||||
ExportImportManager[] exportImportManager = new ExportImportManager[1];
|
||||
|
||||
if (exportImportManager.isRunExport()) {
|
||||
exportImportManager.runExport();
|
||||
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
||||
@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() {
|
||||
|
|
|
@ -1188,6 +1188,7 @@
|
|||
<systemPropertyVariables>
|
||||
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
|
||||
<keycloak.mapStorage.provider>concurrenthashmap</keycloak.mapStorage.provider>
|
||||
<keycloak.dblock.provider>none</keycloak.dblock.provider>
|
||||
<keycloak.realm.provider>map</keycloak.realm.provider>
|
||||
<keycloak.client.provider>map</keycloak.client.provider>
|
||||
<keycloak.clientScope.provider>map</keycloak.clientScope.provider>
|
||||
|
|
|
@ -43,6 +43,10 @@
|
|||
}
|
||||
},
|
||||
|
||||
"dblock": {
|
||||
"provider": "${keycloak.dblock.provider:jpa}"
|
||||
},
|
||||
|
||||
"realm": {
|
||||
"provider": "${keycloak.realm.provider:jpa}"
|
||||
},
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
|
||||
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.junit.Assert;
|
||||
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.DBLockProviderFactory;
|
||||
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>
|
||||
*/
|
||||
@AuthServerContainerExclude(AuthServer.REMOTE)
|
||||
public class DBLockTest extends AbstractTestRealmKeycloakTest {
|
||||
@RequireProvider(value=DBLockProvider.class, only="jpa")
|
||||
public class DBLockTest extends KeycloakModelTest {
|
||||
|
||||
private static final Logger log = Logger.getLogger(DBLockTest.class);
|
||||
|
||||
|
@ -58,8 +51,7 @@ public class DBLockTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
|
||||
testingClient.server().run(session -> {
|
||||
inComittedTransaction(1, (session , i) -> {
|
||||
// Set timeouts for testing
|
||||
DBLockManager lockManager = new DBLockManager(session);
|
||||
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.
|
||||
lockManager.getDBLock().destroyLockInfo();
|
||||
return null;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@ModelTest
|
||||
public void simpleLockTest(KeycloakSession session) throws Exception {
|
||||
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionLC) -> {
|
||||
DBLockProvider dbLock = new DBLockManager(sessionLC).getDBLock();
|
||||
public void simpleLockTest() throws Exception {
|
||||
inComittedTransaction(1, (session , i) -> {
|
||||
DBLockProvider dbLock = new DBLockManager(session).getDBLock();
|
||||
dbLock.waitForLock(DBLockProvider.Namespace.DATABASE);
|
||||
try {
|
||||
Assert.assertEquals(DBLockProvider.Namespace.DATABASE, dbLock.getCurrentLock());
|
||||
|
@ -83,15 +74,15 @@ public class DBLockTest extends AbstractTestRealmKeycloakTest {
|
|||
dbLock.releaseLock();
|
||||
}
|
||||
Assert.assertNull(dbLock.getCurrentLock());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@ModelTest
|
||||
public void simpleNestedLockTest(KeycloakSession session) throws Exception {
|
||||
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionLC) -> {
|
||||
public void simpleNestedLockTest() throws Exception {
|
||||
inComittedTransaction(1, (session , i) -> {
|
||||
// first session lock DATABASE
|
||||
DBLockProvider dbLock1 = new DBLockManager(sessionLC).getDBLock();
|
||||
DBLockProvider dbLock1 = new DBLockManager(session).getDBLock();
|
||||
dbLock1.waitForLock(DBLockProvider.Namespace.DATABASE);
|
||||
try {
|
||||
Assert.assertEquals(DBLockProvider.Namespace.DATABASE, dbLock1.getCurrentLock());
|
||||
|
@ -111,41 +102,84 @@ public class DBLockTest extends AbstractTestRealmKeycloakTest {
|
|||
dbLock1.releaseLock();
|
||||
}
|
||||
Assert.assertNull(dbLock1.getCurrentLock());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@ModelTest
|
||||
public void testLockConcurrentlyGeneral(KeycloakSession session) throws Exception {
|
||||
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionLC) -> {
|
||||
testLockConcurrentlyInternal(sessionLC, DBLockProvider.Namespace.DATABASE);
|
||||
public void testLockConcurrentlyGeneral() throws Exception {
|
||||
inComittedTransaction(1, (session , i) -> {
|
||||
testLockConcurrentlyInternal(session, DBLockProvider.Namespace.DATABASE);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@ModelTest
|
||||
public void testLockConcurrentlyOffline(KeycloakSession session) throws Exception {
|
||||
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionLC) -> {
|
||||
testLockConcurrentlyInternal(sessionLC, DBLockProvider.Namespace.OFFLINE_SESSIONS);
|
||||
public void testLockConcurrentlyOffline() throws Exception {
|
||||
inComittedTransaction(1, (session , i) -> {
|
||||
testLockConcurrentlyInternal(session, DBLockProvider.Namespace.OFFLINE_SESSIONS);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@ModelTest
|
||||
public void testTwoLocksCurrently(KeycloakSession session) throws Exception {
|
||||
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionLC) -> {
|
||||
testTwoLocksCurrentlyInternal(sessionLC, DBLockProvider.Namespace.DATABASE, DBLockProvider.Namespace.OFFLINE_SESSIONS);
|
||||
public void testTwoLocksCurrently() throws Exception {
|
||||
inComittedTransaction(1, (session , i) -> {
|
||||
testTwoLocksCurrentlyInternal(session, DBLockProvider.Namespace.DATABASE, DBLockProvider.Namespace.OFFLINE_SESSIONS);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@ModelTest
|
||||
public void testTwoNestedLocksCurrently(KeycloakSession session) throws Exception {
|
||||
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionLC) -> {
|
||||
testTwoNestedLocksCurrentlyInternal(sessionLC, DBLockProvider.Namespace.KEYCLOAK_BOOT, DBLockProvider.Namespace.DATABASE);
|
||||
public void testTwoNestedLocksCurrently() throws Exception {
|
||||
inComittedTransaction(1, (session , i) -> {
|
||||
testTwoNestedLocksCurrentlyInternal(session, 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) {
|
||||
final Semaphore semaphore = new Semaphore();
|
||||
final KeycloakSessionFactory sessionFactory = sessionLC.getKeycloakSessionFactory();
|
||||
|
@ -218,48 +252,6 @@ public class DBLockTest extends AbstractTestRealmKeycloakTest {
|
|||
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) {
|
||||
DBLockProvider dbLock = new DBLockManager(session).getDBLock();
|
||||
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
|
||||
private class Semaphore {
|
||||
|
||||
private AtomicInteger counter = new AtomicInteger(0);
|
||||
private AtomicInteger totalIncreases = new AtomicInteger(0);
|
||||
private final AtomicInteger counter = new AtomicInteger(0);
|
||||
private final AtomicInteger totalIncreases = new AtomicInteger(0);
|
||||
|
||||
private volatile Exception exception = null;
|
||||
|
||||
|
@ -332,8 +320,4 @@ public class DBLockTest extends AbstractTestRealmKeycloakTest {
|
|||
return totalIncreases.get();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -83,6 +83,7 @@ import org.junit.rules.TestWatcher;
|
|||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
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
|
||||
|
@ -198,6 +199,7 @@ public abstract class KeycloakModelTest {
|
|||
.add(ClientSpi.class)
|
||||
.add(ComponentFactorySpi.class)
|
||||
.add(ClusterSpi.class)
|
||||
.add(DBLockSpi.class)
|
||||
.add(EventStoreSpi.class)
|
||||
.add(ExecutorsSpi.class)
|
||||
.add(GroupSpi.class)
|
||||
|
|
|
@ -30,7 +30,6 @@ import org.keycloak.models.session.UserSessionPersisterSpi;
|
|||
import org.keycloak.migration.MigrationProviderFactory;
|
||||
import org.keycloak.migration.MigrationSpi;
|
||||
import org.keycloak.testsuite.model.KeycloakModelParameters;
|
||||
import org.keycloak.models.dblock.DBLockSpi;
|
||||
import org.keycloak.models.jpa.JpaClientProviderFactory;
|
||||
import org.keycloak.models.jpa.JpaClientScopeProviderFactory;
|
||||
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()
|
||||
// jpa-specific
|
||||
.add(DBLockSpi.class)
|
||||
.add(JpaConnectionSpi.class)
|
||||
.add(JpaUpdaterSpi.class)
|
||||
.add(LiquibaseConnectionSpi.class)
|
||||
|
@ -105,6 +103,7 @@ public class Jpa extends KeycloakModelParameters {
|
|||
.spi("user").defaultProvider("jpa")
|
||||
.spi("realm").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.UserLoginFailureSpi;
|
||||
import org.keycloak.models.UserSessionSpi;
|
||||
import org.keycloak.models.dblock.NoLockingDBLockProviderFactory;
|
||||
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory;
|
||||
import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
|
||||
import org.keycloak.models.map.loginFailure.MapUserLoginFailureProviderFactory;
|
||||
|
@ -64,6 +65,7 @@ public class Map extends KeycloakModelParameters {
|
|||
.add(MapUserProviderFactory.class)
|
||||
.add(MapUserSessionProviderFactory.class)
|
||||
.add(MapUserLoginFailureProviderFactory.class)
|
||||
.add(NoLockingDBLockProviderFactory.class)
|
||||
|
||||
.add(MapStorageProviderFactory.class)
|
||||
.build();
|
||||
|
@ -85,6 +87,7 @@ public class Map extends KeycloakModelParameters {
|
|||
.spi("user").defaultProvider(MapUserProviderFactory.PROVIDER_ID)
|
||||
.spi(UserSessionSpi.NAME).defaultProvider(MapUserSessionProviderFactory.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": {
|
||||
"provider": "${keycloak.realm.provider:jpa}"
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue