Feature - RTransferQueue object added. #322
parent
b4b1e3a052
commit
8b382013f2
@ -0,0 +1,756 @@
|
||||
/**
|
||||
* Copyright (c) 2013-2019 Nikita Koksharov
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.redisson;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.util.Timeout;
|
||||
import io.netty.util.concurrent.ImmediateEventExecutor;
|
||||
import org.redisson.api.RFuture;
|
||||
import org.redisson.api.RRemoteService;
|
||||
import org.redisson.api.RTransferQueue;
|
||||
import org.redisson.api.RemoteInvocationOptions;
|
||||
import org.redisson.api.annotation.RRemoteAsync;
|
||||
import org.redisson.client.codec.Codec;
|
||||
import org.redisson.client.protocol.RedisCommand;
|
||||
import org.redisson.client.protocol.RedisStrictCommand;
|
||||
import org.redisson.client.protocol.convertor.Convertor;
|
||||
import org.redisson.client.protocol.decoder.ObjectListReplayDecoder;
|
||||
import org.redisson.command.CommandAsyncExecutor;
|
||||
import org.redisson.connection.decoder.ListDrainToDecoder;
|
||||
import org.redisson.executor.RemotePromise;
|
||||
import org.redisson.iterator.RedissonListIterator;
|
||||
import org.redisson.misc.RPromise;
|
||||
import org.redisson.misc.RedissonPromise;
|
||||
import org.redisson.remote.RemoteServiceRequest;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
*/
|
||||
public class RedissonTransferQueue<V> extends RedissonExpirable implements RTransferQueue<V> {
|
||||
|
||||
public interface TransferQueueService {
|
||||
|
||||
<V> void invoke(V value);
|
||||
|
||||
}
|
||||
|
||||
@RRemoteAsync(TransferQueueService.class)
|
||||
public interface TransferQueueServiceAsync {
|
||||
|
||||
<V> RFuture<Void> invoke(V value);
|
||||
|
||||
}
|
||||
|
||||
public static class TransferQueueServiceImpl implements TransferQueueService {
|
||||
|
||||
private Object result;
|
||||
|
||||
@Override
|
||||
public <V> void invoke(V value) {
|
||||
result = value;
|
||||
}
|
||||
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final Convertor<Object> CONVERTER = obj -> {
|
||||
if (obj != null) {
|
||||
RemoteServiceRequest request = (RemoteServiceRequest) obj;
|
||||
return request.getArgs()[0];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
private static final RedisStrictCommand<Object> EVAL_REQUEST = new RedisStrictCommand<>("EVAL", CONVERTER);
|
||||
private static final RedisCommand EVAL_LIST = new RedisCommand("EVAL", new ObjectListReplayDecoder<>(), CONVERTER);
|
||||
|
||||
private final String queueName;
|
||||
private final String mapName;
|
||||
private final TransferQueueServiceAsync service;
|
||||
private final RRemoteService remoteService;
|
||||
|
||||
public RedissonTransferQueue(Codec codec, CommandAsyncExecutor commandExecutor, String name, RRemoteService remoteService) {
|
||||
super(codec, commandExecutor, name);
|
||||
service = remoteService.get(TransferQueueServiceAsync.class, RemoteInvocationOptions.defaults().noAck());
|
||||
this.remoteService = remoteService;
|
||||
|
||||
queueName = ((RedissonRemoteService) remoteService).getRequestQueueName(TransferQueueService.class);
|
||||
mapName = ((RedissonRemoteService) remoteService).getRequestTasksMapName(TransferQueueService.class);
|
||||
}
|
||||
|
||||
public RedissonTransferQueue(CommandAsyncExecutor commandExecutor, String name, RRemoteService remoteService) {
|
||||
super(commandExecutor, name);
|
||||
service = remoteService.get(TransferQueueServiceAsync.class, RemoteInvocationOptions.defaults().noAck());
|
||||
this.remoteService = remoteService;
|
||||
|
||||
queueName = ((RedissonRemoteService) remoteService).getRequestQueueName(TransferQueueService.class);
|
||||
mapName = ((RedissonRemoteService) remoteService).getRequestTasksMapName(TransferQueueService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryTransfer(V v) {
|
||||
RemotePromise<Void> future = (RemotePromise<Void>) service.invoke(v);
|
||||
boolean added = get(future.getAddFuture());
|
||||
if (added && !future.cancel(false)) {
|
||||
get(future);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public RFuture<Boolean> tryTransferAsync(V v) {
|
||||
RPromise<Boolean> result = new RedissonPromise<>();
|
||||
result.setUncancellable();
|
||||
|
||||
RemotePromise<Void> future = (RemotePromise<Void>) service.invoke(v);
|
||||
future.getAddFuture().onComplete((added, e) -> {
|
||||
if (e != null) {
|
||||
result.tryFailure(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!added) {
|
||||
result.trySuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
future.cancelAsync(false).onComplete((canceled, ex) -> {
|
||||
if (ex != null) {
|
||||
result.tryFailure(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (canceled) {
|
||||
result.trySuccess(false);
|
||||
} else {
|
||||
future.onComplete((res, exc) -> {
|
||||
if (exc != null) {
|
||||
result.tryFailure(exc);
|
||||
return;
|
||||
}
|
||||
|
||||
result.trySuccess(true);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transfer(V v) throws InterruptedException {
|
||||
RFuture<Void> future = service.invoke(v);
|
||||
commandExecutor.getInterrupted(future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RFuture<Void> transferAsync(V v) {
|
||||
return service.invoke(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryTransfer(V v, long timeout, TimeUnit unit) throws InterruptedException {
|
||||
RemotePromise<Void> future = (RemotePromise<Void>) service.invoke(v);
|
||||
long remainTime = unit.toMillis(timeout);
|
||||
long startTime = System.currentTimeMillis();
|
||||
if (!future.getAddFuture().await(remainTime, TimeUnit.MILLISECONDS)) {
|
||||
if (!future.getAddFuture().cancel(false)) {
|
||||
if (!future.cancel(false)) {
|
||||
commandExecutor.getInterrupted(future);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
remainTime -= System.currentTimeMillis() - startTime;
|
||||
|
||||
if (!future.await(remainTime)) {
|
||||
if (!future.cancel(false)) {
|
||||
commandExecutor.getInterrupted(future);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
commandExecutor.getInterrupted(future);
|
||||
return true;
|
||||
}
|
||||
|
||||
public RFuture<Boolean> tryTransferAsync(V v, long timeout, TimeUnit unit) {
|
||||
RPromise<Boolean> result = new RedissonPromise<>();
|
||||
result.setUncancellable();
|
||||
|
||||
RemotePromise<Void> future = (RemotePromise<Void>) service.invoke(v);
|
||||
|
||||
long remainTime = unit.toMillis(timeout);
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
Timeout timeoutFuture = commandExecutor.getConnectionManager().newTimeout(tt -> {
|
||||
if (!future.getAddFuture().cancel(false)) {
|
||||
future.cancelAsync(false);
|
||||
}
|
||||
}, remainTime, TimeUnit.MILLISECONDS);
|
||||
|
||||
|
||||
future.onComplete((res, exc) -> {
|
||||
if (future.isCancelled()) {
|
||||
result.trySuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
timeoutFuture.cancel();
|
||||
if (exc != null) {
|
||||
result.tryFailure(exc);
|
||||
return;
|
||||
}
|
||||
|
||||
result.trySuccess(true);
|
||||
});
|
||||
|
||||
future.getAddFuture().onComplete((added, e) -> {
|
||||
if (future.getAddFuture().isCancelled()) {
|
||||
result.trySuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e != null) {
|
||||
timeoutFuture.cancel();
|
||||
result.tryFailure(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!added) {
|
||||
timeoutFuture.cancel();
|
||||
result.trySuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Runnable task = () -> {
|
||||
future.cancelAsync(false).onComplete((canceled, ex) -> {
|
||||
if (ex != null) {
|
||||
timeoutFuture.cancel();
|
||||
result.tryFailure(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (canceled) {
|
||||
timeoutFuture.cancel();
|
||||
result.trySuccess(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
long time = remainTime - (System.currentTimeMillis() - startTime);
|
||||
if (time > 0) {
|
||||
commandExecutor.getConnectionManager().newTimeout(tt -> {
|
||||
task.run();
|
||||
}, time, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
task.run();
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasWaitingConsumer() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWaitingConsumerCount() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(V v) {
|
||||
RemotePromise<Void> future = (RemotePromise<Void>) service.invoke(v);
|
||||
return get(future.getAddFuture());
|
||||
}
|
||||
|
||||
public RFuture<Boolean> addAsync(V v) {
|
||||
RemotePromise<Void> future = (RemotePromise<Void>) service.invoke(v);
|
||||
return future.getAddFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(V v) {
|
||||
return add(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove() {
|
||||
V value = poll();
|
||||
if (value == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V poll() {
|
||||
TransferQueueServiceImpl s = new TransferQueueServiceImpl();
|
||||
RFuture<Boolean> r = remoteService.tryExecuteAsync(TransferQueueService.class, s, ImmediateEventExecutor.INSTANCE, -1, null);
|
||||
get(r);
|
||||
return (V) s.getResult();
|
||||
}
|
||||
|
||||
public RFuture<V> pollAsync() {
|
||||
TransferQueueServiceImpl s = new TransferQueueServiceImpl();
|
||||
RFuture<Boolean> future = remoteService.tryExecuteAsync(TransferQueueService.class, s, ImmediateEventExecutor.INSTANCE, -1, null);
|
||||
|
||||
RPromise<V> result = new RedissonPromise<>();
|
||||
result.setUncancellable();
|
||||
|
||||
future.onComplete((r, e) -> {
|
||||
if (e != null) {
|
||||
result.tryFailure(e);
|
||||
return;
|
||||
}
|
||||
|
||||
result.trySuccess((V) s.getResult());
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public V element() {
|
||||
V value = peek();
|
||||
if (value == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V peek() {
|
||||
return get(peekAsync());
|
||||
}
|
||||
|
||||
public RFuture<V> peekAsync() {
|
||||
return commandExecutor.evalReadAsync(queueName, codec, EVAL_REQUEST,
|
||||
"local id = redis.call('lindex', KEYS[1], 0); "
|
||||
+ "if id ~= false then "
|
||||
+ "return redis.call('hget', KEYS[2], id); "
|
||||
+ "end "
|
||||
+ "return nil;",
|
||||
Arrays.asList(queueName, mapName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(V v) throws InterruptedException {
|
||||
add(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(V v, long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return add(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V take() throws InterruptedException {
|
||||
return poll(0, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public RFuture<V> takeAsync() {
|
||||
return pollAsync(0, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V poll(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
TransferQueueServiceImpl s = new TransferQueueServiceImpl();
|
||||
remoteService.tryExecute(TransferQueueService.class, s, ImmediateEventExecutor.INSTANCE, timeout, unit);
|
||||
return (V) s.getResult();
|
||||
}
|
||||
|
||||
public RFuture<V> pollAsync(long timeout, TimeUnit unit) {
|
||||
RPromise<V> result = new RedissonPromise<>();
|
||||
result.setUncancellable();
|
||||
|
||||
TransferQueueServiceImpl s = new TransferQueueServiceImpl();
|
||||
RFuture<Boolean> future = remoteService.tryExecuteAsync(TransferQueueService.class, s, ImmediateEventExecutor.INSTANCE, timeout, unit);
|
||||
future.onComplete((r, e) -> {
|
||||
if (e != null) {
|
||||
result.tryFailure(e);
|
||||
return;
|
||||
}
|
||||
|
||||
result.trySuccess((V) s.getResult());
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int remainingCapacity() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
if (c.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean all = true;
|
||||
for (Object obj : c) {
|
||||
all &= contains(obj);
|
||||
}
|
||||
return all;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends V> c) {
|
||||
if (c.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean added = false;
|
||||
for (V obj : c) {
|
||||
added |= add(obj);
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
RedissonKeys keys = new RedissonKeys(commandExecutor);
|
||||
keys.delete(queueName, mapName);
|
||||
}
|
||||
|
||||
public RFuture<Void> clearAsync() {
|
||||
RPromise<Void> result = new RedissonPromise<>();
|
||||
result.setUncancellable();
|
||||
|
||||
RedissonKeys keys = new RedissonKeys(commandExecutor);
|
||||
keys.deleteAsync(queueName, mapName).onComplete((r, e) -> {
|
||||
if (e != null) {
|
||||
result.tryFailure(e);
|
||||
return;
|
||||
}
|
||||
result.trySuccess(null);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return remoteService.getPendingInvocations(TransferQueueService.class);
|
||||
}
|
||||
|
||||
public RFuture<Integer> sizeAsync() {
|
||||
return remoteService.getPendingInvocationsAsync(TransferQueueService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
ByteBuf encodedObject = encode(o);
|
||||
boolean result = stream().anyMatch(v -> {
|
||||
ByteBuf encodedValue = encode(v);
|
||||
boolean res = encodedValue.equals(encodedObject);
|
||||
encodedValue.release();
|
||||
return res;
|
||||
});
|
||||
encodedObject.release();
|
||||
return result;
|
||||
}
|
||||
|
||||
public RFuture<V> getValueAsync(int index) {
|
||||
return commandExecutor.evalReadAsync(queueName, codec, EVAL_REQUEST,
|
||||
"local id = redis.call('lindex', KEYS[1], ARGV[1]); "
|
||||
+ "if id ~= false then "
|
||||
+ "return redis.call('hget', KEYS[2], id); "
|
||||
+ "end "
|
||||
+ "return nil;",
|
||||
Arrays.asList(queueName, mapName), index);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
return new RedissonListIterator<V>(0) {
|
||||
|
||||
@Override
|
||||
public V getValue(int index) {
|
||||
RFuture<V> future = getValueAsync(index);
|
||||
return get(future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(int index) {
|
||||
if (index == 0) {
|
||||
RFuture<V> future = commandExecutor.evalWriteAsync(queueName, codec, EVAL_REQUEST,
|
||||
"local id = redis.call('lpop', KEYS[1]); "
|
||||
+ "if id ~= false then "
|
||||
+ "return redis.call('hget', KEYS[2], id); "
|
||||
+ "end "
|
||||
+ "return nil;",
|
||||
Arrays.asList(queueName, mapName));
|
||||
|
||||
return get(future);
|
||||
}
|
||||
|
||||
RFuture<V> future = commandExecutor.evalWriteAsync(queueName, codec, EVAL_REQUEST,
|
||||
"local id = redis.call('lindex', KEYS[1], ARGV[1]); " +
|
||||
"if id ~= false then " +
|
||||
"redis.call('lset', KEYS[1], ARGV[1], 'DELETED_BY_REDISSON');" +
|
||||
"redis.call('lrem', KEYS[1], 1, 'DELETED_BY_REDISSON');" +
|
||||
"local val = redis.call('hget', KEYS[2], id); " +
|
||||
"redis.call('hdel', KEYS[2], id); " +
|
||||
"return val; " +
|
||||
"end; " +
|
||||
"return nil;",
|
||||
Arrays.asList(queueName, mapName), index);
|
||||
|
||||
return get(future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fastSet(int index, V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
List<V> list = readAll();
|
||||
return list.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(T[] a) {
|
||||
List<V> list = readAll();
|
||||
return list.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int drainTo(Collection<? super V> c) {
|
||||
return get(drainToAsync(c));
|
||||
}
|
||||
|
||||
public RFuture<Integer> drainToAsync(Collection<? super V> c) {
|
||||
if (c == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
return commandExecutor.evalWriteAsync(getName(), codec, new RedisCommand<Object>("EVAL", new ListDrainToDecoder(c), CONVERTER),
|
||||
"local ids = redis.call('lrange', KEYS[1], 0, -1); " +
|
||||
"local result = {};"
|
||||
+ "for i=1, #ids, 5000 do "
|
||||
+ "local vals = redis.call('hmget', KEYS[2], unpack(ids, i, math.min(i+4999, #ids))); "
|
||||
+ "for k,v in ipairs(vals) do "
|
||||
+ "table.insert(result, v); "
|
||||
+ "end; "
|
||||
+ "end; " +
|
||||
"redis.call('del', KEYS[1], KEYS[2]); " +
|
||||
"return result",
|
||||
Arrays.asList(queueName, mapName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int drainTo(Collection<? super V> c, int maxElements) {
|
||||
if (maxElements <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return get(drainToAsync(c, maxElements));
|
||||
}
|
||||
|
||||
public RFuture<Integer> drainToAsync(Collection<? super V> c, int maxElements) {
|
||||
if (c == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
return commandExecutor.evalWriteAsync(getName(), codec, new RedisCommand<Object>("EVAL", new ListDrainToDecoder(c), CONVERTER),
|
||||
"local elemNum = math.min(ARGV[1], redis.call('llen', KEYS[1])) - 1;" +
|
||||
"local ids = redis.call('lrange', KEYS[1], 0, elemNum); " +
|
||||
"redis.call('ltrim', KEYS[1], elemNum + 1, -1); " +
|
||||
"local result = {};"
|
||||
+ "for i=1, #ids, 5000 do "
|
||||
+ "local vals = redis.call('hmget', KEYS[2], unpack(ids, i, math.min(i+4999, #ids))); "
|
||||
+ "redis.call('hdel', KEYS[2], unpack(ids, i, math.min(i+4999, #ids)));"
|
||||
+ "for k,v in ipairs(vals) do "
|
||||
+ "table.insert(result, v); "
|
||||
+ "end; "
|
||||
+ "end; " +
|
||||
"return result",
|
||||
Arrays.asList(queueName, mapName), maxElements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<V> readAll() {
|
||||
return get(readAllAsync());
|
||||
}
|
||||
|
||||
public RFuture<List<V>> readAllAsync() {
|
||||
return commandExecutor.evalReadAsync(getName(), codec, EVAL_LIST,
|
||||
"local ids = redis.call('lrange', KEYS[1], 0, -1); " +
|
||||
"local result = {};"
|
||||
+ "for i=1, #ids, 5000 do "
|
||||
+ "local vals = redis.call('hmget', KEYS[2], unpack(ids, i, math.min(i+4999, #ids))); "
|
||||
+ "for k,v in ipairs(vals) do "
|
||||
+ "table.insert(result, v); "
|
||||
+ "end; "
|
||||
+ "end; " +
|
||||
"return result;",
|
||||
Arrays.asList(queueName, mapName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public V pollFromAny(long timeout, TimeUnit unit, String... queueNames) throws InterruptedException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V pollLastAndOfferFirstTo(String queueName, long timeout, TimeUnit unit) throws InterruptedException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V takeLastAndOfferFirstTo(String queueName) throws InterruptedException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int subscribeOnElements(Consumer<V> consumer) {
|
||||
return commandExecutor.getConnectionManager().getElementsSubscribeService().subscribeOnElements(this::takeAsync, consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe(int listenerId) {
|
||||
commandExecutor.getConnectionManager().getElementsSubscribeService().unsubscribe(listenerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V pollLastAndOfferFirstTo(String queueName) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<V> poll(int limit) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RFuture<V> pollFromAnyAsync(long timeout, TimeUnit unit, String... queueNames) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RFuture<V> pollLastAndOfferFirstToAsync(String queueName, long timeout, TimeUnit unit) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RFuture<V> takeLastAndOfferFirstToAsync(String queueName) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RFuture<Void> putAsync(V value) {
|
||||
RemotePromise<Void> future = (RemotePromise<Void>) service.invoke(value);
|
||||
RPromise<Void> result = new RedissonPromise<>();
|
||||
future.getAddFuture().onComplete((r, e) -> {
|
||||
if (e != null) {
|
||||
result.tryFailure(e);
|
||||
return;
|
||||
}
|
||||
|
||||
result.trySuccess(null);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RFuture<Boolean> offerAsync(V e) {
|
||||
return addAsync(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RFuture<V> pollLastAndOfferFirstToAsync(String queueName) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RFuture<List<V>> pollAsync(int limit) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RFuture<Boolean> retainAllAsync(Collection<?> c) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RFuture<Boolean> removeAllAsync(Collection<?> c) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RFuture<Boolean> containsAsync(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RFuture<Boolean> containsAllAsync(Collection<?> c) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RFuture<Boolean> removeAsync(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RFuture<Boolean> addAllAsync(Collection<? extends V> c) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2013-2019 Nikita Koksharov
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.redisson.api;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TransferQueue;
|
||||
|
||||
/**
|
||||
* Redis based implementation of {@link java.util.concurrent.TransferQueue}
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
*/
|
||||
public interface RTransferQueue<V> extends TransferQueue<V>, RBlockingQueue<V>, RTransferQueueAsync<V> {
|
||||
|
||||
/**
|
||||
* Returns all queue elements at once
|
||||
*
|
||||
* @return elements
|
||||
*/
|
||||
List<V> readAll();
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) 2013-2019 Nikita Koksharov
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.redisson.api;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Async interface for Redis based implementation of {@link java.util.concurrent.TransferQueue}
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
*/
|
||||
public interface RTransferQueueAsync<V> extends RBlockingQueueAsync<V> {
|
||||
|
||||
/**
|
||||
* Tries to transfer the element to waiting consumer
|
||||
* which invoked {@link #takeAsync} or {@link #pollAsync} method
|
||||
* at the moment of transfer.
|
||||
*
|
||||
* @param e element to transfer
|
||||
* @return {@code true} if element was transferred, otherwise
|
||||
* {@code false}
|
||||
*/
|
||||
RFuture<Boolean> tryTransferAsync(V e);
|
||||
|
||||
/**
|
||||
* Transfers the element to waiting consumer
|
||||
* which invoked {@link #takeAsync} or {@link #pollAsync} method
|
||||
* at the moment of transfer.
|
||||
* Waits if necessary for a consumer.
|
||||
*
|
||||
* @param e the element to transfer
|
||||
* @throws ClassCastException if the class of the specified element
|
||||
* prevents it from being added to this queue
|
||||
* @throws NullPointerException if the specified element is null
|
||||
* @throws IllegalArgumentException if some property of the specified
|
||||
* element prevents it from being added to this queue
|
||||
*/
|
||||
RFuture<Void> transferAsync(V e);
|
||||
|
||||
/**
|
||||
* Transfers the element to waiting consumer
|
||||
* which invoked {@link #takeAsync} or {@link #pollAsync} method
|
||||
* at the moment of transfer.
|
||||
* Waits up to defined <code>timeout</code> if necessary for a consumer.
|
||||
*
|
||||
* @param e the element to transfer
|
||||
* @param timeout the maximum time to wait
|
||||
* @param unit the time unit
|
||||
* @return <code>true</code> if the element was transferred and <code>false</code>
|
||||
* otherwise
|
||||
*/
|
||||
RFuture<Boolean> tryTransferAsync(V e, long timeout, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Returns all queue elements at once
|
||||
*
|
||||
* @return elements
|
||||
*/
|
||||
List<V> readAll();
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (c) 2013-2019 Nikita Koksharov
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.redisson.api;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Reactive interface of Redis based implementation of {@link java.util.concurrent.TransferQueue}
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
* @param <V> the type of elements held in this collection
|
||||
*/
|
||||
public interface RTransferQueueReactive<V> extends RBlockingQueueReactive<V> {
|
||||
|
||||
/**
|
||||
* Tries to transfer the element to waiting consumer
|
||||
* which invoked {@link #take} or {@link #poll} method
|
||||
* at the moment of transfer.
|
||||
*
|
||||
* @param e element to transfer
|
||||
* @return {@code true} if element was transferred, otherwise
|
||||
* {@code false}
|
||||
*/
|
||||
Mono<Boolean> tryTransfer(V e);
|
||||
|
||||
/**
|
||||
* Transfers the element to waiting consumer
|
||||
* which invoked {@link #take} or {@link #poll} method
|
||||
* at the moment of transfer.
|
||||
* Waits if necessary for a consumer.
|
||||
*
|
||||
* @param e the element to transfer
|
||||
* @throws ClassCastException if the class of the specified element
|
||||
* prevents it from being added to this queue
|
||||
* @throws NullPointerException if the specified element is null
|
||||
* @throws IllegalArgumentException if some property of the specified
|
||||
* element prevents it from being added to this queue
|
||||
*/
|
||||
Mono<Void> transfer(V e);
|
||||
|
||||
/**
|
||||
* Transfers the element to waiting consumer
|
||||
* which invoked {@link #take} or {@link #poll} method
|
||||
* at the moment of transfer.
|
||||
* Waits up to defined <code>timeout</code> if necessary for a consumer.
|
||||
*
|
||||
* @param e the element to transfer
|
||||
* @param timeout the maximum time to wait
|
||||
* @param unit the time unit
|
||||
* @return <code>true</code> if the element was transferred and <code>false</code>
|
||||
* otherwise
|
||||
*/
|
||||
Mono<Boolean> tryTransfer(V e, long timeout, TimeUnit unit);
|
||||
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright (c) 2013-2019 Nikita Koksharov
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.redisson.api;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* RxJava2 interface of Redis based implementation of {@link java.util.concurrent.TransferQueue}
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
* @param <V> the type of elements held in this collection
|
||||
*/
|
||||
public interface RTransferQueueRx<V> extends RBlockingQueueRx<V> {
|
||||
|
||||
/**
|
||||
* Tries to transfer the element to waiting consumer
|
||||
* which invoked {@link #take} or {@link #poll} method
|
||||
* at the moment of transfer.
|
||||
*
|
||||
* @param e element to transfer
|
||||
* @return {@code true} if element was transferred, otherwise
|
||||
* {@code false}
|
||||
*/
|
||||
Single<Boolean> tryTransfer(V e);
|
||||
|
||||
/**
|
||||
* Transfers the element to waiting consumer
|
||||
* which invoked {@link #take} or {@link #poll} method
|
||||
* at the moment of transfer.
|
||||
* Waits if necessary for a consumer.
|
||||
*
|
||||
* @param e the element to transfer
|
||||
* @throws ClassCastException if the class of the specified element
|
||||
* prevents it from being added to this queue
|
||||
* @throws NullPointerException if the specified element is null
|
||||
* @throws IllegalArgumentException if some property of the specified
|
||||
* element prevents it from being added to this queue
|
||||
*/
|
||||
Completable transfer(V e);
|
||||
|
||||
/**
|
||||
* Transfers the element to waiting consumer
|
||||
* which invoked {@link #take} or {@link #poll} method
|
||||
* at the moment of transfer.
|
||||
* Waits up to defined <code>timeout</code> if necessary for a consumer.
|
||||
*
|
||||
* @param e the element to transfer
|
||||
* @param timeout the maximum time to wait
|
||||
* @param unit the time unit
|
||||
* @return <code>true</code> if the element was transferred and <code>false</code>
|
||||
* otherwise
|
||||
*/
|
||||
Single<Boolean> tryTransfer(V e, long timeout, TimeUnit unit);
|
||||
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Copyright (c) 2013-2019 Nikita Koksharov
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.redisson.reactive;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.redisson.RedissonTransferQueue;
|
||||
import org.redisson.api.RFuture;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.FluxSink;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
* @param <V> - value type
|
||||
*/
|
||||
public class RedissonTransferQueueReactive<V> {
|
||||
|
||||
private final RedissonTransferQueue<V> queue;
|
||||
|
||||
public RedissonTransferQueueReactive(RedissonTransferQueue<V> queue) {
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
public Flux<V> takeElements() {
|
||||
return ElementsStream.takeElements(queue::takeAsync);
|
||||
}
|
||||
|
||||
public Publisher<V> iterator() {
|
||||
return Flux.create(new Consumer<FluxSink<V>>() {
|
||||
|
||||
@Override
|
||||
public void accept(FluxSink<V> emitter) {
|
||||
emitter.onRequest(new LongConsumer() {
|
||||
|
||||
int currentIndex = 0;
|
||||
|
||||
@Override
|
||||
public void accept(long value) {
|
||||
onRequest(true, emitter, value);
|
||||
}
|
||||
|
||||
protected void onRequest(boolean forward, FluxSink<V> emitter, long n) {
|
||||
queue.getValueAsync(currentIndex).onComplete((value, e) -> {
|
||||
if (e != null) {
|
||||
emitter.error(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
emitter.next(value);
|
||||
if (forward) {
|
||||
currentIndex++;
|
||||
} else {
|
||||
currentIndex--;
|
||||
}
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
emitter.complete();
|
||||
return;
|
||||
}
|
||||
if (n-1 == 0) {
|
||||
return;
|
||||
}
|
||||
onRequest(forward, emitter, n-1);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public Publisher<Boolean> addAll(Publisher<? extends V> c) {
|
||||
return new PublisherAdder<V>() {
|
||||
|
||||
@Override
|
||||
public RFuture<Boolean> add(Object o) {
|
||||
return queue.addAsync((V) o);
|
||||
}
|
||||
|
||||
}.addAll(c);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Copyright (c) 2013-2019 Nikita Koksharov
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.redisson.rx;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.functions.LongConsumer;
|
||||
import io.reactivex.processors.ReplayProcessor;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.redisson.RedissonTransferQueue;
|
||||
import org.redisson.api.RFuture;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
* @param <V> - value type
|
||||
*/
|
||||
public class RedissonTransferQueueRx<V> {
|
||||
|
||||
private final RedissonTransferQueue<V> queue;
|
||||
|
||||
public RedissonTransferQueueRx(RedissonTransferQueue<V> queue) {
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
public Flowable<V> takeElements() {
|
||||
return ElementsStream.takeElements(queue::takeAsync);
|
||||
}
|
||||
|
||||
public Publisher<V> iterator() {
|
||||
ReplayProcessor<V> p = ReplayProcessor.create();
|
||||
return p.doOnRequest(new LongConsumer() {
|
||||
|
||||
private int currentIndex = 0;
|
||||
|
||||
@Override
|
||||
public void accept(long n) throws Exception {
|
||||
queue.getValueAsync(currentIndex).onComplete((value, e) -> {
|
||||
if (e != null) {
|
||||
p.onError(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
p.onNext(value);
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
p.onComplete();
|
||||
return;
|
||||
}
|
||||
if (n-1 == 0) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
accept(n-1);
|
||||
} catch (Exception e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public Single<Boolean> addAll(Publisher<? extends V> c) {
|
||||
return new PublisherAdder<V>() {
|
||||
|
||||
@Override
|
||||
public RFuture<Boolean> add(Object o) {
|
||||
return queue.addAsync((V) o);
|
||||
}
|
||||
|
||||
}.addAll(c);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,167 @@
|
||||
package org.redisson;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
import org.redisson.api.RBlockingQueueReactive;
|
||||
import org.redisson.api.RTransferQueue;
|
||||
import org.redisson.api.RTransferQueueReactive;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.shouldHaveThrown;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class RedissonTransferQueueReactiveTest extends BaseReactiveTest {
|
||||
|
||||
@Test
|
||||
public void testTakeElements() {
|
||||
RBlockingQueueReactive<Integer> queue = redisson.getTransferQueue("test");
|
||||
List<Integer> elements = new ArrayList<>();
|
||||
queue.takeElements().subscribe(new Subscriber<Integer>() {
|
||||
|
||||
@Override
|
||||
public void onSubscribe(Subscription s) {
|
||||
s.request(4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Integer t) {
|
||||
elements.add(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
}
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
sync(queue.add(i));
|
||||
}
|
||||
|
||||
assertThat(elements).containsExactly(0, 1, 2, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTake() throws InterruptedException {
|
||||
RBlockingQueueReactive<Integer> queue1 = redisson.getTransferQueue("queue:take");
|
||||
Executors.newSingleThreadScheduledExecutor().schedule(() -> {
|
||||
RBlockingQueueReactive<Integer> queue = redisson.getTransferQueue("queue:take");
|
||||
sync(queue.put(3));
|
||||
}, 10, TimeUnit.SECONDS);
|
||||
|
||||
long s = System.currentTimeMillis();
|
||||
int l = sync(queue1.take());
|
||||
|
||||
Assert.assertEquals(3, l);
|
||||
Assert.assertTrue(System.currentTimeMillis() - s > 9000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPoll() throws InterruptedException {
|
||||
RBlockingQueueReactive<Integer> queue1 = redisson.getTransferQueue("queue1");
|
||||
sync(queue1.put(1));
|
||||
Assert.assertEquals((Integer)1, sync(queue1.poll(2, TimeUnit.SECONDS)));
|
||||
|
||||
long s = System.currentTimeMillis();
|
||||
Assert.assertNull(sync(queue1.poll(5, TimeUnit.SECONDS)));
|
||||
Assert.assertTrue(System.currentTimeMillis() - s > 5000);
|
||||
}
|
||||
@Test
|
||||
public void testAwait() throws InterruptedException {
|
||||
RBlockingQueueReactive<Integer> queue1 = redisson.getTransferQueue("queue1");
|
||||
sync(queue1.put(1));
|
||||
|
||||
Assert.assertEquals((Integer)1, sync(queue1.poll(10, TimeUnit.SECONDS)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrainTo() {
|
||||
RBlockingQueueReactive<Integer> queue = redisson.getTransferQueue("queue");
|
||||
for (int i = 0 ; i < 100; i++) {
|
||||
sync(queue.offer(i));
|
||||
}
|
||||
Assert.assertEquals(100, sync(queue.size()).intValue());
|
||||
Set<Integer> batch = new HashSet<Integer>();
|
||||
int count = sync(queue.drainTo(batch, 10));
|
||||
Assert.assertEquals(10, count);
|
||||
Assert.assertEquals(10, batch.size());
|
||||
Assert.assertEquals(90, sync(queue.size()).intValue());
|
||||
sync(queue.drainTo(batch, 10));
|
||||
sync(queue.drainTo(batch, 20));
|
||||
sync(queue.drainTo(batch, 60));
|
||||
Assert.assertEquals(0, sync(queue.size()).intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlockingQueue() throws InterruptedException {
|
||||
|
||||
RTransferQueueReactive<Integer> queue = redisson.getTransferQueue("test_:blocking:queue:");
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(10);
|
||||
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
int total = 100;
|
||||
for (int i = 0; i < total; i++) {
|
||||
// runnable won't be executed in any particular order, and hence, int value as well.
|
||||
executor.submit(() -> {
|
||||
sync(redisson.getTransferQueue("test_:blocking:queue:").add(counter.incrementAndGet()));
|
||||
});
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
assertThat(executor.awaitTermination(10, TimeUnit.SECONDS)).isTrue();
|
||||
|
||||
int count = 0;
|
||||
while (count < total) {
|
||||
int item = sync(queue.take());
|
||||
assertTrue(item > 0 && item <= total);
|
||||
count++;
|
||||
}
|
||||
|
||||
assertThat(counter.get()).isEqualTo(total);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrainToCollection() throws Exception {
|
||||
RBlockingQueueReactive<Object> queue1 = redisson.getTransferQueue("queue1");
|
||||
sync(queue1.put(1));
|
||||
sync(queue1.put(2L));
|
||||
sync(queue1.put("e"));
|
||||
|
||||
ArrayList<Object> dst = new ArrayList<Object>();
|
||||
sync(queue1.drainTo(dst));
|
||||
assertThat(dst).containsExactly(1, 2L, "e");
|
||||
Assert.assertEquals(0, sync(queue1.size()).intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrainToCollectionLimited() throws Exception {
|
||||
RBlockingQueueReactive<Object> queue1 = redisson.getTransferQueue("queue1");
|
||||
sync(queue1.put(1));
|
||||
sync(queue1.put(2L));
|
||||
sync(queue1.put("e"));
|
||||
|
||||
ArrayList<Object> dst = new ArrayList<Object>();
|
||||
sync(queue1.drainTo(dst, 2));
|
||||
assertThat(dst).containsExactly(1, 2L);
|
||||
Assert.assertEquals(1, sync(queue1.size()).intValue());
|
||||
|
||||
dst.clear();
|
||||
sync(queue1.drainTo(dst, 2));
|
||||
assertThat(dst).containsExactly("e");
|
||||
}
|
||||
}
|
@ -0,0 +1,244 @@
|
||||
package org.redisson;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.redisson.api.RTransferQueue;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Nikita Koksharov
|
||||
*/
|
||||
public class RedissonTransferQueueTest extends BaseTest {
|
||||
|
||||
@Test
|
||||
public void testTryTransferWithDelay() throws InterruptedException, ExecutionException {
|
||||
RTransferQueue<Integer> queue1 = redisson.getTransferQueue("queue");
|
||||
Future<?> f = Executors.newSingleThreadExecutor().submit(() -> {
|
||||
RTransferQueue<Integer> queue = redisson.getTransferQueue("queue");
|
||||
try {
|
||||
long time = System.currentTimeMillis();
|
||||
boolean res = queue.tryTransfer(3, 2, TimeUnit.SECONDS);
|
||||
assertThat(System.currentTimeMillis() - time).isGreaterThan(1900);
|
||||
assertThat(res).isFalse();
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
boolean res2 = queue.tryTransfer(4, 1, TimeUnit.SECONDS);
|
||||
assertThat(res2).isTrue();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
Thread.sleep(2100);
|
||||
|
||||
assertThat(queue1.size()).isZero();
|
||||
|
||||
Thread.sleep(1100);
|
||||
|
||||
assertThat(queue1.size()).isEqualTo(1);
|
||||
assertThat(queue1.peek()).isEqualTo(4);
|
||||
assertThat(queue1.poll()).isEqualTo(4);
|
||||
f.get();
|
||||
assertThat(queue1.size()).isZero();
|
||||
assertThat(queue1.peek()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransfer() throws InterruptedException, ExecutionException {
|
||||
RTransferQueue<Integer> queue1 = redisson.getTransferQueue("queue");
|
||||
AtomicBoolean takeExecuted = new AtomicBoolean();
|
||||
Future<?> f = Executors.newSingleThreadExecutor().submit(() -> {
|
||||
RTransferQueue<Integer> queue = redisson.getTransferQueue("queue");
|
||||
try {
|
||||
long time = System.currentTimeMillis();
|
||||
queue.transfer(3);
|
||||
assertThat(takeExecuted.get()).isTrue();
|
||||
assertThat(System.currentTimeMillis() - time).isGreaterThan(3000);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
});
|
||||
|
||||
Thread.sleep(3000);
|
||||
|
||||
assertThat(queue1.size()).isEqualTo(1);
|
||||
assertThat(queue1.peek()).isEqualTo(3);
|
||||
assertThat(queue1.take()).isEqualTo(3);
|
||||
takeExecuted.set(true);
|
||||
f.get();
|
||||
assertThat(queue1.size()).isZero();
|
||||
assertThat(queue1.peek()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTryTransfer() throws InterruptedException, ExecutionException {
|
||||
RTransferQueue<Integer> queue1 = redisson.getTransferQueue("queue");
|
||||
AtomicBoolean takeExecuted = new AtomicBoolean();
|
||||
ScheduledFuture<?> f = Executors.newSingleThreadScheduledExecutor().schedule(() -> {
|
||||
RTransferQueue<Integer> queue = redisson.getTransferQueue("queue");
|
||||
boolean res = queue.tryTransfer(3);
|
||||
assertThat(takeExecuted.get()).isTrue();
|
||||
assertThat(res).isTrue();
|
||||
boolean res2 = queue.tryTransfer(4);
|
||||
assertThat(res2).isFalse();
|
||||
}, 4, TimeUnit.SECONDS);
|
||||
|
||||
long s = System.currentTimeMillis();
|
||||
int l = queue1.take();
|
||||
takeExecuted.set(true);
|
||||
|
||||
Assert.assertEquals(3, l);
|
||||
Assert.assertTrue(System.currentTimeMillis() - s > 3900);
|
||||
f.get();
|
||||
assertThat(queue1.size()).isZero();
|
||||
assertThat(queue1.peek()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTake() throws InterruptedException {
|
||||
RTransferQueue<Integer> queue1 = redisson.getTransferQueue("queue");
|
||||
Executors.newSingleThreadScheduledExecutor().schedule(() -> {
|
||||
RTransferQueue<Integer> queue = redisson.getTransferQueue("queue");
|
||||
try {
|
||||
queue.put(3);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, 10, TimeUnit.SECONDS);
|
||||
|
||||
long s = System.currentTimeMillis();
|
||||
int l = queue1.take();
|
||||
|
||||
Assert.assertEquals(3, l);
|
||||
Assert.assertTrue(System.currentTimeMillis() - s > 9000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPoll() throws InterruptedException {
|
||||
RTransferQueue<Integer> queue1 = redisson.getTransferQueue("queue");
|
||||
queue1.put(1);
|
||||
Assert.assertEquals((Integer)1, queue1.poll(2, TimeUnit.SECONDS));
|
||||
|
||||
long s = System.currentTimeMillis();
|
||||
Assert.assertNull(queue1.poll(5, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(System.currentTimeMillis() - s > 4900);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPeek() {
|
||||
RTransferQueue<String> queue = redisson.getTransferQueue("queue");
|
||||
assertThat(queue.peek()).isNull();
|
||||
|
||||
queue.add("1");
|
||||
|
||||
assertThat(queue.size()).isEqualTo(1);
|
||||
assertThat(queue.peek()).isEqualTo("1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadAll() {
|
||||
RTransferQueue<String> queue = redisson.getTransferQueue("queue");
|
||||
|
||||
queue.add("1");
|
||||
queue.add("2");
|
||||
queue.add("3");
|
||||
queue.add("4");
|
||||
|
||||
assertThat(queue.readAll()).containsExactly("1", "2", "3", "4");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrainTo() {
|
||||
RTransferQueue<Integer> queue = redisson.getTransferQueue("queue");
|
||||
for (int i = 0 ; i < 100; i++) {
|
||||
queue.offer(i);
|
||||
}
|
||||
Assert.assertEquals(100, queue.size());
|
||||
Set<Integer> batch = new HashSet<>();
|
||||
int count = queue.drainTo(batch, 10);
|
||||
Assert.assertEquals(10, count);
|
||||
Assert.assertEquals(10, batch.size());
|
||||
Assert.assertEquals(90, queue.size());
|
||||
queue.drainTo(batch, 10);
|
||||
queue.drainTo(batch, 20);
|
||||
queue.drainTo(batch, 60);
|
||||
Assert.assertEquals(0, queue.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrainToSingle() {
|
||||
RTransferQueue<Integer> queue = redisson.getTransferQueue("queue");
|
||||
Assert.assertTrue(queue.add(1));
|
||||
Assert.assertEquals(1, queue.size());
|
||||
Set<Integer> batch = new HashSet<>();
|
||||
int count = queue.drainTo(batch);
|
||||
Assert.assertEquals(1, count);
|
||||
Assert.assertEquals(1, batch.size());
|
||||
Assert.assertTrue(queue.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClear() throws ExecutionException, InterruptedException {
|
||||
RTransferQueue<String> queue = redisson.getTransferQueue("queue");
|
||||
queue.add("1");
|
||||
queue.add("4");
|
||||
queue.add("2");
|
||||
queue.add("5");
|
||||
queue.add("3");
|
||||
|
||||
ScheduledFuture<?> f = Executors.newSingleThreadScheduledExecutor().schedule(() -> {
|
||||
RTransferQueue<Integer> queue1 = redisson.getTransferQueue("queue");
|
||||
try {
|
||||
queue1.take();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, 1, TimeUnit.SECONDS);
|
||||
|
||||
f.get();
|
||||
queue.clear();
|
||||
assertThat(redisson.getKeys().count()).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIteratorRemove() {
|
||||
RTransferQueue<String> queue = redisson.getTransferQueue("queue");
|
||||
queue.add("1");
|
||||
queue.add("4");
|
||||
queue.add("2");
|
||||
queue.add("5");
|
||||
queue.add("3");
|
||||
|
||||
for (Iterator<String> iterator = queue.iterator(); iterator.hasNext();) {
|
||||
String value = iterator.next();
|
||||
if (value.equals("2")) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(queue).containsExactly("1", "4", "5", "3");
|
||||
|
||||
int iteration = 0;
|
||||
for (Iterator<String> iterator = queue.iterator(); iterator.hasNext();) {
|
||||
iterator.next();
|
||||
iterator.remove();
|
||||
iteration++;
|
||||
}
|
||||
|
||||
Assert.assertEquals(4, iteration);
|
||||
|
||||
Assert.assertEquals(0, queue.size());
|
||||
Assert.assertTrue(queue.isEmpty());
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue