ScheduledFuture memory allocation optimization. #1158

pull/1204/head
Nikita 7 years ago
parent 4c8f966713
commit 353f9aa24d

@ -22,6 +22,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
@ -34,6 +35,7 @@ import org.redisson.api.RMap;
import org.redisson.api.RedissonClient; import org.redisson.api.RedissonClient;
import org.redisson.api.RemoteInvocationOptions; import org.redisson.api.RemoteInvocationOptions;
import org.redisson.api.annotation.RRemoteAsync; import org.redisson.api.annotation.RRemoteAsync;
import org.redisson.client.RedisException;
import org.redisson.client.codec.Codec; import org.redisson.client.codec.Codec;
import org.redisson.client.codec.LongCodec; import org.redisson.client.codec.LongCodec;
import org.redisson.client.protocol.RedisCommands; import org.redisson.client.protocol.RedisCommands;
@ -51,6 +53,7 @@ import org.redisson.remote.RemoteServiceResponse;
import org.redisson.remote.RemoteServiceTimeoutException; import org.redisson.remote.RemoteServiceTimeoutException;
import org.redisson.remote.ResponseEntry; import org.redisson.remote.ResponseEntry;
import org.redisson.remote.ResponseEntry.Key; import org.redisson.remote.ResponseEntry.Key;
import org.redisson.remote.ResponseEntry.Result;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -192,6 +195,20 @@ public abstract class BaseRemoteService {
final RemoteServiceRequest request = new RemoteServiceRequest(executorId, requestId, method.getName(), getMethodSignatures(method), args, final RemoteServiceRequest request = new RemoteServiceRequest(executorId, requestId, method.getName(), getMethodSignatures(method), args,
optionsCopy, System.currentTimeMillis()); optionsCopy, System.currentTimeMillis());
final RFuture<RemoteServiceAck> ackFuture;
if (optionsCopy.isAckExpected()) {
ackFuture = poll(optionsCopy.getAckTimeoutInMillis(), requestId, false);
} else {
ackFuture = null;
}
final RPromise<RRemoteServiceResponse> responseFuture;
if (optionsCopy.isResultExpected()) {
responseFuture = poll(optionsCopy.getExecutionTimeoutInMillis(), requestId, false);
} else {
responseFuture = null;
}
final RemotePromise<Object> result = new RemotePromise<Object>(requestId) { final RemotePromise<Object> result = new RemotePromise<Object>(requestId) {
@Override @Override
@ -248,7 +265,7 @@ public abstract class BaseRemoteService {
return false; return false;
} }
cancelExecution(optionsCopy, request, mayInterruptIfRunning, this); cancelExecution(optionsCopy, request, mayInterruptIfRunning, this, responseFuture);
try { try {
awaitUninterruptibly(60, TimeUnit.SECONDS); awaitUninterruptibly(60, TimeUnit.SECONDS);
@ -265,16 +282,36 @@ public abstract class BaseRemoteService {
@Override @Override
public void operationComplete(Future<Boolean> future) throws Exception { public void operationComplete(Future<Boolean> future) throws Exception {
if (!future.isSuccess()) { if (!future.isSuccess()) {
if (responseFuture != null) {
responseFuture.cancel(false);
}
if (ackFuture != null) {
ackFuture.cancel(false);
}
result.tryFailure(future.cause()); result.tryFailure(future.cause());
return; return;
} }
if (!future.get()) {
result.tryFailure(new RedisException("Task hasn't been added"));
if (responseFuture != null) {
responseFuture.cancel(false);
}
if (ackFuture != null) {
ackFuture.cancel(false);
}
return;
}
if (optionsCopy.isAckExpected()) { if (optionsCopy.isAckExpected()) {
RPromise<RemoteServiceAck> ackFuture = poll(optionsCopy.getAckTimeoutInMillis(), requestId);
ackFuture.addListener(new FutureListener<RemoteServiceAck>() { ackFuture.addListener(new FutureListener<RemoteServiceAck>() {
@Override @Override
public void operationComplete(Future<RemoteServiceAck> future) throws Exception { public void operationComplete(Future<RemoteServiceAck> future) throws Exception {
if (!future.isSuccess()) { if (!future.isSuccess()) {
if (responseFuture != null) {
responseFuture.cancel(false);
}
result.tryFailure(future.cause()); result.tryFailure(future.cause());
return; return;
} }
@ -303,17 +340,17 @@ public abstract class BaseRemoteService {
return; return;
} }
awaitResultAsync(optionsCopy, result, request, ackName); awaitResultAsync(optionsCopy, result, request, ackName, responseFuture);
} }
}); });
} else { } else {
awaitResultAsync(optionsCopy, result, request); awaitResultAsync(optionsCopy, result, request, responseFuture);
} }
} }
}); });
} else { } else {
awaitResultAsync(optionsCopy, result, request); awaitResultAsync(optionsCopy, result, request, responseFuture);
} }
} }
}); });
@ -326,7 +363,7 @@ public abstract class BaseRemoteService {
} }
private void awaitResultAsync(final RemoteInvocationOptions optionsCopy, final RemotePromise<Object> result, private void awaitResultAsync(final RemoteInvocationOptions optionsCopy, final RemotePromise<Object> result,
final RemoteServiceRequest request, final String ackName) { final RemoteServiceRequest request, final String ackName, final RFuture<RRemoteServiceResponse> responseFuture) {
RFuture<Boolean> deleteFuture = redisson.getBucket(ackName).deleteAsync(); RFuture<Boolean> deleteFuture = redisson.getBucket(ackName).deleteAsync();
deleteFuture.addListener(new FutureListener<Boolean>() { deleteFuture.addListener(new FutureListener<Boolean>() {
@Override @Override
@ -336,21 +373,19 @@ public abstract class BaseRemoteService {
return; return;
} }
awaitResultAsync(optionsCopy, result, request); awaitResultAsync(optionsCopy, result, request, responseFuture);
} }
}); });
} }
protected void awaitResultAsync(final RemoteInvocationOptions optionsCopy, final RemotePromise<Object> result, protected void awaitResultAsync(final RemoteInvocationOptions optionsCopy, final RemotePromise<Object> result,
final RemoteServiceRequest request) { final RemoteServiceRequest request, RFuture<RRemoteServiceResponse> responseFuture) {
// poll for the response only if expected // poll for the response only if expected
if (!optionsCopy.isResultExpected()) { if (!optionsCopy.isResultExpected()) {
return; return;
} }
String requestId = request.getId(); final String requestId = request.getId();
RPromise<RRemoteServiceResponse> responseFuture = poll(optionsCopy.getExecutionTimeoutInMillis(), requestId);
responseFuture.addListener(new FutureListener<RRemoteServiceResponse>() { responseFuture.addListener(new FutureListener<RRemoteServiceResponse>() {
@Override @Override
@ -384,9 +419,9 @@ public abstract class BaseRemoteService {
} }
private <T extends RRemoteServiceResponse> RPromise<T> poll(final long timeout, private <T extends RRemoteServiceResponse> RPromise<T> poll(final long timeout,
String requestId) { String requestId, boolean insertFirst) {
final RPromise<T> responseFuture = new RedissonPromise<T>();
final Key key = new Key(requestId); final Key key = new Key(requestId);
final RPromise<T> responseFuture = new RedissonPromise<T>();
ResponseEntry entry; ResponseEntry entry;
synchronized (responses) { synchronized (responses) {
@ -399,31 +434,69 @@ public abstract class BaseRemoteService {
} }
} }
final ConcurrentMap<Key, RPromise<? extends RRemoteServiceResponse>> entryResponses = entry.getResponses(); responseFuture.addListener(new FutureListener<T>() {
entryResponses.put(key, responseFuture); @Override
} public void operationComplete(Future<T> future) throws Exception {
if (future.isCancelled()) {
synchronized (responses) {
ResponseEntry entry = responses.get(responseQueueName);
List<Result> list = entry.getResponses().get(key);
for (Iterator<Result> iterator = list.iterator(); iterator.hasNext();) {
Result result = iterator.next();
if (result.getPromise() == responseFuture) {
result.getScheduledFuture().cancel(true);
iterator.remove();
}
}
if (list.isEmpty()) {
entry.getResponses().remove(key);
}
ScheduledFuture<?> future = commandExecutor.getConnectionManager().getGroup().schedule(new Runnable() { if (entry.getResponses().isEmpty()) {
@Override responses.remove(responseQueueName, entry);
public void run() { }
synchronized (responses) { }
ResponseEntry entry = responses.get(responseQueueName);
if (entry == null) {
return;
} }
RemoteServiceTimeoutException ex = new RemoteServiceTimeoutException("No response after " + timeout + "ms"); }
if (responseFuture.tryFailure(ex)) { });
entry.getTimeouts().remove(key);
entry.getResponses().remove(key, responseFuture); ScheduledFuture<?> future = commandExecutor.getConnectionManager().getGroup().schedule(new Runnable() {
if (entry.getResponses().isEmpty()) { @Override
responses.remove(responseQueueName, entry); public void run() {
synchronized (responses) {
ResponseEntry entry = responses.get(responseQueueName);
if (entry == null) {
return;
}
RemoteServiceTimeoutException ex = new RemoteServiceTimeoutException("No response after " + timeout + "ms");
if (responseFuture.tryFailure(ex)) {
List<Result> list = entry.getResponses().get(key);
list.remove(0);
if (list.isEmpty()) {
responses.remove(responseQueueName, entry);
}
} }
} }
} }
}, timeout, TimeUnit.MILLISECONDS);
final Map<Key, List<Result>> entryResponses = entry.getResponses();
List<Result> list = entryResponses.get(key);
if (list == null) {
list = new ArrayList<Result>();
entryResponses.put(key, list);
}
Result res = new Result(responseFuture, future);
if (insertFirst) {
list.add(0, res);
} else {
list.add(res);
} }
}, timeout, TimeUnit.MILLISECONDS); }
entry.getTimeouts().put(key, future);
pollTasks(entry); pollTasks(entry);
return responseFuture; return responseFuture;
@ -450,16 +523,21 @@ public abstract class BaseRemoteService {
synchronized (responses) { synchronized (responses) {
ResponseEntry entry = responses.get(responseQueueName); ResponseEntry entry = responses.get(responseQueueName);
if (entry == null) { if (entry == null) {
log.error("Can't find entry " + responseQueueName);
return; return;
} }
Key key = new Key(response.getId()); Key key = new Key(response.getId());
ConcurrentMap<Key, RPromise<? extends RRemoteServiceResponse>> entryResponses = entry.getResponses(); List<Result> list = entry.getResponses().get(key);
promise = (RPromise<RRemoteServiceResponse>) entryResponses.remove(key); Result res = list.remove(0);
java.util.concurrent.ScheduledFuture<?> timeoutFuture = entry.getTimeouts().remove(key); if (list.isEmpty()) {
timeoutFuture.cancel(false); entry.getResponses().remove(key);
}
promise = res.getPromise();
res.getScheduledFuture().cancel(true);
if (entryResponses.isEmpty()) { if (entry.getResponses().isEmpty()) {
responses.remove(responseQueueName, entry); responses.remove(responseQueueName, entry);
} else { } else {
RBlockingQueue<RRemoteServiceResponse> responseQueue = redisson.getBlockingQueue(responseQueueName, codec); RBlockingQueue<RRemoteServiceResponse> responseQueue = redisson.getBlockingQueue(responseQueueName, codec);
@ -470,7 +548,6 @@ public abstract class BaseRemoteService {
if (promise != null) { if (promise != null) {
promise.trySuccess(response); promise.trySuccess(response);
} }
} }
}); });
} }
@ -583,7 +660,7 @@ public abstract class BaseRemoteService {
} }
if (future.getNow()) { if (future.getNow()) {
RPromise<RemoteServiceAck> ackFuture = poll(optionsCopy.getAckTimeoutInMillis(), requestId); RPromise<RemoteServiceAck> ackFuture = poll(commandExecutor.getConnectionManager().getConfig().getTimeout(), requestId, true);
ackFuture.addListener(new FutureListener<RemoteServiceAck>() { ackFuture.addListener(new FutureListener<RemoteServiceAck>() {
@Override @Override
public void operationComplete(Future<RemoteServiceAck> future) throws Exception { public void operationComplete(Future<RemoteServiceAck> future) throws Exception {
@ -655,7 +732,7 @@ public abstract class BaseRemoteService {
} }
private void cancelExecution(RemoteInvocationOptions optionsCopy, private void cancelExecution(RemoteInvocationOptions optionsCopy,
RemoteServiceRequest request, boolean mayInterruptIfRunning, RemotePromise<Object> remotePromise) { RemoteServiceRequest request, boolean mayInterruptIfRunning, RemotePromise<Object> remotePromise, RFuture<RRemoteServiceResponse> responseFuture) {
RMap<String, RemoteServiceCancelRequest> canceledRequests = redisson.getMap(cancelRequestMapName, codec); RMap<String, RemoteServiceCancelRequest> canceledRequests = redisson.getMap(cancelRequestMapName, codec);
canceledRequests.putAsync(request.getId(), new RemoteServiceCancelRequest(mayInterruptIfRunning, false)); canceledRequests.putAsync(request.getId(), new RemoteServiceCancelRequest(mayInterruptIfRunning, false));
canceledRequests.expireAsync(60, TimeUnit.SECONDS); canceledRequests.expireAsync(60, TimeUnit.SECONDS);
@ -664,7 +741,7 @@ public abstract class BaseRemoteService {
if (!optionsCopy.isResultExpected()) { if (!optionsCopy.isResultExpected()) {
RemoteInvocationOptions options = new RemoteInvocationOptions(optionsCopy); RemoteInvocationOptions options = new RemoteInvocationOptions(optionsCopy);
options.expectResultWithin(60, TimeUnit.SECONDS); options.expectResultWithin(60, TimeUnit.SECONDS);
awaitResultAsync(options, remotePromise, request); awaitResultAsync(options, remotePromise, request, responseFuture);
} }
} }

@ -28,6 +28,7 @@ import org.redisson.client.codec.Codec;
import org.redisson.client.codec.LongCodec; import org.redisson.client.codec.LongCodec;
import org.redisson.client.protocol.RedisCommands; import org.redisson.client.protocol.RedisCommands;
import org.redisson.command.CommandExecutor; import org.redisson.command.CommandExecutor;
import org.redisson.remote.RRemoteServiceResponse;
import org.redisson.remote.RemoteServiceRequest; import org.redisson.remote.RemoteServiceRequest;
import org.redisson.remote.ResponseEntry; import org.redisson.remote.ResponseEntry;
@ -115,7 +116,7 @@ public class ScheduledTasksService extends TasksService {
@Override @Override
protected void awaitResultAsync(final RemoteInvocationOptions optionsCopy, final RemotePromise<Object> result, protected void awaitResultAsync(final RemoteInvocationOptions optionsCopy, final RemotePromise<Object> result,
final RemoteServiceRequest request) { final RemoteServiceRequest request, final RFuture<RRemoteServiceResponse> responseFuture) {
if (!optionsCopy.isResultExpected()) { if (!optionsCopy.isResultExpected()) {
return; return;
} }
@ -129,11 +130,11 @@ public class ScheduledTasksService extends TasksService {
commandExecutor.getConnectionManager().getGroup().schedule(new Runnable() { commandExecutor.getConnectionManager().getGroup().schedule(new Runnable() {
@Override @Override
public void run() { public void run() {
ScheduledTasksService.super.awaitResultAsync(optionsCopy, result, request); ScheduledTasksService.super.awaitResultAsync(optionsCopy, result, request, responseFuture);
} }
}, (long)(delay - delay*0.10), TimeUnit.MILLISECONDS); }, (long)(delay - delay*0.10), TimeUnit.MILLISECONDS);
} else { } else {
super.awaitResultAsync(optionsCopy, result, request); super.awaitResultAsync(optionsCopy, result, request, responseFuture);
} }
} }

@ -15,7 +15,9 @@
*/ */
package org.redisson.remote; package org.redisson.remote;
import java.util.concurrent.ConcurrentMap; import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@ -24,7 +26,6 @@ import org.redisson.misc.RPromise;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.util.internal.PlatformDependent;
/** /**
* *
@ -76,15 +77,31 @@ public class ResponseEntry {
} }
private final ConcurrentMap<Key, RPromise<? extends RRemoteServiceResponse>> responses = PlatformDependent.newConcurrentHashMap(); public static class Result {
private final ConcurrentMap<Key, ScheduledFuture<?>> timeouts = PlatformDependent.newConcurrentHashMap();
private final AtomicBoolean started = new AtomicBoolean(); private final RPromise<? extends RRemoteServiceResponse> promise;
private final ScheduledFuture<?> scheduledFuture;
public ConcurrentMap<Key, ScheduledFuture<?>> getTimeouts() {
return timeouts; public Result(RPromise<? extends RRemoteServiceResponse> promise, ScheduledFuture<?> scheduledFuture) {
super();
this.promise = promise;
this.scheduledFuture = scheduledFuture;
}
public <T extends RRemoteServiceResponse> RPromise<T> getPromise() {
return (RPromise<T>) promise;
}
public ScheduledFuture<?> getScheduledFuture() {
return scheduledFuture;
}
} }
public ConcurrentMap<Key, RPromise<? extends RRemoteServiceResponse>> getResponses() { private final Map<Key, List<Result>> responses = new HashMap<Key, List<Result>>();
private final AtomicBoolean started = new AtomicBoolean();
public Map<Key, List<Result>> getResponses() {
return responses; return responses;
} }

Loading…
Cancel
Save