|
|
|
@ -36,6 +36,7 @@ import org.redisson.client.protocol.Decoder;
|
|
|
|
|
import org.redisson.client.protocol.QueueCommand;
|
|
|
|
|
import org.redisson.client.protocol.RedisCommand.ValueType;
|
|
|
|
|
import org.redisson.client.protocol.decoder.MultiDecoder;
|
|
|
|
|
import org.redisson.client.protocol.decoder.NestedMultiDecoder;
|
|
|
|
|
import org.redisson.client.protocol.pubsub.Message;
|
|
|
|
|
import org.redisson.client.protocol.pubsub.PubSubMessage;
|
|
|
|
|
import org.redisson.client.protocol.pubsub.PubSubPatternMessage;
|
|
|
|
@ -79,61 +80,99 @@ public class CommandDecoder extends ReplayingDecoder<State> {
|
|
|
|
|
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
|
|
|
|
QueueCommand data = ctx.channel().attr(CommandsQueue.CURRENT_COMMAND).get();
|
|
|
|
|
|
|
|
|
|
Decoder<Object> currentDecoder = null;
|
|
|
|
|
if (data == null) {
|
|
|
|
|
currentDecoder = StringCodec.INSTANCE.getValueDecoder();
|
|
|
|
|
if (log.isTraceEnabled()) {
|
|
|
|
|
log.trace("channel: {} message: {}", ctx.channel(), in.toString(0, in.writerIndex(), CharsetUtil.UTF_8));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (state() == null) {
|
|
|
|
|
state(new State());
|
|
|
|
|
|
|
|
|
|
if (log.isTraceEnabled()) {
|
|
|
|
|
log.trace("channel: {} message: {}", ctx.channel(), in.toString(0, in.writerIndex(), CharsetUtil.UTF_8));
|
|
|
|
|
boolean makeCheckpoint = data != null;
|
|
|
|
|
if (data != null) {
|
|
|
|
|
if (data instanceof CommandsData) {
|
|
|
|
|
makeCheckpoint = false;
|
|
|
|
|
} else {
|
|
|
|
|
CommandData<Object, Object> cmd = (CommandData<Object, Object>)data;
|
|
|
|
|
if (cmd.getCommand().getReplayMultiDecoder() != null && NestedMultiDecoder.class.isAssignableFrom(cmd.getCommand().getReplayMultiDecoder().getClass())) {
|
|
|
|
|
makeCheckpoint = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
state(new State(makeCheckpoint));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state().setDecoderState(null);
|
|
|
|
|
|
|
|
|
|
if (data == null) {
|
|
|
|
|
decode(in, null, null, ctx.channel(), currentDecoder);
|
|
|
|
|
decode(in, null, null, ctx.channel());
|
|
|
|
|
} else if (data instanceof CommandData) {
|
|
|
|
|
CommandData<Object, Object> cmd = (CommandData<Object, Object>)data;
|
|
|
|
|
try {
|
|
|
|
|
// if (state().getSize() > 0) {
|
|
|
|
|
// List<Object> respParts = new ArrayList<Object>();
|
|
|
|
|
// if (state().getRespParts() != null) {
|
|
|
|
|
// respParts = state().getRespParts();
|
|
|
|
|
// }
|
|
|
|
|
// decodeMulti(in, cmd, null, ctx.channel(), currentDecoder, state().getSize(), respParts, true);
|
|
|
|
|
// } else {
|
|
|
|
|
decode(in, cmd, null, ctx.channel(), currentDecoder);
|
|
|
|
|
// }
|
|
|
|
|
if (state().getLevels().size() > 0) {
|
|
|
|
|
decodeFromCheckpoint(ctx, in, data, cmd);
|
|
|
|
|
} else {
|
|
|
|
|
decode(in, cmd, null, ctx.channel());
|
|
|
|
|
}
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
cmd.getPromise().tryFailure(e);
|
|
|
|
|
}
|
|
|
|
|
} else if (data instanceof CommandsData) {
|
|
|
|
|
CommandsData commands = (CommandsData)data;
|
|
|
|
|
|
|
|
|
|
handleCommandsDataResponse(ctx, in, data, currentDecoder, commands);
|
|
|
|
|
decodeCommandBatch(ctx, in, data, commands);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ctx.pipeline().get(CommandsQueue.class).sendNextCommand(ctx.channel());
|
|
|
|
|
|
|
|
|
|
state(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void handleCommandsDataResponse(ChannelHandlerContext ctx, ByteBuf in, QueueCommand data,
|
|
|
|
|
Decoder<Object> currentDecoder, CommandsData commands) {
|
|
|
|
|
int i = state().getIndex();
|
|
|
|
|
private void decodeFromCheckpoint(ChannelHandlerContext ctx, ByteBuf in, QueueCommand data,
|
|
|
|
|
CommandData<Object, Object> cmd) throws IOException {
|
|
|
|
|
if (state().getLevels().size() == 2) {
|
|
|
|
|
StateLevel secondLevel = state().getLevels().get(1);
|
|
|
|
|
|
|
|
|
|
if (secondLevel.getParts().isEmpty()) {
|
|
|
|
|
state().getLevels().remove(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (state().getLevels().size() == 2) {
|
|
|
|
|
StateLevel firstLevel = state().getLevels().get(0);
|
|
|
|
|
StateLevel secondLevel = state().getLevels().get(1);
|
|
|
|
|
|
|
|
|
|
decodeMulti(in, cmd, firstLevel.getParts(), ctx.channel(), secondLevel.getSize(), secondLevel.getParts());
|
|
|
|
|
|
|
|
|
|
Channel channel = ctx.channel();
|
|
|
|
|
MultiDecoder<Object> decoder = messageDecoder(cmd, firstLevel.getParts(), channel);
|
|
|
|
|
if (decoder != null) {
|
|
|
|
|
Object result = decoder.decode(firstLevel.getParts(), state());
|
|
|
|
|
if (data != null) {
|
|
|
|
|
handleResult(cmd, null, result, true, channel);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (state().getLevels().size() == 1) {
|
|
|
|
|
StateLevel firstLevel = state().getLevels().get(0);
|
|
|
|
|
if (firstLevel.getParts().isEmpty()) {
|
|
|
|
|
state().resetLevel();
|
|
|
|
|
decode(in, cmd, null, ctx.channel());
|
|
|
|
|
} else {
|
|
|
|
|
decodeMulti(in, cmd, null, ctx.channel(), firstLevel.getSize(), firstLevel.getParts());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void decodeCommandBatch(ChannelHandlerContext ctx, ByteBuf in, QueueCommand data,
|
|
|
|
|
CommandsData commandBatch) {
|
|
|
|
|
int i = state().getBatchIndex();
|
|
|
|
|
|
|
|
|
|
RedisException error = null;
|
|
|
|
|
while (in.writerIndex() > in.readerIndex()) {
|
|
|
|
|
CommandData<Object, Object> cmd = null;
|
|
|
|
|
try {
|
|
|
|
|
checkpoint();
|
|
|
|
|
state().setIndex(i);
|
|
|
|
|
cmd = (CommandData<Object, Object>) commands.getCommands().get(i);
|
|
|
|
|
decode(in, cmd, null, ctx.channel(), currentDecoder);
|
|
|
|
|
state().setBatchIndex(i);
|
|
|
|
|
cmd = (CommandData<Object, Object>) commandBatch.getCommands().get(i);
|
|
|
|
|
decode(in, cmd, null, ctx.channel());
|
|
|
|
|
i++;
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
cmd.getPromise().tryFailure(e);
|
|
|
|
@ -147,8 +186,8 @@ public class CommandDecoder extends ReplayingDecoder<State> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i == commands.getCommands().size()) {
|
|
|
|
|
Promise<Void> promise = commands.getPromise();
|
|
|
|
|
if (i == commandBatch.getCommands().size()) {
|
|
|
|
|
Promise<Void> promise = commandBatch.getPromise();
|
|
|
|
|
if (error != null) {
|
|
|
|
|
if (!promise.tryFailure(error) && promise.cause() instanceof RedisTimeoutException) {
|
|
|
|
|
log.warn("response has been skipped due to timeout! channel: {}, command: {}", ctx.channel(), data);
|
|
|
|
@ -164,11 +203,11 @@ public class CommandDecoder extends ReplayingDecoder<State> {
|
|
|
|
|
state(null);
|
|
|
|
|
} else {
|
|
|
|
|
checkpoint();
|
|
|
|
|
state().setIndex(i);
|
|
|
|
|
state().setBatchIndex(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void decode(ByteBuf in, CommandData<Object, Object> data, List<Object> parts, Channel channel, Decoder<Object> currentDecoder) throws IOException {
|
|
|
|
|
private void decode(ByteBuf in, CommandData<Object, Object> data, List<Object> parts, Channel channel) throws IOException {
|
|
|
|
|
int code = in.readByte();
|
|
|
|
|
if (code == '+') {
|
|
|
|
|
String result = in.readBytes(in.bytesBefore((byte) '\r')).toString(CharsetUtil.UTF_8);
|
|
|
|
@ -212,32 +251,40 @@ public class CommandDecoder extends ReplayingDecoder<State> {
|
|
|
|
|
ByteBuf buf = readBytes(in);
|
|
|
|
|
Object result = null;
|
|
|
|
|
if (buf != null) {
|
|
|
|
|
result = decoder(data, parts, currentDecoder).decode(buf, state());
|
|
|
|
|
Decoder<Object> decoder = selectDecoder(data, parts);
|
|
|
|
|
result = decoder.decode(buf, state());
|
|
|
|
|
}
|
|
|
|
|
handleResult(data, parts, result, false, channel);
|
|
|
|
|
} else if (code == '*') {
|
|
|
|
|
int level = state().incLevel();
|
|
|
|
|
|
|
|
|
|
long size = readLong(in);
|
|
|
|
|
List<Object> respParts = new ArrayList<Object>();
|
|
|
|
|
boolean top = false;
|
|
|
|
|
// if (state().trySetSize(size)) {
|
|
|
|
|
// state().setRespParts(respParts);
|
|
|
|
|
// top = true;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
decodeMulti(in, data, parts, channel, currentDecoder, size, respParts, top);
|
|
|
|
|
List<Object> respParts;
|
|
|
|
|
if (state().getLevels().size()-1 >= level) {
|
|
|
|
|
StateLevel stateLevel = state().getLevels().get(level);
|
|
|
|
|
respParts = stateLevel.getParts();
|
|
|
|
|
size = stateLevel.getSize();
|
|
|
|
|
} else {
|
|
|
|
|
respParts = new ArrayList<Object>();
|
|
|
|
|
if (state().isMakeCheckpoint()) {
|
|
|
|
|
state().addLevel(new StateLevel(size, respParts));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
decodeMulti(in, data, parts, channel, size, respParts);
|
|
|
|
|
} else {
|
|
|
|
|
throw new IllegalStateException("Can't decode replay " + (char)code);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void decodeMulti(ByteBuf in, CommandData<Object, Object> data, List<Object> parts,
|
|
|
|
|
Channel channel, Decoder<Object> currentDecoder, long size, List<Object> respParts, boolean top)
|
|
|
|
|
Channel channel, long size, List<Object> respParts)
|
|
|
|
|
throws IOException {
|
|
|
|
|
for (int i = respParts.size(); i < size; i++) {
|
|
|
|
|
decode(in, data, respParts, channel, currentDecoder);
|
|
|
|
|
// if (top) {
|
|
|
|
|
// checkpoint();
|
|
|
|
|
// }
|
|
|
|
|
decode(in, data, respParts, channel);
|
|
|
|
|
if (state().isMakeCheckpoint()) {
|
|
|
|
|
checkpoint();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MultiDecoder<Object> decoder = messageDecoder(data, respParts, channel);
|
|
|
|
@ -246,7 +293,10 @@ public class CommandDecoder extends ReplayingDecoder<State> {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Object result = decoder.decode(respParts, state());
|
|
|
|
|
|
|
|
|
|
if (data != null) {
|
|
|
|
|
handleResult(data, parts, result, true, channel);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result instanceof Message) {
|
|
|
|
|
// store current message index
|
|
|
|
@ -255,40 +305,34 @@ public class CommandDecoder extends ReplayingDecoder<State> {
|
|
|
|
|
handleMultiResult(data, null, channel, result);
|
|
|
|
|
// has next messages?
|
|
|
|
|
if (in.writerIndex() > in.readerIndex()) {
|
|
|
|
|
decode(in, data, null, channel, currentDecoder);
|
|
|
|
|
decode(in, data, null, channel);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
handleMultiResult(data, parts, channel, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void handleMultiResult(CommandData<Object, Object> data, List<Object> parts,
|
|
|
|
|
Channel channel, Object result) {
|
|
|
|
|
if (data != null) {
|
|
|
|
|
handleResult(data, parts, result, true, channel);
|
|
|
|
|
} else {
|
|
|
|
|
if (result instanceof PubSubStatusMessage) {
|
|
|
|
|
String channelName = ((PubSubStatusMessage) result).getChannel();
|
|
|
|
|
CommandData<Object, Object> d = pubSubChannels.get(channelName);
|
|
|
|
|
if (Arrays.asList("PSUBSCRIBE", "SUBSCRIBE").contains(d.getCommand().getName())) {
|
|
|
|
|
pubSubChannels.remove(channelName);
|
|
|
|
|
pubSubMessageDecoders.put(channelName, d.getMessageDecoder());
|
|
|
|
|
}
|
|
|
|
|
if (Arrays.asList("PUNSUBSCRIBE", "UNSUBSCRIBE").contains(d.getCommand().getName())) {
|
|
|
|
|
pubSubChannels.remove(channelName);
|
|
|
|
|
pubSubMessageDecoders.remove(channelName);
|
|
|
|
|
}
|
|
|
|
|
if (result instanceof PubSubStatusMessage) {
|
|
|
|
|
String channelName = ((PubSubStatusMessage) result).getChannel();
|
|
|
|
|
CommandData<Object, Object> d = pubSubChannels.get(channelName);
|
|
|
|
|
if (Arrays.asList("PSUBSCRIBE", "SUBSCRIBE").contains(d.getCommand().getName())) {
|
|
|
|
|
pubSubChannels.remove(channelName);
|
|
|
|
|
pubSubMessageDecoders.put(channelName, d.getMessageDecoder());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RedisPubSubConnection pubSubConnection = RedisPubSubConnection.getFrom(channel);
|
|
|
|
|
if (result instanceof PubSubStatusMessage) {
|
|
|
|
|
pubSubConnection.onMessage((PubSubStatusMessage) result);
|
|
|
|
|
} else if (result instanceof PubSubMessage) {
|
|
|
|
|
pubSubConnection.onMessage((PubSubMessage) result);
|
|
|
|
|
} else {
|
|
|
|
|
pubSubConnection.onMessage((PubSubPatternMessage) result);
|
|
|
|
|
if (Arrays.asList("PUNSUBSCRIBE", "UNSUBSCRIBE").contains(d.getCommand().getName())) {
|
|
|
|
|
pubSubChannels.remove(channelName);
|
|
|
|
|
pubSubMessageDecoders.remove(channelName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RedisPubSubConnection pubSubConnection = RedisPubSubConnection.getFrom(channel);
|
|
|
|
|
if (result instanceof PubSubStatusMessage) {
|
|
|
|
|
pubSubConnection.onMessage((PubSubStatusMessage) result);
|
|
|
|
|
} else if (result instanceof PubSubMessage) {
|
|
|
|
|
pubSubConnection.onMessage((PubSubMessage) result);
|
|
|
|
|
} else {
|
|
|
|
|
pubSubConnection.onMessage((PubSubPatternMessage) result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void handleResult(CommandData<Object, Object> data, List<Object> parts, Object result, boolean multiResult, Channel channel) {
|
|
|
|
@ -329,7 +373,7 @@ public class CommandDecoder extends ReplayingDecoder<State> {
|
|
|
|
|
return data.getCommand().getReplayMultiDecoder();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Decoder<Object> decoder(CommandData<Object, Object> data, List<Object> parts, Decoder<Object> currentDecoder) {
|
|
|
|
|
private Decoder<Object> selectDecoder(CommandData<Object, Object> data, List<Object> parts) {
|
|
|
|
|
if (data == null) {
|
|
|
|
|
if (parts.size() == 2 && parts.get(0).equals("message")) {
|
|
|
|
|
String channelName = (String) parts.get(1);
|
|
|
|
@ -339,7 +383,7 @@ public class CommandDecoder extends ReplayingDecoder<State> {
|
|
|
|
|
String patternName = (String) parts.get(1);
|
|
|
|
|
return pubSubMessageDecoders.get(patternName);
|
|
|
|
|
}
|
|
|
|
|
return currentDecoder;
|
|
|
|
|
return StringCodec.INSTANCE.getValueDecoder();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Decoder<Object> decoder = data.getCommand().getReplayDecoder();
|
|
|
|
|