Gilad Chaplik has uploaded a new change for review. Change subject: webadmin: Avoid duplicate server queries ......................................................................
webadmin: Avoid duplicate server queries While executing several queries in parallel, the UI gets stuck; There is a hard limitation of ~6 connections for http requests (for the same host name). We are getting a lot of performance issues that are related to it. The main reproducer to 'UI not responding' is re-sending a 'slow' query and run out of connections. For a large scale env, this can be a serious problem; In the patch: When sending a second duplicate request from the same place (model, queryType and parameters), while the first request is waiting for response, we will postpone it till the first is finished. NOTE: we will postpone (save) only a single request, and avoid the others; e.g., when pressing 30 time on a key in the GUI the first one will be excuted and the others will pile up on the same place in the set. Change-Id: I483a1c449f1eb2e9820464dbaf0a686f90923bb6 Signed-off-by: Gilad Chaplik <[email protected]> --- M frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/AsyncQuery.java M frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/Frontend.java 2 files changed, 107 insertions(+), 33 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/54/9554/1 diff --git a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/AsyncQuery.java b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/AsyncQuery.java index 2826dc4..1c2ffb1 100644 --- a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/AsyncQuery.java +++ b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/AsyncQuery.java @@ -3,6 +3,7 @@ import org.ovirt.engine.core.common.queries.VdcQueryReturnValue; public class AsyncQuery { + private String queryKey; public Object Model; public INewAsyncCallback asyncCallback; public IAsyncConverter converterCallback; @@ -82,4 +83,12 @@ public IAsyncConverter getConverter() { return converterCallback; } + + public String getQueryKey() { + return queryKey; + } + + public void setQueryKey(String queryKey) { + this.queryKey = queryKey; + } } diff --git a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/Frontend.java b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/Frontend.java index aa19f29..1b8a86e 100644 --- a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/Frontend.java +++ b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/Frontend.java @@ -1,7 +1,9 @@ package org.ovirt.engine.ui.frontend; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -39,6 +41,37 @@ import com.google.gwt.user.client.rpc.StatusCodeException; public class Frontend { + static class QueryWrapper { + VdcQueryType queryType; + VdcQueryParametersBase parameters; + AsyncQuery callback; + + public QueryWrapper(VdcQueryType queryType, + VdcQueryParametersBase parameters, + AsyncQuery callback) { + this.queryType = queryType; + this.parameters = parameters; + this.callback = callback; + } + + public VdcQueryType getQueryType() { + return queryType; + } + + public VdcQueryParametersBase getParameters() { + return parameters; + } + + public AsyncQuery getCallback() { + return callback; + } + + public String getKey() { + return queryType.toString() + parameters.getClass().toString() + + callback.getModel().toString(); + } + } + private static final String RPC_TIMEOUT_EXCEPTION_STATUS_CODE_PREFIX = "120"; //$NON-NLS-1$ private static Logger logger = Logger.getLogger(Frontend.class.getName()); @@ -48,6 +81,9 @@ new ErrorTranslator((AppErrors) GWT.create(AppErrors.class)); private static ErrorTranslator vdsmErrorsTranslator = new ErrorTranslator((VdsmErrors) GWT.create(VdsmErrors.class)); + + static Map<String, QueryWrapper> currentRequests = new HashMap<String, Frontend.QueryWrapper>(); + static Map<String, QueryWrapper> pendingRequests = new HashMap<String, Frontend.QueryWrapper>(); private static VdcQueryType[] subscribedQueryTypes; @@ -131,59 +167,88 @@ public static void RunQuery(final VdcQueryType queryType, final VdcQueryParametersBase parameters, final AsyncQuery callback) { + QueryWrapper queryWrapper = new QueryWrapper(queryType, parameters, callback); + callback.setQueryKey(queryWrapper.getKey()); + if (currentRequests.get(callback.getQueryKey()) == null) { + currentRequests.put(callback.getQueryKey(), queryWrapper); + } else { + pendingRequests.put(callback.getQueryKey(), queryWrapper); + return; + } + initQueryParamsFilter(parameters); dumpQueryDetails(queryType, parameters); logger.finer("Frontend: Invoking async runQuery."); //$NON-NLS-1$ raiseQueryStartedEvent(queryType, callback.getContext()); - GenericApiGWTServiceAsync service = GenericApiGWTServiceAsync.Util.getInstance(); service.RunQuery(queryType, parameters, new AsyncCallback<VdcQueryReturnValue>() { @Override public void onFailure(Throwable caught) { - if (ignoreFailure(caught)) { - return; + try { + if (ignoreFailure(caught)) { + return; + } + logger.log(Level.SEVERE, "Failed to execute RunQuery: " + caught, caught); //$NON-NLS-1$ + getEventsHandler().runQueryFailed(null); + failureEventHandler(caught); + if (callback.isHandleFailure()) { + callback.asyncCallback.OnSuccess(callback.getModel(), null); + } + raiseQueryCompleteEvent(queryType, callback.getContext()); + } finally { + QueryWrapper wrapper = pendingRequests.get(callback.getQueryKey()); + if (wrapper != null) { + pendingRequests.remove(callback.getQueryKey()); + RunQuery(wrapper.getQueryType(), wrapper.getParameters(), wrapper.getCallback()); + } else { + currentRequests.remove(callback.getQueryKey()); + } } - logger.log(Level.SEVERE, "Failed to execute RunQuery: " + caught, caught); //$NON-NLS-1$ - getEventsHandler().runQueryFailed(null); - failureEventHandler(caught); - if (callback.isHandleFailure()) { - callback.asyncCallback.OnSuccess(callback.getModel(), null); - } - raiseQueryCompleteEvent(queryType, callback.getContext()); } @Override public void onSuccess(VdcQueryReturnValue result) { - logger.finer("Succesful returned result from RunQuery."); //$NON-NLS-1$ + try { + logger.finer("Succesful returned result from RunQuery."); //$NON-NLS-1$ - if (!result.getSucceeded()) { - logger.log(Level.WARNING, "Failure while invoking ReturnQuery [" + result.getExceptionString() //$NON-NLS-1$ - + "]"); //$NON-NLS-1$ - if (getEventsHandler() != null) { - ArrayList<VdcQueryReturnValue> failedResult = new ArrayList<VdcQueryReturnValue>(); - failedResult.add(result); - // getEventsHandler().runQueryFailed(failedResult); - String errorMessage = result.getExceptionString(); - handleNotLoggedInEvent(errorMessage); + if (!result.getSucceeded()) { + logger.log(Level.WARNING, "Failure while invoking ReturnQuery [" + result.getExceptionString() //$NON-NLS-1$ + + "]"); //$NON-NLS-1$ + if (getEventsHandler() != null) { + ArrayList<VdcQueryReturnValue> failedResult = new ArrayList<VdcQueryReturnValue>(); + failedResult.add(result); + // getEventsHandler().runQueryFailed(failedResult); + String errorMessage = result.getExceptionString(); + handleNotLoggedInEvent(errorMessage); + if (callback.isHandleFailure()) { + callback.getDel().OnSuccess(callback.getModel(), result); + } + } if (callback.isHandleFailure()) { callback.getDel().OnSuccess(callback.getModel(), result); } + } else { + callback.setOriginalReturnValue(result); + if (callback.getConverter() != null) { + callback.getDel().OnSuccess(callback.getModel(), + callback.getConverter().Convert(result.getReturnValue(), callback)); + } + else { + callback.getDel().OnSuccess(callback.getModel(), result); + } } - if (callback.isHandleFailure()) { - callback.getDel().OnSuccess(callback.getModel(), result); - } - } else { - callback.setOriginalReturnValue(result); - if (callback.getConverter() != null) { - callback.getDel().OnSuccess(callback.getModel(), - callback.getConverter().Convert(result.getReturnValue(), callback)); - } - else { - callback.getDel().OnSuccess(callback.getModel(), result); + + raiseQueryCompleteEvent(queryType, callback.getContext()); + } + finally { + QueryWrapper wrapper = pendingRequests.get(callback.getQueryKey()); + if (wrapper != null) { + pendingRequests.remove(callback.getQueryKey()); + RunQuery(wrapper.getQueryType(), wrapper.getParameters(), wrapper.getCallback()); + } else { + currentRequests.remove(callback.getQueryKey()); } } - - raiseQueryCompleteEvent(queryType, callback.getContext()); } }); } -- To view, visit http://gerrit.ovirt.org/9554 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I483a1c449f1eb2e9820464dbaf0a686f90923bb6 Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine Gerrit-Branch: master Gerrit-Owner: Gilad Chaplik <[email protected]> _______________________________________________ Engine-patches mailing list [email protected] http://lists.ovirt.org/mailman/listinfo/engine-patches
