|
|
|
@ -27,8 +27,7 @@ import org.redisson.command.CommandAsyncExecutor;
|
|
|
|
|
import org.redisson.misc.CompletableFutureWrapper;
|
|
|
|
|
import org.redisson.pubsub.SemaphorePubSub;
|
|
|
|
|
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
import java.util.concurrent.*;
|
|
|
|
|
import java.util.concurrent.atomic.AtomicLong;
|
|
|
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
|
@ -56,23 +55,25 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String acquire() throws InterruptedException {
|
|
|
|
|
return acquire(1, -1, TimeUnit.MILLISECONDS);
|
|
|
|
|
return acquire(-1, TimeUnit.MILLISECONDS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public List<String> acquire(int permits) throws InterruptedException {
|
|
|
|
|
return acquire(permits, -1, TimeUnit.MILLISECONDS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String acquire(long leaseTime, TimeUnit timeUnit) throws InterruptedException {
|
|
|
|
|
return acquire(1, leaseTime, timeUnit);
|
|
|
|
|
List<String> ids = acquire(1, leaseTime, timeUnit);
|
|
|
|
|
return getFirstOrNull(ids);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public RFuture<String> acquireAsync(long leaseTime, TimeUnit timeUnit) {
|
|
|
|
|
return acquireAsync(1, leaseTime, timeUnit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String acquire(int permits, long ttl, TimeUnit timeUnit) throws InterruptedException {
|
|
|
|
|
String permitId = tryAcquire(permits, ttl, timeUnit);
|
|
|
|
|
if (permitId != null && !permitId.startsWith(":")) {
|
|
|
|
|
return permitId;
|
|
|
|
|
public List<String> acquire(int permits, long leaseTime, TimeUnit timeUnit) throws InterruptedException {
|
|
|
|
|
List<String> ids = tryAcquire(permits, leaseTime, timeUnit);
|
|
|
|
|
if (!ids.isEmpty() && !hasOnlyNearestTimeout(ids)) {
|
|
|
|
|
return ids;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CompletableFuture<RedissonLockEntry> future = subscribe();
|
|
|
|
@ -81,15 +82,13 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
try {
|
|
|
|
|
while (true) {
|
|
|
|
|
Long nearestTimeout;
|
|
|
|
|
permitId = tryAcquire(permits, ttl, timeUnit);
|
|
|
|
|
if (permitId != null) {
|
|
|
|
|
if (!permitId.startsWith(":")) {
|
|
|
|
|
return permitId;
|
|
|
|
|
} else {
|
|
|
|
|
nearestTimeout = Long.valueOf(permitId.substring(1)) - System.currentTimeMillis();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ids = tryAcquire(permits, leaseTime, timeUnit);
|
|
|
|
|
if (ids.isEmpty()) {
|
|
|
|
|
nearestTimeout = null;
|
|
|
|
|
} else if (hasOnlyNearestTimeout(ids)) {
|
|
|
|
|
nearestTimeout = Long.parseLong(ids.get(0).substring(1)) - System.currentTimeMillis();
|
|
|
|
|
} else {
|
|
|
|
|
return ids;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nearestTimeout != null) {
|
|
|
|
@ -101,32 +100,47 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
} finally {
|
|
|
|
|
unsubscribe(entry);
|
|
|
|
|
}
|
|
|
|
|
// return get(acquireAsync(permits, ttl, timeUnit));
|
|
|
|
|
// return get(acquireAsync(permits, leaseTime, timeUnit));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public RFuture<String> acquireAsync() {
|
|
|
|
|
return acquireAsync(1, -1, TimeUnit.MILLISECONDS);
|
|
|
|
|
CompletionStage<String> future = acquireAsync(1)
|
|
|
|
|
.thenApply(RedissonPermitExpirableSemaphore::getFirstOrNull);
|
|
|
|
|
return new CompletableFutureWrapper<>(future);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public RFuture<List<String>> acquireAsync(int permits) {
|
|
|
|
|
return acquireAsync(permits, -1, TimeUnit.MILLISECONDS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private RFuture<String> acquireAsync(int permits, long ttl, TimeUnit timeUnit) {
|
|
|
|
|
long timeoutDate = calcTimeout(ttl, timeUnit);
|
|
|
|
|
RFuture<String> tryAcquireFuture = tryAcquireAsync(permits, timeoutDate);
|
|
|
|
|
CompletionStage<String> f = tryAcquireFuture.thenCompose(permitId -> {
|
|
|
|
|
if (permitId != null && !permitId.startsWith(":")) {
|
|
|
|
|
return CompletableFuture.completedFuture(permitId);
|
|
|
|
|
@Override
|
|
|
|
|
public RFuture<String> acquireAsync(long leaseTime, TimeUnit timeUnit) {
|
|
|
|
|
CompletionStage<String> future = acquireAsync(1, leaseTime, timeUnit)
|
|
|
|
|
.thenApply(RedissonPermitExpirableSemaphore::getFirstOrNull);
|
|
|
|
|
|
|
|
|
|
return new CompletableFutureWrapper<>(future);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public RFuture<List<String>> acquireAsync(int permits, long leaseTime, TimeUnit timeUnit) {
|
|
|
|
|
long timeoutDate = calcTimeout(leaseTime, timeUnit);
|
|
|
|
|
RFuture<List<String>> tryAcquireFuture = tryAcquireAsync(permits, timeoutDate);
|
|
|
|
|
CompletionStage<List<String>> f = tryAcquireFuture.thenCompose(ids -> {
|
|
|
|
|
if (!ids.isEmpty() && !hasOnlyNearestTimeout(ids)) {
|
|
|
|
|
return CompletableFuture.completedFuture(ids);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CompletableFuture<RedissonLockEntry> subscribeFuture = subscribe();
|
|
|
|
|
semaphorePubSub.timeout(subscribeFuture);
|
|
|
|
|
return subscribeFuture.thenCompose(res -> {
|
|
|
|
|
return acquireAsync(permits, res, ttl, timeUnit);
|
|
|
|
|
});
|
|
|
|
|
return subscribeFuture.thenCompose(res -> acquireAsync(permits, res, leaseTime, timeUnit));
|
|
|
|
|
});
|
|
|
|
|
f.whenComplete((r, e) -> {
|
|
|
|
|
if (f.toCompletableFuture().isCancelled()) {
|
|
|
|
|
tryAcquireFuture.whenComplete((permitId, ex) -> {
|
|
|
|
|
if (permitId != null && !permitId.startsWith(":")) {
|
|
|
|
|
releaseAsync(permitId);
|
|
|
|
|
tryAcquireFuture.whenComplete((ids, ex) -> {
|
|
|
|
|
if (!ids.isEmpty() && !hasOnlyNearestTimeout(ids)) {
|
|
|
|
|
releaseAsync(ids);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
@ -134,7 +148,7 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
return new CompletableFutureWrapper<>(f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void tryAcquireAsync(AtomicLong time, int permits, RedissonLockEntry entry, CompletableFuture<String> result, long ttl, TimeUnit timeUnit) {
|
|
|
|
|
private void tryAcquireAsync(AtomicLong time, int permits, RedissonLockEntry entry, CompletableFuture<List<String>> result, long leaseTime, TimeUnit timeUnit) {
|
|
|
|
|
if (result.isDone()) {
|
|
|
|
|
unsubscribe(entry);
|
|
|
|
|
return;
|
|
|
|
@ -146,10 +160,10 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long timeoutDate = calcTimeout(ttl, timeUnit);
|
|
|
|
|
long timeoutDate = calcTimeout(leaseTime, timeUnit);
|
|
|
|
|
long curr = System.currentTimeMillis();
|
|
|
|
|
RFuture<String> tryAcquireFuture = tryAcquireAsync(permits, timeoutDate);
|
|
|
|
|
tryAcquireFuture.whenComplete((permitId, e) -> {
|
|
|
|
|
RFuture<List<String>> tryAcquireFuture = tryAcquireAsync(permits, timeoutDate);
|
|
|
|
|
tryAcquireFuture.whenComplete((ids, e) -> {
|
|
|
|
|
if (e != null) {
|
|
|
|
|
unsubscribe(entry);
|
|
|
|
|
result.completeExceptionally(e);
|
|
|
|
@ -157,18 +171,16 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Long nearestTimeout;
|
|
|
|
|
if (permitId != null) {
|
|
|
|
|
if (!permitId.startsWith(":")) {
|
|
|
|
|
unsubscribe(entry);
|
|
|
|
|
if (!result.complete(permitId)) {
|
|
|
|
|
releaseAsync(permitId);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
nearestTimeout = Long.valueOf(permitId.substring(1)) - System.currentTimeMillis();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (ids.isEmpty()) {
|
|
|
|
|
nearestTimeout = null;
|
|
|
|
|
} else if (hasOnlyNearestTimeout(ids)) {
|
|
|
|
|
nearestTimeout = Long.parseLong(ids.get(0).substring(1)) - System.currentTimeMillis();
|
|
|
|
|
} else {
|
|
|
|
|
unsubscribe(entry);
|
|
|
|
|
if (!result.complete(ids)) {
|
|
|
|
|
releaseAsync(ids);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long el = System.currentTimeMillis() - curr;
|
|
|
|
@ -183,7 +195,7 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
// waiting for message
|
|
|
|
|
long current = System.currentTimeMillis();
|
|
|
|
|
if (entry.getLatch().tryAcquire()) {
|
|
|
|
|
tryAcquireAsync(time, permits, entry, result, ttl, timeUnit);
|
|
|
|
|
tryAcquireAsync(time, permits, entry, result, leaseTime, timeUnit);
|
|
|
|
|
} else {
|
|
|
|
|
AtomicReference<Timeout> waitTimeoutFutureRef = new AtomicReference<>();
|
|
|
|
|
|
|
|
|
@ -199,7 +211,7 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
long elapsed = System.currentTimeMillis() - current;
|
|
|
|
|
time.addAndGet(-elapsed);
|
|
|
|
|
|
|
|
|
|
tryAcquireAsync(time, permits, entry, result, ttl, timeUnit);
|
|
|
|
|
tryAcquireAsync(time, permits, entry, result, leaseTime, timeUnit);
|
|
|
|
|
}
|
|
|
|
|
}, nearestTimeout, TimeUnit.MILLISECONDS);
|
|
|
|
|
} else {
|
|
|
|
@ -219,7 +231,7 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
long elapsed = System.currentTimeMillis() - current;
|
|
|
|
|
time.addAndGet(-elapsed);
|
|
|
|
|
|
|
|
|
|
tryAcquireAsync(time, permits, entry, result, ttl, timeUnit);
|
|
|
|
|
tryAcquireAsync(time, permits, entry, result, leaseTime, timeUnit);
|
|
|
|
|
};
|
|
|
|
|
entry.addListener(listener);
|
|
|
|
|
|
|
|
|
@ -235,7 +247,7 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
long elapsed = System.currentTimeMillis() - current;
|
|
|
|
|
time.addAndGet(-elapsed);
|
|
|
|
|
|
|
|
|
|
tryAcquireAsync(time, permits, entry, result, ttl, timeUnit);
|
|
|
|
|
tryAcquireAsync(time, permits, entry, result, leaseTime, timeUnit);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}, t, TimeUnit.MILLISECONDS);
|
|
|
|
@ -245,35 +257,33 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private CompletableFuture<String> acquireAsync(int permits, RedissonLockEntry entry, long ttl, TimeUnit timeUnit) {
|
|
|
|
|
long timeoutDate = calcTimeout(ttl, timeUnit);
|
|
|
|
|
CompletableFuture<String> tryAcquireFuture = tryAcquireAsync(permits, timeoutDate).toCompletableFuture();
|
|
|
|
|
private CompletableFuture<List<String>> acquireAsync(int permits, RedissonLockEntry entry, long leaseTime, TimeUnit timeUnit) {
|
|
|
|
|
long timeoutDate = calcTimeout(leaseTime, timeUnit);
|
|
|
|
|
CompletableFuture<List<String>> tryAcquireFuture = tryAcquireAsync(permits, timeoutDate).toCompletableFuture();
|
|
|
|
|
return tryAcquireFuture.whenComplete((p, e) -> {
|
|
|
|
|
if (e != null) {
|
|
|
|
|
unsubscribe(entry);
|
|
|
|
|
}
|
|
|
|
|
}).thenCompose(permitId -> {
|
|
|
|
|
}).thenCompose(ids -> {
|
|
|
|
|
Long nearestTimeout;
|
|
|
|
|
if (permitId != null) {
|
|
|
|
|
if (!permitId.startsWith(":")) {
|
|
|
|
|
unsubscribe(entry);
|
|
|
|
|
return CompletableFuture.completedFuture(permitId);
|
|
|
|
|
} else {
|
|
|
|
|
nearestTimeout = Long.valueOf(permitId.substring(1)) - System.currentTimeMillis();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (ids.isEmpty()) {
|
|
|
|
|
nearestTimeout = null;
|
|
|
|
|
} else if (hasOnlyNearestTimeout(ids)) {
|
|
|
|
|
nearestTimeout = Long.parseLong(ids.get(0).substring(1)) - System.currentTimeMillis();
|
|
|
|
|
} else {
|
|
|
|
|
unsubscribe(entry);
|
|
|
|
|
return CompletableFuture.completedFuture(ids);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (entry.getLatch().tryAcquire(permits)) {
|
|
|
|
|
return acquireAsync(permits, entry, ttl, timeUnit);
|
|
|
|
|
return acquireAsync(permits, entry, leaseTime, timeUnit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CompletableFuture<String> res = new CompletableFuture<>();
|
|
|
|
|
CompletableFuture<List<String>> res = new CompletableFuture<>();
|
|
|
|
|
Timeout scheduledFuture;
|
|
|
|
|
if (nearestTimeout != null) {
|
|
|
|
|
scheduledFuture = getServiceManager().newTimeout(timeout -> {
|
|
|
|
|
CompletableFuture<String> r = acquireAsync(permits, entry, ttl, timeUnit);
|
|
|
|
|
CompletableFuture<List<String>> r = acquireAsync(permits, entry, leaseTime, timeUnit);
|
|
|
|
|
commandExecutor.transfer(r, res);
|
|
|
|
|
}, nearestTimeout, TimeUnit.MILLISECONDS);
|
|
|
|
|
} else {
|
|
|
|
@ -285,7 +295,7 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
entry.getLatch().release();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
CompletableFuture<String> r = acquireAsync(permits, entry, ttl, timeUnit);
|
|
|
|
|
CompletableFuture<List<String>> r = acquireAsync(permits, entry, leaseTime, timeUnit);
|
|
|
|
|
commandExecutor.transfer(r, res);
|
|
|
|
|
};
|
|
|
|
|
entry.addListener(listener);
|
|
|
|
@ -295,114 +305,158 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String tryAcquire() {
|
|
|
|
|
String res = tryAcquire(1, -1, TimeUnit.MILLISECONDS);
|
|
|
|
|
if (res != null && res.startsWith(":")) {
|
|
|
|
|
return null;
|
|
|
|
|
List<String> ids = tryAcquire(1);
|
|
|
|
|
return getFirstOrNull(ids);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public List<String> tryAcquire(int permits) {
|
|
|
|
|
List<String> ids = tryAcquire(permits, -1, TimeUnit.MILLISECONDS);
|
|
|
|
|
if (hasOnlyNearestTimeout(ids)) {
|
|
|
|
|
return Collections.emptyList();
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
return ids;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String tryAcquire(int permits, long ttl, TimeUnit timeUnit) {
|
|
|
|
|
long timeoutDate = calcTimeout(ttl, timeUnit);
|
|
|
|
|
private List<String> tryAcquire(int permits, long leaseTime, TimeUnit timeUnit) {
|
|
|
|
|
long timeoutDate = calcTimeout(leaseTime, timeUnit);
|
|
|
|
|
return get(tryAcquireAsync(permits, timeoutDate));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private long calcTimeout(long ttl, TimeUnit timeUnit) {
|
|
|
|
|
if (ttl != -1) {
|
|
|
|
|
return System.currentTimeMillis() + timeUnit.toMillis(ttl);
|
|
|
|
|
private long calcTimeout(long leaseTime, TimeUnit timeUnit) {
|
|
|
|
|
if (leaseTime != -1) {
|
|
|
|
|
return System.currentTimeMillis() + timeUnit.toMillis(leaseTime);
|
|
|
|
|
}
|
|
|
|
|
return nonExpirableTimeout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public RFuture<String> tryAcquireAsync() {
|
|
|
|
|
CompletableFuture<String> future = tryAcquireAsync(1, nonExpirableTimeout).toCompletableFuture();
|
|
|
|
|
CompletableFuture<String> f = future.thenApply(permitId -> {
|
|
|
|
|
if (permitId != null && !permitId.startsWith(":")) {
|
|
|
|
|
return permitId;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
});
|
|
|
|
|
CompletionStage<String> future = tryAcquireAsync(1)
|
|
|
|
|
.thenApply(RedissonPermitExpirableSemaphore::getFirstOrNull);
|
|
|
|
|
return new CompletableFutureWrapper<>(future);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
future.whenComplete((permitId, e) -> {
|
|
|
|
|
if (f.isCancelled() && permitId != null && !permitId.startsWith(":")) {
|
|
|
|
|
releaseAsync(permitId);
|
|
|
|
|
@Override
|
|
|
|
|
public RFuture<List<String>> tryAcquireAsync(int permits) {
|
|
|
|
|
CompletableFuture<List<String>> future = tryAcquireAsync(permits, nonExpirableTimeout).toCompletableFuture()
|
|
|
|
|
.thenApply(ids -> {
|
|
|
|
|
if (hasOnlyNearestTimeout(ids)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return ids;
|
|
|
|
|
});
|
|
|
|
|
future.whenComplete((ids, e) -> {
|
|
|
|
|
if (future.isCancelled() && !ids.isEmpty() && !hasOnlyNearestTimeout(ids)) {
|
|
|
|
|
releaseAsync(ids);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return new CompletableFutureWrapper<>(f);
|
|
|
|
|
return new CompletableFutureWrapper<>(future);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private RFuture<String> tryAcquireAsync(int permits, long timeoutDate) {
|
|
|
|
|
private RFuture<List<String>> tryAcquireAsync(int permits, long timeoutDate) {
|
|
|
|
|
if (permits < 0) {
|
|
|
|
|
throw new IllegalArgumentException("Permits amount can't be negative");
|
|
|
|
|
}
|
|
|
|
|
List<String> ids = new ArrayList<>(permits);
|
|
|
|
|
for (int i = 0; i < permits; i++) {
|
|
|
|
|
ids.add(getServiceManager().generateId());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte[] id = getServiceManager().generateIdArray();
|
|
|
|
|
return getServiceManager().execute(() -> {
|
|
|
|
|
RFuture<String> future = tryAcquireAsync(id, permits, timeoutDate);
|
|
|
|
|
return commandExecutor.handleNoSync(future, () -> releaseAsync(ByteBufUtil.hexDump(id)));
|
|
|
|
|
RFuture<List<String>> future = tryAcquireAsync(ids, timeoutDate);
|
|
|
|
|
return commandExecutor.handleNoSync(future, () -> releaseAsync(ids));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private RFuture<String> tryAcquireAsync(byte[] id, int permits, long timeoutDate) {
|
|
|
|
|
return commandExecutor.syncedEval(getRawName(), ByteArrayCodec.INSTANCE, RedisCommands.EVAL_PERMIT_DATA,
|
|
|
|
|
"local expiredIds = redis.call('zrangebyscore', KEYS[2], 0, ARGV[4], 'limit', 0, ARGV[1]); " +
|
|
|
|
|
private RFuture<List<String>> tryAcquireAsync(List<String> ids, long timeoutDate) {
|
|
|
|
|
List<Object> params = new ArrayList<>();
|
|
|
|
|
params.add(ids.size());
|
|
|
|
|
params.add(timeoutDate);
|
|
|
|
|
params.add(System.currentTimeMillis());
|
|
|
|
|
params.add(nonExpirableTimeout);
|
|
|
|
|
params.add(getSubscribeService().getPublishCommand());
|
|
|
|
|
|
|
|
|
|
for (String permitId: ids) {
|
|
|
|
|
params.add(ByteBufUtil.decodeHexDump(permitId));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CompletionStage<List<String>> future = commandExecutor.syncedEval(getRawName(), ByteArrayCodec.INSTANCE, RedisCommands.EVAL_STRING,
|
|
|
|
|
"local expiredIds = redis.call('zrangebyscore', KEYS[2], 0, ARGV[3], 'limit', 0, ARGV[1]); " +
|
|
|
|
|
"if #expiredIds > 0 then " +
|
|
|
|
|
"redis.call('zrem', KEYS[2], unpack(expiredIds)); " +
|
|
|
|
|
"local value = redis.call('incrby', KEYS[1], #expiredIds); " +
|
|
|
|
|
"if tonumber(value) > 0 then " +
|
|
|
|
|
"redis.call(ARGV[6], KEYS[3], value); " +
|
|
|
|
|
"redis.call(ARGV[5], KEYS[3], value); " +
|
|
|
|
|
"end;" +
|
|
|
|
|
"end; " +
|
|
|
|
|
"local value = redis.call('get', KEYS[1]); " +
|
|
|
|
|
"if (value ~= false and tonumber(value) >= tonumber(ARGV[1])) then " +
|
|
|
|
|
"redis.call('decrby', KEYS[1], ARGV[1]); " +
|
|
|
|
|
"redis.call('zadd', KEYS[2], ARGV[2], ARGV[3]); " +
|
|
|
|
|
"for i = 6, #ARGV, 1 do " +
|
|
|
|
|
"redis.call('zadd', KEYS[2], ARGV[2], ARGV[i]); " +
|
|
|
|
|
"end; " +
|
|
|
|
|
|
|
|
|
|
"local ttl = redis.call('pttl', KEYS[1]); " +
|
|
|
|
|
"if ttl > 0 then " +
|
|
|
|
|
"redis.call('pexpire', KEYS[2], ttl); " +
|
|
|
|
|
"end; " +
|
|
|
|
|
|
|
|
|
|
"return ARGV[3]; " +
|
|
|
|
|
"return 'OK'; " +
|
|
|
|
|
"end; " +
|
|
|
|
|
"local v = redis.call('zrange', KEYS[2], 0, 0, 'WITHSCORES'); " +
|
|
|
|
|
"if v[1] ~= nil and v[2] ~= ARGV[5] then " +
|
|
|
|
|
"if v[1] ~= nil and v[2] ~= ARGV[4] then " +
|
|
|
|
|
"return ':' .. tostring(v[2]); " +
|
|
|
|
|
"end " +
|
|
|
|
|
"return nil;",
|
|
|
|
|
Arrays.asList(getRawName(), timeoutName, channelName),
|
|
|
|
|
permits, timeoutDate, id, System.currentTimeMillis(), nonExpirableTimeout, getSubscribeService().getPublishCommand());
|
|
|
|
|
params.toArray()
|
|
|
|
|
).thenApply(result -> {
|
|
|
|
|
if (result == null) {
|
|
|
|
|
return Collections.emptyList();
|
|
|
|
|
}
|
|
|
|
|
if (result.equals("OK")) {
|
|
|
|
|
return ids;
|
|
|
|
|
}
|
|
|
|
|
return Collections.singletonList(result);
|
|
|
|
|
});
|
|
|
|
|
return new CompletableFutureWrapper<>(future);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public RFuture<String> tryAcquireAsync(long waitTime, TimeUnit unit) {
|
|
|
|
|
return tryAcquireAsync(1, waitTime, -1, unit);
|
|
|
|
|
CompletionStage<String> future = tryAcquireAsync(1, waitTime, -1, unit)
|
|
|
|
|
.thenApply(RedissonPermitExpirableSemaphore::getFirstOrNull);
|
|
|
|
|
return new CompletableFutureWrapper<>(future);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String tryAcquire(long waitTime, long ttl, TimeUnit unit) throws InterruptedException {
|
|
|
|
|
return tryAcquire(1, waitTime, ttl, unit);
|
|
|
|
|
public String tryAcquire(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
|
|
|
|
|
List<String> ids = tryAcquire(1, waitTime, leaseTime, unit);
|
|
|
|
|
return getFirstOrNull(ids);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public RFuture<String> tryAcquireAsync(long waitTime, long ttl, TimeUnit unit) {
|
|
|
|
|
return tryAcquireAsync(1, waitTime, ttl, unit);
|
|
|
|
|
public RFuture<String> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit) {
|
|
|
|
|
CompletionStage<String> future = tryAcquireAsync(1, waitTime, leaseTime, unit)
|
|
|
|
|
.thenApply(RedissonPermitExpirableSemaphore::getFirstOrNull);
|
|
|
|
|
return new CompletableFutureWrapper<>(future);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String tryAcquire(int permits, long waitTime, long ttl, TimeUnit unit) throws InterruptedException {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public List<String> tryAcquire(int permits, long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
|
|
|
|
|
long time = unit.toMillis(waitTime);
|
|
|
|
|
long current = System.currentTimeMillis();
|
|
|
|
|
|
|
|
|
|
String permitId = tryAcquire(permits, ttl, unit);
|
|
|
|
|
if (permitId != null && !permitId.startsWith(":")) {
|
|
|
|
|
return permitId;
|
|
|
|
|
List<String> ids = tryAcquire(permits, leaseTime, unit);
|
|
|
|
|
if (!ids.isEmpty() && !hasOnlyNearestTimeout(ids)) {
|
|
|
|
|
return ids;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
time -= System.currentTimeMillis() - current;
|
|
|
|
|
if (time <= 0) {
|
|
|
|
|
return null;
|
|
|
|
|
return Collections.emptyList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
current = System.currentTimeMillis();
|
|
|
|
@ -411,32 +465,30 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
try {
|
|
|
|
|
entry = future.get(time, TimeUnit.MILLISECONDS);
|
|
|
|
|
} catch (ExecutionException | TimeoutException e) {
|
|
|
|
|
return null;
|
|
|
|
|
return Collections.emptyList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
time -= System.currentTimeMillis() - current;
|
|
|
|
|
if (time <= 0) {
|
|
|
|
|
return null;
|
|
|
|
|
return Collections.emptyList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
current = System.currentTimeMillis();
|
|
|
|
|
Long nearestTimeout;
|
|
|
|
|
permitId = tryAcquire(permits, ttl, unit);
|
|
|
|
|
if (permitId != null) {
|
|
|
|
|
if (!permitId.startsWith(":")) {
|
|
|
|
|
return permitId;
|
|
|
|
|
} else {
|
|
|
|
|
nearestTimeout = Long.valueOf(permitId.substring(1)) - System.currentTimeMillis();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ids = tryAcquire(permits, leaseTime, unit);
|
|
|
|
|
if (ids.isEmpty()) {
|
|
|
|
|
nearestTimeout = null;
|
|
|
|
|
} else if (hasOnlyNearestTimeout(ids)) {
|
|
|
|
|
nearestTimeout = Long.parseLong(ids.get(0).substring(1)) - System.currentTimeMillis();
|
|
|
|
|
} else {
|
|
|
|
|
return ids;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
time -= System.currentTimeMillis() - current;
|
|
|
|
|
if (time <= 0) {
|
|
|
|
|
return null;
|
|
|
|
|
return Collections.emptyList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// waiting for message
|
|
|
|
@ -451,30 +503,30 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
long elapsed = System.currentTimeMillis() - current;
|
|
|
|
|
time -= elapsed;
|
|
|
|
|
if (time <= 0) {
|
|
|
|
|
return null;
|
|
|
|
|
return Collections.emptyList();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
unsubscribe(entry);
|
|
|
|
|
}
|
|
|
|
|
// return get(tryAcquireAsync(permits, waitTime, ttl, unit));
|
|
|
|
|
// return get(tryAcquireAsync(permits, waitTime, leaseTime, unit));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private RFuture<String> tryAcquireAsync(int permits, long waitTime, long ttl, TimeUnit timeUnit) {
|
|
|
|
|
CompletableFuture<String> result = new CompletableFuture<>();
|
|
|
|
|
@Override
|
|
|
|
|
public RFuture<List<String>> tryAcquireAsync(int permits, long waitTime, long leaseTime, TimeUnit timeUnit) {
|
|
|
|
|
CompletableFuture<List<String>> result = new CompletableFuture<>();
|
|
|
|
|
AtomicLong time = new AtomicLong(timeUnit.toMillis(waitTime));
|
|
|
|
|
long curr = System.currentTimeMillis();
|
|
|
|
|
long timeoutDate = calcTimeout(ttl, timeUnit);
|
|
|
|
|
RFuture<String> tryAcquireFuture = tryAcquireAsync(permits, timeoutDate);
|
|
|
|
|
tryAcquireFuture.whenComplete((permitId, e) -> {
|
|
|
|
|
long timeoutDate = calcTimeout(leaseTime, timeUnit);
|
|
|
|
|
tryAcquireAsync(permits, timeoutDate).whenComplete((ids, e) -> {
|
|
|
|
|
if (e != null) {
|
|
|
|
|
result.completeExceptionally(e);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (permitId != null && !permitId.startsWith(":")) {
|
|
|
|
|
if (!result.complete(permitId)) {
|
|
|
|
|
releaseAsync(permitId);
|
|
|
|
|
if (!ids.isEmpty() && !hasOnlyNearestTimeout(ids)) {
|
|
|
|
|
if (!result.complete(ids)) {
|
|
|
|
|
releaseAsync(ids);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
@ -503,7 +555,7 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
long elapsed = System.currentTimeMillis() - current;
|
|
|
|
|
time.addAndGet(-elapsed);
|
|
|
|
|
|
|
|
|
|
tryAcquireAsync(time, permits, r, result, ttl, timeUnit);
|
|
|
|
|
tryAcquireAsync(time, permits, r, result, leaseTime, timeUnit);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!subscribeFuture.isDone()) {
|
|
|
|
@ -532,11 +584,11 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String tryAcquire(long waitTime, TimeUnit unit) throws InterruptedException {
|
|
|
|
|
String res = tryAcquire(1, waitTime, -1, unit);
|
|
|
|
|
if (res != null && res.startsWith(":")) {
|
|
|
|
|
List<String> ids = tryAcquire(1, waitTime, -1, unit);
|
|
|
|
|
if (ids.isEmpty() || hasOnlyNearestTimeout(ids)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
return ids.get(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@ -544,11 +596,21 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
get(releaseAsync(permitId));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void release(List<String> permitsIds) {
|
|
|
|
|
get(releaseAsync(permitsIds));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean tryRelease(String permitId) {
|
|
|
|
|
return get(tryReleaseAsync(permitId));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int tryRelease(List<String> permitsIds) {
|
|
|
|
|
return get(tryReleaseAsync(permitsIds));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public RFuture<Boolean> tryReleaseAsync(String permitId) {
|
|
|
|
|
if (permitId == null) {
|
|
|
|
@ -572,6 +634,45 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
id, 1, System.currentTimeMillis(), getSubscribeService().getPublishCommand());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public RFuture<Integer> tryReleaseAsync(List<String> permitsIds) {
|
|
|
|
|
if (permitsIds == null || permitsIds.isEmpty()) {
|
|
|
|
|
throw new IllegalArgumentException("permitIds can't be null or empty");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<Object> params = new ArrayList<>(permitsIds.size() + 3);
|
|
|
|
|
params.add(permitsIds.size());
|
|
|
|
|
params.add(System.currentTimeMillis());
|
|
|
|
|
params.add(getSubscribeService().getPublishCommand());
|
|
|
|
|
|
|
|
|
|
for (String permitId : permitsIds) {
|
|
|
|
|
params.add(ByteBufUtil.decodeHexDump(permitId));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return commandExecutor.syncedEvalWithRetry(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_INTEGER,
|
|
|
|
|
"local expiredIds = redis.call('zrangebyscore', KEYS[3], 0, ARGV[2], 'limit', 0, -1); " +
|
|
|
|
|
"if #expiredIds > 0 then " +
|
|
|
|
|
"redis.call('zrem', KEYS[3], unpack(expiredIds)); " +
|
|
|
|
|
"local value = redis.call('incrby', KEYS[1], #expiredIds); " +
|
|
|
|
|
"if tonumber(value) > 0 then " +
|
|
|
|
|
"redis.call(ARGV[3], KEYS[2], value); " +
|
|
|
|
|
"end; " +
|
|
|
|
|
"end; " +
|
|
|
|
|
"local keys = {}; " +
|
|
|
|
|
"for i = 4, #ARGV, 1 do " +
|
|
|
|
|
"table.insert(keys, ARGV[i]); " +
|
|
|
|
|
"end; " +
|
|
|
|
|
"local removed = redis.call('zrem', KEYS[3], unpack(keys)); " +
|
|
|
|
|
"if tonumber(removed) == 0 then " +
|
|
|
|
|
"return 0;" +
|
|
|
|
|
"end; " +
|
|
|
|
|
"redis.call('incrby', KEYS[1], removed); " +
|
|
|
|
|
"redis.call(ARGV[3], KEYS[2], removed); " +
|
|
|
|
|
"return removed;",
|
|
|
|
|
Arrays.asList(getRawName(), channelName, timeoutName),
|
|
|
|
|
params.toArray());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public RFuture<Long> sizeInMemoryAsync() {
|
|
|
|
|
List<Object> keys = Arrays.<Object>asList(getRawName(), timeoutName);
|
|
|
|
@ -613,6 +714,21 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
return new CompletableFutureWrapper<>(f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public RFuture<Void> releaseAsync(List<String> permitsIds) {
|
|
|
|
|
CompletionStage<Void> f = tryReleaseAsync(permitsIds).handle((res, e) -> {
|
|
|
|
|
if (e != null) {
|
|
|
|
|
throw new CompletionException(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (res == permitsIds.size()) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
throw new CompletionException(new IllegalArgumentException("Permits with ids " + permitsIds + " have already been released or don't exist"));
|
|
|
|
|
});
|
|
|
|
|
return new CompletableFutureWrapper<>(f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int availablePermits() {
|
|
|
|
|
return get(availablePermitsAsync());
|
|
|
|
@ -779,4 +895,15 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
|
|
|
|
|
return get(updateLeaseTimeAsync(permitId, leaseTime, unit));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static boolean hasOnlyNearestTimeout(List<String> ids) {
|
|
|
|
|
return ids.size() == 1 && ids.get(0).startsWith(":");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static String getFirstOrNull(List<String> ids) {
|
|
|
|
|
if (ids.isEmpty()) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return ids.get(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|