net.javacrumbs.shedlock.provider.memcached.spy.MemcachedLockProvider Maven / Gradle / Ivy
package net.javacrumbs.shedlock.provider.memcached.spy;
import static net.javacrumbs.shedlock.support.Utils.getHostname;
import static net.javacrumbs.shedlock.support.Utils.toIsoString;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
import net.javacrumbs.shedlock.core.AbstractSimpleLock;
import net.javacrumbs.shedlock.core.ClockProvider;
import net.javacrumbs.shedlock.core.LockConfiguration;
import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.core.SimpleLock;
import net.javacrumbs.shedlock.support.LockException;
import net.javacrumbs.shedlock.support.annotation.NonNull;
import net.spy.memcached.MemcachedClient;
import net.spy.memcached.ops.OperationStatus;
import net.spy.memcached.util.StringUtils;
/**
* Lock Provider for Memcached
*
* @see memcached
*/
public class MemcachedLockProvider implements LockProvider {
/** KEY PREFIX */
private static final String KEY_PREFIX = "shedlock";
/** ENV DEFAULT */
private static final String ENV_DEFAULT = "default";
private final MemcachedClient client;
private final String env;
/**
* Create MemcachedLockProvider
*
* @param client
* Spy.memcached.MemcachedClient
*/
public MemcachedLockProvider(@NonNull MemcachedClient client) {
this(client, ENV_DEFAULT);
}
/**
* Create MemcachedLockProvider
*
* @param client
* Spy.memcached.MemcachedClient
* @param env
* is part of the key and thus makes sure there is not key conflict
* between multiple ShedLock instances running on the same memcached
*/
public MemcachedLockProvider(@NonNull MemcachedClient client, @NonNull String env) {
this.client = client;
this.env = env;
}
@Override
@NonNull
public Optional lock(@NonNull LockConfiguration lockConfiguration) {
long expireTime = getSecondUntil(lockConfiguration.getLockAtMostUntil());
String key = buildKey(lockConfiguration.getName(), this.env);
OperationStatus status = client.add(key, (int) expireTime, buildValue()).getStatus();
if (status.isSuccess()) {
return Optional.of(new MemcachedLock(key, client, lockConfiguration));
}
return Optional.empty();
}
private static long getSecondUntil(Instant instant) {
long millis = Duration.between(ClockProvider.now(), instant).toMillis();
return millis / 1000;
}
static String buildKey(String lockName, String env) {
String k = String.format("%s:%s:%s", KEY_PREFIX, env, lockName);
StringUtils.validateKey(k, false);
return k;
}
private static String buildValue() {
return String.format("ADDED:%s@%s", toIsoString(ClockProvider.now()), getHostname());
}
private static final class MemcachedLock extends AbstractSimpleLock {
private final String key;
private final MemcachedClient client;
private MemcachedLock(
@NonNull String key, @NonNull MemcachedClient client, @NonNull LockConfiguration lockConfiguration) {
super(lockConfiguration);
this.key = key;
this.client = client;
}
@Override
protected void doUnlock() {
long keepLockFor = getSecondUntil(lockConfiguration.getLockAtLeastUntil());
if (keepLockFor <= 0) {
OperationStatus status = client.delete(key).getStatus();
if (!status.isSuccess()) {
throw new LockException("Can not remove node. " + status.getMessage());
}
} else {
OperationStatus status =
client.replace(key, (int) keepLockFor, buildValue()).getStatus();
if (!status.isSuccess()) {
throw new LockException("Can not replace node. " + status.getMessage());
}
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy