mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-20 06:00:44 +00:00
Add internal timeouts to RDBMS query, count, and aggregate, with timeoutSeconds field on their inputs; also add cancel method on those 3 actions, implemented down in RDBMS as well (e.g., to cancel inresponse to http request being abandoned)
This commit is contained in:
@ -67,4 +67,15 @@ public interface BaseQueryInterface
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
default void cancelAction()
|
||||
{
|
||||
//////////////////////////////////////////////
|
||||
// initially at least, a noop in base class //
|
||||
//////////////////////////////////////////////
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.AggregateInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.helpers.QueryStatManager;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
@ -41,6 +42,12 @@ import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
*******************************************************************************/
|
||||
public class AggregateAction
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(AggregateAction.class);
|
||||
|
||||
private AggregateInterface aggregateInterface;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -56,7 +63,7 @@ public class AggregateAction
|
||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(aggregateInput.getBackend());
|
||||
|
||||
AggregateInterface aggregateInterface = qModule.getAggregateInterface();
|
||||
aggregateInterface = qModule.getAggregateInterface();
|
||||
aggregateInterface.setQueryStat(queryStat);
|
||||
AggregateOutput aggregateOutput = aggregateInterface.execute(aggregateInput);
|
||||
|
||||
@ -64,4 +71,20 @@ public class AggregateAction
|
||||
|
||||
return aggregateOutput;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void cancel()
|
||||
{
|
||||
if(aggregateInterface == null)
|
||||
{
|
||||
LOG.warn("aggregateInterface object was null when requested to cancel");
|
||||
return;
|
||||
}
|
||||
|
||||
aggregateInterface.cancelAction();
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.CountInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.helpers.QueryStatManager;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
@ -41,6 +42,12 @@ import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
*******************************************************************************/
|
||||
public class CountAction
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(CountAction.class);
|
||||
|
||||
private CountInterface countInterface;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -56,7 +63,7 @@ public class CountAction
|
||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(countInput.getBackend());
|
||||
|
||||
CountInterface countInterface = qModule.getCountInterface();
|
||||
countInterface = qModule.getCountInterface();
|
||||
countInterface.setQueryStat(queryStat);
|
||||
CountOutput countOutput = countInterface.execute(countInput);
|
||||
|
||||
@ -64,4 +71,20 @@ public class CountAction
|
||||
|
||||
return countOutput;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void cancel()
|
||||
{
|
||||
if(countInterface == null)
|
||||
{
|
||||
LOG.warn("countInterface object was null when requested to cancel");
|
||||
return;
|
||||
}
|
||||
|
||||
countInterface.cancelAction();
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ public class QueryAction
|
||||
private Optional<AbstractPostQueryCustomizer> postQueryRecordCustomizer;
|
||||
|
||||
private QueryInput queryInput;
|
||||
private QueryInterface queryInterface;
|
||||
private QPossibleValueTranslator qPossibleValueTranslator;
|
||||
|
||||
|
||||
@ -121,7 +122,7 @@ public class QueryAction
|
||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(backend);
|
||||
|
||||
QueryInterface queryInterface = qModule.getQueryInterface();
|
||||
queryInterface = qModule.getQueryInterface();
|
||||
queryInterface.setQueryStat(queryStat);
|
||||
QueryOutput queryOutput = queryInterface.execute(queryInput);
|
||||
|
||||
@ -339,4 +340,20 @@ public class QueryAction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void cancel()
|
||||
{
|
||||
if(queryInterface == null)
|
||||
{
|
||||
LOG.warn("queryInterface object was null when requested to cancel");
|
||||
return;
|
||||
}
|
||||
|
||||
queryInterface.cancelAction();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.tables.helpers;
|
||||
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** For actions that may want to set a timeout, and cancel themselves if they run
|
||||
** too long - this class helps.
|
||||
**
|
||||
** Construct with the timeout (delay & timeUnit), and a runnable that takes care
|
||||
** of doing the cancel (e.g., cancelling a JDBC statement).
|
||||
**
|
||||
** Call start() to make a future get scheduled (note, if delay was null or <= 0,
|
||||
** then it doesn't get scheduled at all).
|
||||
**
|
||||
** Call cancel() if the action got far enough/completed, to cancel the future.
|
||||
**
|
||||
** You can check didTimeout (getDidTimeout()) to know if the timeout did occur.
|
||||
*******************************************************************************/
|
||||
public class ActionTimeoutHelper
|
||||
{
|
||||
private final Integer delay;
|
||||
private final TimeUnit timeUnit;
|
||||
private final Runnable runnable;
|
||||
private ScheduledFuture<?> future;
|
||||
|
||||
private boolean didTimeout = false;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ActionTimeoutHelper(Integer delay, TimeUnit timeUnit, Runnable runnable)
|
||||
{
|
||||
this.delay = delay;
|
||||
this.timeUnit = timeUnit;
|
||||
this.runnable = runnable;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void start()
|
||||
{
|
||||
if(delay == null || delay <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
future = Executors.newSingleThreadScheduledExecutor().schedule(() ->
|
||||
{
|
||||
didTimeout = true;
|
||||
runnable.run();
|
||||
}, delay, timeUnit);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void cancel()
|
||||
{
|
||||
if(future != null)
|
||||
{
|
||||
future.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for didTimeout
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getDidTimeout()
|
||||
{
|
||||
return didTimeout;
|
||||
}
|
||||
|
||||
}
|
@ -40,6 +40,8 @@ public class AggregateInput extends AbstractTableActionInput
|
||||
private List<GroupBy> groupBys = new ArrayList<>();
|
||||
private Integer limit;
|
||||
|
||||
private Integer timeoutSeconds;
|
||||
|
||||
private List<QueryJoin> queryJoins = null;
|
||||
|
||||
|
||||
@ -269,4 +271,35 @@ public class AggregateInput extends AbstractTableActionInput
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public Integer getTimeoutSeconds()
|
||||
{
|
||||
return (this.timeoutSeconds);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public void setTimeoutSeconds(Integer timeoutSeconds)
|
||||
{
|
||||
this.timeoutSeconds = timeoutSeconds;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public AggregateInput withTimeoutSeconds(Integer timeoutSeconds)
|
||||
{
|
||||
this.timeoutSeconds = timeoutSeconds;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ public class CountInput extends AbstractTableActionInput
|
||||
{
|
||||
private QQueryFilter filter;
|
||||
|
||||
private Integer timeoutSeconds;
|
||||
|
||||
private List<QueryJoin> queryJoins = null;
|
||||
private Boolean includeDistinctCount = false;
|
||||
|
||||
@ -174,4 +176,35 @@ public class CountInput extends AbstractTableActionInput
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public Integer getTimeoutSeconds()
|
||||
{
|
||||
return (this.timeoutSeconds);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public void setTimeoutSeconds(Integer timeoutSeconds)
|
||||
{
|
||||
this.timeoutSeconds = timeoutSeconds;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public CountInput withTimeoutSeconds(Integer timeoutSeconds)
|
||||
{
|
||||
this.timeoutSeconds = timeoutSeconds;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ public class QueryInput extends AbstractTableActionInput implements QueryOrGetIn
|
||||
private QQueryFilter filter;
|
||||
|
||||
private RecordPipe recordPipe;
|
||||
private Integer timeoutSeconds;
|
||||
|
||||
private boolean shouldTranslatePossibleValues = false;
|
||||
private boolean shouldGenerateDisplayValues = false;
|
||||
@ -537,4 +538,35 @@ public class QueryInput extends AbstractTableActionInput implements QueryOrGetIn
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public Integer getTimeoutSeconds()
|
||||
{
|
||||
return (this.timeoutSeconds);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public void setTimeoutSeconds(Integer timeoutSeconds)
|
||||
{
|
||||
this.timeoutSeconds = timeoutSeconds;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public QueryInput withTimeoutSeconds(Integer timeoutSeconds)
|
||||
{
|
||||
this.timeoutSeconds = timeoutSeconds;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user