/*******************************************************************************
* Copyright (c) 2016 Sierra Wireless and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Sierra Wireless - initial API and implementation
*******************************************************************************/
package org.eclipse.leshan.server.cluster;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.util.Arrays;
import java.util.Random;
import redis.clients.jedis.Jedis;
/**
* Utility class providing locking methods based on the Redis SETNX primitive (see
* http://redis.io/topics/distlock#correct-implementation-with-a-single-instance for more information).
*/
public class RedisLock {
private static final byte[] NX_OPTION = "NX".getBytes(UTF_8); // set the key if it does not already exist
private static final byte[] PX_OPTION = "PX".getBytes(UTF_8); // expire time in millisecond
private static final Random RND = new Random();
/**
* Acquires a lock for the given key.
*
* @param j a Redis connection
* @param lockKey the key to use as lock
* @return a lock value that must be used to release the lock.
*/
public static byte[] acquire(Jedis j, byte[] lockKey) {
long start = System.currentTimeMillis();
byte[] randomLockValue = new byte[10];
RND.nextBytes(randomLockValue);
// setnx with a 500ms expiration
while (!"OK".equals(j.set(lockKey, randomLockValue, NX_OPTION, PX_OPTION, 500))) {
if (System.currentTimeMillis() - start > 5_000L)
throw new IllegalStateException("Could not acquire a lock from redis");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
}
return randomLockValue;
}
/**
* Releases a lock for a given key and value.
*
* @param j a Redis connection
* @param lockKey the locked key
* @param lockValue the value returned when the lock was acquired
*/
public static void release(Jedis j, byte[] lockKey, byte[] lockValue) {
if (lockValue != null) {
if (Arrays.equals(j.get(lockKey), lockValue)) {
j.del(lockKey);
}
}
}
}