Fix testReleaseAllLocksMethod timing out intermittently

Closes #17337
This commit is contained in:
Michal Hajas 2023-03-01 12:22:49 +01:00 committed by Hynek Mlnařík
parent 4c4015cf0b
commit e02c95f9d3

View file

@ -34,6 +34,7 @@ import java.util.Random;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
@ -128,20 +129,23 @@ public class GlobalLocksTest extends KeycloakModelTest {
@Test @Test
public void testReleaseAllLocksMethod() throws InterruptedException { public void testReleaseAllLocksMethod() throws InterruptedException {
final int NUMBER_OF_THREADS = 4; final int numberOfThreads = 4;
ExecutorService executor = Executors.newFixedThreadPool(NUMBER_OF_THREADS); ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads);
CountDownLatch locksAcquired = new CountDownLatch(NUMBER_OF_THREADS); CountDownLatch locksAcquired = new CountDownLatch(numberOfThreads);
CountDownLatch testFinished = new CountDownLatch(1); CountDownLatch testFinished = new CountDownLatch(1);
LOG.info("Initial locks acquiring phase.");
try { try {
// Acquire locks and let the threads wait until the end of this test method // Acquire locks and let the threads wait until the end of this test method
executor.submit(() -> { for (int index = 0; index < numberOfThreads; index++) {
IntStream.range(0, NUMBER_OF_THREADS).parallel() final int i = index;
.forEach(i -> executor.submit(() ->
inComittedTransaction(s -> { inComittedTransaction(s -> {
GlobalLockProvider lockProvider = s.getProvider(GlobalLockProvider.class); GlobalLockProvider lockProvider = s.getProvider(GlobalLockProvider.class);
LOG.infof("Acquiring LOCK_%d", i);
lockProvider.withLock("LOCK_" + i, session -> { lockProvider.withLock("LOCK_" + i, session -> {
LOG.infof("Lock LOCK_%d acquired.", i);
locksAcquired.countDown(); locksAcquired.countDown();
try { try {
testFinished.await(); testFinished.await();
@ -151,50 +155,62 @@ public class GlobalLocksTest extends KeycloakModelTest {
} }
return null; return null;
}); });
LOG.infof("Initial acquiring tx finished for lock LOCK_%d", i);
}) })
); );
}); }
locksAcquired.await(); if (!locksAcquired.await(5, TimeUnit.MINUTES)) {
throw new RuntimeException("Acquiring locks phase took too long.");
}
LOG.info("Expecting timeouts for each lock.");
// Test no lock can be acquired because all are still hold by the executor above // Test no lock can be acquired because all are still hold by the executor above
AtomicInteger counter = new AtomicInteger(); AtomicInteger counter = new AtomicInteger();
IntStream.range(0, NUMBER_OF_THREADS).parallel() for (int index = 0; index < numberOfThreads; index++) {
.forEach(i -> final int i = index;
inComittedTransaction(s -> { inComittedTransaction(s -> {
GlobalLockProvider lockProvider = s.getProvider(GlobalLockProvider.class); GlobalLockProvider lockProvider = s.getProvider(GlobalLockProvider.class);
try { try {
LOG.infof("Attempt to acquire LOCK_%d.", i);
lockProvider.withLock("LOCK_" + i, Duration.ofSeconds(1), is -> { lockProvider.withLock("LOCK_" + i, Duration.ofSeconds(1), is -> {
throw new RuntimeException("Acquiring lock should not succeed as it was acquired in the first transaction"); throw new RuntimeException("Acquiring lock should not succeed as it was acquired in the first transaction");
}); });
} catch (LockAcquiringTimeoutException e) { } catch (LockAcquiringTimeoutException e) {
LOG.infof("Timeout was successfully received for LOCK_%d", i);
counter.incrementAndGet(); counter.incrementAndGet();
} }
}) });
); }
assertThat(counter.get(), Matchers.equalTo(NUMBER_OF_THREADS));
assertThat(counter.get(), Matchers.equalTo(numberOfThreads));
// Unlock all locks forcefully // Unlock all locks forcefully
inComittedTransaction(s -> { inComittedTransaction(s -> {
GlobalLockProvider lockProvider = s.getProvider(GlobalLockProvider.class); GlobalLockProvider lockProvider = s.getProvider(GlobalLockProvider.class);
LOG.infof("Releasing all locks", Thread.currentThread().getName());
lockProvider.forceReleaseAllLocks(); lockProvider.forceReleaseAllLocks();
}); });
// Test all locks can be acquired again // Test all locks can be acquired again
counter.set(0); counter.set(0);
IntStream.range(0, NUMBER_OF_THREADS).parallel() for (int index = 0; index < numberOfThreads; index++) {
.forEach(i -> final int i = index;
inComittedTransaction(s -> { inComittedTransaction(s -> {
GlobalLockProvider lockProvider = s.getProvider(GlobalLockProvider.class); GlobalLockProvider lockProvider = s.getProvider(GlobalLockProvider.class);
try { try {
lockProvider.withLock("LOCK_" + i, Duration.ofSeconds(1), is -> counter.incrementAndGet()); lockProvider.withLock("LOCK_" + i, Duration.ofSeconds(1), is -> {
LOG.infof("Lock LOCK_%d acquired again.", i);
counter.incrementAndGet();
return null;
});
} catch (LockAcquiringTimeoutException e) { } catch (LockAcquiringTimeoutException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
}) });
); }
assertThat(counter.get(), Matchers.equalTo(NUMBER_OF_THREADS)); assertThat(counter.get(), Matchers.equalTo(numberOfThreads));
} finally { } finally {
testFinished.countDown(); testFinished.countDown();
executor.shutdown(); executor.shutdown();