use of org.redisson.api.RLock in project redisson by redisson.
the class RedissonFairLockTest method testFirstThreadDeathTimeoutDrift.
@Test
public void testFirstThreadDeathTimeoutDrift() 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 < 10; 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);
}
});
// 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}"));
for (int i = 0; i < queue.size(); i++) {
long timeout = queue.get(i);
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
Assertions.assertFalse(epiry > leaseTimeSeconds + 60 * 5 * (i + 1), "It would take more than " + (leaseTimeSeconds + 60 * 5 * (i + 1)) + "s to get the lock!");
}
}
use of org.redisson.api.RLock in project redisson by redisson.
the class RedissonFairLockTest method testIsLockedOtherThread.
@Test
public void testIsLockedOtherThread() throws InterruptedException {
RLock lock = redisson.getFairLock("lock");
lock.lock();
Thread t = new Thread() {
public void run() {
RLock lock = redisson.getFairLock("lock");
Assertions.assertTrue(lock.isLocked());
}
};
t.start();
t.join();
lock.unlock();
Thread t2 = new Thread() {
public void run() {
RLock lock = redisson.getFairLock("lock");
Assertions.assertFalse(lock.isLocked());
}
};
t2.start();
t2.join();
}
use of org.redisson.api.RLock in project redisson by redisson.
the class RedissonLockTest method testSubscriptionsPerConnection.
@Test
public void testSubscriptionsPerConnection() throws InterruptedException, IOException {
RedisRunner.RedisProcess runner = new RedisRunner().port(RedisRunner.findFreePort()).nosave().randomDir().run();
Config config = new Config();
config.useSingleServer().setSubscriptionConnectionPoolSize(1).setSubscriptionConnectionMinimumIdleSize(1).setSubscriptionsPerConnection(1).setAddress(runner.getRedisServerAddressAndPort());
RedissonClient redisson = Redisson.create(config);
ExecutorService e = Executors.newFixedThreadPool(32);
AtomicInteger errors = new AtomicInteger();
AtomicInteger ops = new AtomicInteger();
for (int i = 0; i < 5000; i++) {
int j = i;
e.submit(() -> {
try {
String lockKey = "lock-" + ThreadLocalRandom.current().nextInt(5);
RLock lock = redisson.getLock(lockKey);
lock.lock();
Thread.sleep(ThreadLocalRandom.current().nextInt(20));
lock.unlock();
ops.incrementAndGet();
} catch (Exception exception) {
exception.printStackTrace();
if (exception instanceof RedisTimeoutException) {
return;
}
errors.incrementAndGet();
}
});
}
e.shutdown();
assertThat(e.awaitTermination(150, TimeUnit.SECONDS)).isTrue();
assertThat(errors.get()).isZero();
RedisClientConfig cc = new RedisClientConfig();
cc.setAddress(runner.getRedisServerAddressAndPort());
RedisClient c = RedisClient.create(cc);
RedisConnection ccc = c.connect();
List<String> channels = ccc.sync(RedisCommands.PUBSUB_CHANNELS);
assertThat(channels).isEmpty();
c.shutdown();
redisson.shutdown();
runner.stop();
}
use of org.redisson.api.RLock in project redisson by redisson.
the class RedissonLockTest method testLockIsNotRenewedAfterInterruptedTryLock.
@Test
public void testLockIsNotRenewedAfterInterruptedTryLock() throws InterruptedException {
final CountDownLatch countDownLatch = new CountDownLatch(1);
RLock lock = redisson.getLock("myLock");
assertThat(lock.isLocked()).isFalse();
Thread thread = new Thread(() -> {
countDownLatch.countDown();
if (!lock.tryLock()) {
return;
}
lock.unlock();
});
thread.start();
countDownLatch.await();
// let the tcp request be sent out
TimeUnit.MILLISECONDS.sleep(5);
thread.interrupt();
TimeUnit.SECONDS.sleep(45);
assertThat(lock.isLocked()).isFalse();
}
use of org.redisson.api.RLock in project redisson by redisson.
the class RedissonLockTest method testRedisFailed.
@Test
public void testRedisFailed() {
Assertions.assertThrows(WriteRedisConnectionException.class, () -> {
RedisRunner.RedisProcess master = new RedisRunner().port(6377).nosave().randomDir().run();
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6377");
RedissonClient redisson = Redisson.create(config);
RLock lock = redisson.getLock("myLock");
// kill RedisServer while main thread is sleeping.
master.stop();
Thread.sleep(3000);
lock.tryLock(5, 10, TimeUnit.SECONDS);
});
}
Aggregations