use of org.redisson.api.RLock in project redisson by redisson.
the class RedissonFairLockTest method testIsLocked.
@Test
public void testIsLocked() {
RLock lock = redisson.getFairLock("lock");
Assertions.assertFalse(lock.isLocked());
lock.lock();
Assertions.assertTrue(lock.isLocked());
lock.unlock();
Assertions.assertFalse(lock.isLocked());
}
use of org.redisson.api.RLock in project redisson by redisson.
the class RedissonFairLockTest method testMultipleLocks.
@Test
public void testMultipleLocks() throws InterruptedException {
ExecutorService executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 1000L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
Config cfg = createConfig();
cfg.useSingleServer().setSubscriptionsPerConnection(100);
RedissonClient redisson = Redisson.create(cfg);
AtomicInteger acquiredLocks = new AtomicInteger();
for (int i = 0; i < 500; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
RLock test = redisson.getFairLock("lock");
try {
test.lock(5, TimeUnit.SECONDS);
try {
// 200ms
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
acquiredLocks.incrementAndGet();
} finally {
test.unlock();
}
}
});
}
executorService.shutdown();
assertThat(executorService.awaitTermination(3, TimeUnit.MINUTES)).isTrue();
assertThat(acquiredLocks.get()).isEqualTo(500);
redisson.shutdown();
}
use of org.redisson.api.RLock in project redisson by redisson.
the class RedissonFairLockTest method testWaitTimeoutDrift.
@Test
public void testWaitTimeoutDrift() throws Exception {
int leaseTimeSeconds = 30;
RLock lock = redisson.getFairLock("test-fair-lock");
AtomicBoolean lastThreadTryingToLock = new AtomicBoolean(false);
// create a scenario where the same 3 threads keep on trying to get a lock
// to exacerbate the problem, use a very short wait time and a long lease time
// this will end up setting the queue timeout score to a value far away in the future
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 50; i++) {
final int finalI = i;
executor.submit(() -> {
log.info("running " + finalI + " in thread " + Thread.currentThread().getId());
try {
if (lock.tryLock(500, leaseTimeSeconds * 1000, TimeUnit.MILLISECONDS)) {
log.info("Lock taken by thread " + Thread.currentThread().getId());
Thread.sleep(10000);
try {
// this could fail before use sleep for the same value as the lock expiry, that's fine
// for the purpose of this test
lock.unlock();
log.info("Lock released by thread " + Thread.currentThread().getId());
} catch (Exception ignored) {
}
}
} catch (InterruptedException ex) {
log.warn("Interrupted " + Thread.currentThread().getId());
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
}
});
// attempting to lock do so in a staggered pattern. This delay will be carried over by the thread pool.
if (i < 3) {
Thread.sleep(50);
}
}
// we now launch one more thread and kill it before it manages to fail and clean up
// that thread will end up with a timeout that will prevent any others from taking the lock for a long time
executor.submit(() -> {
log.info("Final thread trying to take the lock with thread id: " + Thread.currentThread().getId());
try {
lastThreadTryingToLock.set(true);
if (lock.tryLock(30000, 30000, TimeUnit.MILLISECONDS)) {
log.info("Lock taken by final thread " + Thread.currentThread().getId());
Thread.sleep(1000);
lock.unlock();
log.info("Lock released by final thread " + Thread.currentThread().getId());
}
} catch (InterruptedException ex) {
log.warn("Interrupted " + Thread.currentThread().getId());
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
}
});
// now we wait for all others threads to stop trying, and only the last thread is running
while (!lastThreadTryingToLock.get()) {
Thread.sleep(100);
}
// try to kill that last thread, and don't let it clean up after itself
executor.shutdownNow();
// force the lock to unlock just in case
try {
lock.forceUnlock();
} catch (Exception e) {
log.error(e.getMessage(), e);
}
if (lock.isLocked()) {
Assertions.fail("Lock should have been unlocked by now");
}
// check the timeout scores - they should all be within a reasonable amount of time from now
List<Long> queue = redisson.getScript(LongCodec.INSTANCE).eval(RScript.Mode.READ_ONLY, "local result = {}; " + "local timeouts = redis.call('zrange', KEYS[1], 0, 99, 'WITHSCORES'); " + "for i=1,#timeouts,2 do " + "table.insert(result, timeouts[i+1]); " + "end; " + "return result; ", RScript.ReturnType.MULTI, Collections.singletonList("redisson_lock_timeout:{test-fair-lock}"));
int i = 0;
for (Long timeout : queue) {
long epiry = ((timeout - new Date().getTime()) / 1000);
log.info("Item " + (i++) + " expires in " + epiry + " seconds");
// the Redisson library uses this 60000*5ms delay in the code
if (epiry > leaseTimeSeconds + 60 * 5) {
Assertions.fail("It would take more than " + leaseTimeSeconds + "s to get the lock!");
}
}
}
use of org.redisson.api.RLock in project redisson by redisson.
the class RedissonFairLockTest method testLockAcquiredTimeoutDrift.
@Test
public void testLockAcquiredTimeoutDrift() throws Exception {
int leaseTimeSeconds = 30;
RLock lock = redisson.getFairLock("test-fair-lock");
// create a scenario where the same 3 threads keep on trying to get a lock
// to exacerbate the problem, use a very short wait time and a long lease time
// this will end up setting the queue timeout score to a value far away in the future
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
final int finalI = i;
executor.submit(() -> {
log.info("running " + finalI + " in thread " + Thread.currentThread().getId());
try {
if (lock.tryLock(3000, leaseTimeSeconds * 1000, TimeUnit.MILLISECONDS)) {
log.info("Lock taken by thread " + Thread.currentThread().getId());
Thread.sleep(100);
try {
// this could fail before use sleep for the same value as the lock expiry, that's fine
// for the purpose of this test
lock.unlock();
log.info("Lock released by thread " + Thread.currentThread().getId());
} catch (Exception ignored) {
}
}
} catch (InterruptedException ex) {
log.warn("Interrupted " + Thread.currentThread().getId());
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
}
});
// for the first 3 threads, add a 50ms delay. This is to recreate the worst case scenario, where all threads
// attempting to lock do so in a staggered pattern. This delay will be carried over by the thread pool.
Thread.sleep(50);
}
AtomicBoolean lastThreadTryingToLock = new AtomicBoolean(false);
// we now launch one more thread and kill it before it manages to fail and clean up
// that thread will end up with a timeout that will prevent any others from taking the lock for a long time
executor.submit(() -> {
log.info("Final thread trying to take the lock with thread id: " + Thread.currentThread().getId());
try {
lastThreadTryingToLock.set(true);
if (lock.tryLock(30000, 30000, TimeUnit.MILLISECONDS)) {
log.info("Lock taken by final thread " + Thread.currentThread().getId());
Thread.sleep(1000);
lock.unlock();
log.info("Lock released by final thread " + Thread.currentThread().getId());
}
} catch (InterruptedException ex) {
log.warn("Interrupted");
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
}
});
// now we wait for all others threads to stop trying, and only the last thread is running
while (!lastThreadTryingToLock.get()) {
Thread.sleep(100);
}
// try to kill that last thread, and don't let it clean up after itself
executor.shutdownNow();
// force the lock to unlock just in case
try {
lock.forceUnlock();
} catch (Exception e) {
log.error(e.getMessage(), e);
}
if (lock.isLocked()) {
Assertions.fail("Lock should have been unlocked by now");
}
// check the timeout scores - they should all be within a reasonable amount of time from now
List<Long> queue = redisson.getScript(LongCodec.INSTANCE).eval(RScript.Mode.READ_ONLY, "local result = {}; " + "local timeouts = redis.call('zrange', KEYS[1], 0, 99, 'WITHSCORES'); " + "for i=1,#timeouts,2 do " + "table.insert(result, timeouts[i+1]); " + "end; " + "return result; ", RScript.ReturnType.MULTI, Collections.singletonList("redisson_lock_timeout:{test-fair-lock}"));
int i = 0;
for (Long timeout : queue) {
long epiry = ((timeout - new Date().getTime()) / 1000);
log.info("Item " + (i++) + " expires in " + epiry + " seconds");
// the Redisson library uses this 5000ms delay in the code
if (epiry > leaseTimeSeconds + 60 * 5) {
Assertions.fail("It would take more than " + leaseTimeSeconds + "s to get the lock!");
}
}
}
use of org.redisson.api.RLock in project redisson by redisson.
the class RedissonFairLockTest method testGetHoldCount.
@Test
public void testGetHoldCount() {
RLock lock = redisson.getFairLock("lock");
Assertions.assertEquals(0, lock.getHoldCount());
lock.lock();
Assertions.assertEquals(1, lock.getHoldCount());
lock.unlock();
Assertions.assertEquals(0, lock.getHoldCount());
lock.lock();
lock.lock();
Assertions.assertEquals(2, lock.getHoldCount());
lock.unlock();
Assertions.assertEquals(1, lock.getHoldCount());
lock.unlock();
Assertions.assertEquals(0, lock.getHoldCount());
}
Aggregations