com.github.lontime.shaded.org.redisson.command.CommandAsyncService Maven / Gradle / Ivy
The newest version!
/**
* Copyright (c) 2013-2021 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 com.github.lontime.shaded.org.redisson.command;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.ReferenceCountUtil;
import com.github.lontime.shaded.org.redisson.RedissonReference;
import com.github.lontime.shaded.org.redisson.SlotCallback;
import com.github.lontime.shaded.org.redisson.api.NodeType;
import com.github.lontime.shaded.org.redisson.api.RFuture;
import com.github.lontime.shaded.org.redisson.cache.LRUCacheMap;
import com.github.lontime.shaded.org.redisson.client.RedisClient;
import com.github.lontime.shaded.org.redisson.client.RedisException;
import com.github.lontime.shaded.org.redisson.client.codec.Codec;
import com.github.lontime.shaded.org.redisson.client.codec.StringCodec;
import com.github.lontime.shaded.org.redisson.client.protocol.RedisCommand;
import com.github.lontime.shaded.org.redisson.client.protocol.RedisCommands;
import com.github.lontime.shaded.org.redisson.connection.ConnectionManager;
import com.github.lontime.shaded.org.redisson.connection.MasterSlaveEntry;
import com.github.lontime.shaded.org.redisson.connection.NodeSource;
import com.github.lontime.shaded.org.redisson.liveobject.core.RedissonObjectBuilder;
import com.github.lontime.shaded.org.redisson.misc.CompletableFutureWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
*
* @author Nikita Koksharov
*
*/
public class CommandAsyncService implements CommandAsyncExecutor {
static final Logger log = LoggerFactory.getLogger(CommandAsyncService.class);
final ConnectionManager connectionManager;
final RedissonObjectBuilder objectBuilder;
final RedissonObjectBuilder.ReferenceType referenceType;
public CommandAsyncService(ConnectionManager connectionManager, RedissonObjectBuilder objectBuilder, RedissonObjectBuilder.ReferenceType referenceType) {
this.connectionManager = connectionManager;
this.objectBuilder = objectBuilder;
this.referenceType = referenceType;
}
@Override
public ConnectionManager getConnectionManager() {
return connectionManager;
}
private boolean isRedissonReferenceSupportEnabled() {
return objectBuilder != null;
}
@Override
public V getNow(CompletableFuture future) {
try {
return future.getNow(null);
} catch (Exception e) {
return null;
}
}
@Override
public void transfer(CompletableFuture future1, CompletableFuture future2) {
future1.whenComplete((res, e) -> {
if (e != null) {
future2.completeExceptionally(e);
return;
}
future2.complete(res);
});
}
@Override
public V get(RFuture future) {
if (Thread.currentThread().getName().startsWith("redisson-netty")) {
throw new IllegalStateException("Sync methods can't be invoked from async/rx/reactive listeners");
}
try {
return future.toCompletableFuture().get();
} catch (InterruptedException e) {
future.cancel(true);
Thread.currentThread().interrupt();
throw new RedisException(e);
} catch (ExecutionException e) {
throw convertException(e);
}
}
@Override
public V get(CompletableFuture future) {
if (Thread.currentThread().getName().startsWith("redisson-netty")) {
throw new IllegalStateException("Sync methods can't be invoked from async/rx/reactive listeners");
}
try {
return future.get();
} catch (InterruptedException e) {
future.cancel(true);
Thread.currentThread().interrupt();
throw new RedisException(e);
} catch (ExecutionException e) {
throw convertException(e);
}
}
@Override
public V getInterrupted(RFuture future) throws InterruptedException {
try {
return future.toCompletableFuture().get();
} catch (InterruptedException e) {
future.toCompletableFuture().completeExceptionally(e);
throw e;
} catch (ExecutionException e) {
throw convertException(e);
}
}
@Override
public V getInterrupted(CompletableFuture future) throws InterruptedException {
try {
return future.get();
} catch (InterruptedException e) {
future.completeExceptionally(e);
throw e;
} catch (ExecutionException e) {
throw convertException(e);
}
}
protected CompletableFuture createPromise() {
return new CompletableFuture();
}
@Override
public RFuture readAsync(RedisClient client, MasterSlaveEntry entry, Codec codec, RedisCommand command, Object... params) {
return async(true, new NodeSource(entry, client), codec, command, params, false, false);
}
@Override
public RFuture readAsync(RedisClient client, String name, Codec codec, RedisCommand command, Object... params) {
int slot = connectionManager.calcSlot(name);
return async(true, new NodeSource(slot, client), codec, command, params, false, false);
}
public RFuture readAsync(RedisClient client, byte[] key, Codec codec, RedisCommand command, Object... params) {
int slot = connectionManager.calcSlot(key);
return async(true, new NodeSource(slot, client), codec, command, params, false, false);
}
@Override
public RFuture readAsync(RedisClient client, Codec codec, RedisCommand command, Object... params) {
return async(true, new NodeSource(client), codec, command, params, false, false);
}
@Override
public RFuture readRandomAsync(Codec codec, RedisCommand command, Object... params) {
CompletableFuture mainPromise = createPromise();
List nodes = new ArrayList(connectionManager.getEntrySet());
Collections.shuffle(nodes);
retryReadRandomAsync(codec, command, mainPromise, nodes, params);
return new CompletableFutureWrapper<>(mainPromise);
}
@Override
public RFuture readRandomAsync(MasterSlaveEntry entry, Codec codec, RedisCommand command, Object... params) {
CompletableFuture mainPromise = createPromise();
retryReadRandomAsync(codec, command, mainPromise, Collections.singletonList(entry), params);
return new CompletableFutureWrapper<>(mainPromise);
}
private void retryReadRandomAsync(Codec codec, RedisCommand command, CompletableFuture mainPromise,
List nodes, Object... params) {
MasterSlaveEntry entry = nodes.remove(0);
RFuture attemptPromise = async(true, new NodeSource(entry), codec, command, params, false, false);
attemptPromise.whenComplete((res, e) -> {
if (e == null) {
if (res == null) {
if (nodes.isEmpty()) {
mainPromise.complete(null);
} else {
retryReadRandomAsync(codec, command, mainPromise, nodes, params);
}
} else {
mainPromise.complete(res);
}
} else {
mainPromise.completeExceptionally(e);
}
});
}
@Override
public RFuture writeAllVoidAsync(RedisCommand command, Object... params) {
List> futures = writeAllAsync(command, params);
CompletableFuture f = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
return new CompletableFutureWrapper<>(f);
}
@Override
public List> writeAllAsync(RedisCommand> command, Object... params) {
List> futures = connectionManager.getEntrySet().stream().map(e -> {
RFuture f = async(false, new NodeSource(e),
connectionManager.getCodec(), command, params, true, false);
return f.toCompletableFuture();
}).collect(Collectors.toList());
return futures;
}
@Override
public List> readAllAsync(Codec codec, RedisCommand> command, Object... params) {
List> futures = connectionManager.getEntrySet().stream().map(e -> {
RFuture f = async(true, new NodeSource(e), codec, command, params, true, false);
return f.toCompletableFuture();
}).collect(Collectors.toList());
return futures;
}
@Override
public List> readAllAsync(RedisCommand> command, Object... params) {
return readAllAsync(connectionManager.getCodec(), command, params);
}
@Override
public List> executeAllAsync(RedisCommand> command, Object... params) {
Collection nodes = connectionManager.getEntrySet();
List> futures = new ArrayList<>();
nodes.forEach(e -> {
RFuture promise = async(false, new NodeSource(e),
connectionManager.getCodec(), command, params, true, false);
futures.add(promise.toCompletableFuture());
e.getAllEntries().stream().filter(c -> c.getNodeType() == NodeType.SLAVE && !c.isFreezed()).forEach(c -> {
RFuture slavePromise = async(true, new NodeSource(e, c.getClient()),
connectionManager.getCodec(), command, params, true, false);
futures.add(slavePromise.toCompletableFuture());
});
});
return futures;
}
public RedisException convertException(ExecutionException e) {
if (e.getCause() instanceof RedisException) {
return (RedisException) e.getCause();
}
return new RedisException("Unexpected exception while processing command", e.getCause());
}
private NodeSource getNodeSource(String key) {
int slot = connectionManager.calcSlot(key);
return new NodeSource(slot);
}
private NodeSource getNodeSource(byte[] key) {
int slot = connectionManager.calcSlot(key);
return new NodeSource(slot);
}
@Override
public RFuture readAsync(String key, Codec codec, RedisCommand command, Object... params) {
NodeSource source = getNodeSource(key);
return async(true, source, codec, command, params, false, false);
}
@Override
public RFuture readAsync(byte[] key, Codec codec, RedisCommand command, Object... params) {
NodeSource source = getNodeSource(key);
return async(true, source, codec, command, params, false, false);
}
public RFuture readAsync(MasterSlaveEntry entry, Codec codec, RedisCommand command, Object... params) {
return async(true, new NodeSource(entry), codec, command, params, false, false);
}
@Override
public RFuture writeAsync(RedisClient client, Codec codec, RedisCommand command, Object... params) {
MasterSlaveEntry entry = getConnectionManager().getEntry(client);
return writeAsync(entry, codec, command, params);
}
@Override
public RFuture writeAsync(MasterSlaveEntry entry, Codec codec, RedisCommand command, Object... params) {
return async(false, new NodeSource(entry), codec, command, params, false, false);
}
@Override
public RFuture readAsync(String key, RedisCommand command, Object... params) {
return readAsync(key, connectionManager.getCodec(), command, params);
}
@Override
public RFuture evalReadAsync(String key, Codec codec, RedisCommand evalCommandType, String script, List