queryDatasources;
diff --git a/services/src/main/java/org/apache/druid/server/AsyncQueryForwardingServlet.java b/services/src/main/java/org/apache/druid/server/AsyncQueryForwardingServlet.java
index 0a2da6293a9..6779c06f596 100644
--- a/services/src/main/java/org/apache/druid/server/AsyncQueryForwardingServlet.java
+++ b/services/src/main/java/org/apache/druid/server/AsyncQueryForwardingServlet.java
@@ -228,7 +228,7 @@ public class AsyncQueryForwardingServlet extends AsyncProxyServlet implements Qu
targetServer = hostFinder.findServerAvatica(connectionId);
byte[] requestBytes = objectMapper.writeValueAsBytes(requestMap);
request.setAttribute(AVATICA_QUERY_ATTRIBUTE, requestBytes);
- } else if (isNativeQueryEndpoint && HttpMethod.DELETE.is(method)) {
+ } else if (HttpMethod.DELETE.is(method)) {
// query cancellation request
targetServer = hostFinder.pickDefaultServer();
broadcastQueryCancelRequest(request, targetServer);
@@ -285,8 +285,6 @@ public class AsyncQueryForwardingServlet extends AsyncProxyServlet implements Qu
*/
private void broadcastQueryCancelRequest(HttpServletRequest request, Server targetServer)
{
- // send query cancellation to all brokers this query may have gone to
- // to keep the code simple, the proxy servlet will also send a request to the default targetServer.
for (final Server server : hostFinder.getAllServers()) {
if (server.getHost().equals(targetServer.getHost())) {
continue;
diff --git a/sql/src/main/java/org/apache/druid/sql/SqlLifecycle.java b/sql/src/main/java/org/apache/druid/sql/SqlLifecycle.java
index c30aba0fa23..98a7c22abb7 100644
--- a/sql/src/main/java/org/apache/druid/sql/SqlLifecycle.java
+++ b/sql/src/main/java/org/apache/druid/sql/SqlLifecycle.java
@@ -24,7 +24,6 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import com.google.errorprone.annotations.concurrent.GuardedBy;
import org.apache.calcite.avatica.remote.TypedValue;
-import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.sql.parser.SqlParseException;
import org.apache.calcite.tools.RelConversionException;
import org.apache.calcite.tools.ValidationException;
@@ -40,6 +39,7 @@ import org.apache.druid.java.util.emitter.service.ServiceMetricEvent;
import org.apache.druid.query.QueryContexts;
import org.apache.druid.query.QueryInterruptedException;
import org.apache.druid.query.QueryTimeoutException;
+import org.apache.druid.server.QueryScheduler;
import org.apache.druid.server.QueryStats;
import org.apache.druid.server.RequestLogLine;
import org.apache.druid.server.log.RequestLogger;
@@ -64,7 +64,9 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -77,11 +79,10 @@ import java.util.stream.Collectors;
* Validation and Authorization ({@link #validateAndAuthorize(HttpServletRequest)} or {@link #validateAndAuthorize(AuthenticationResult)})
* Planning ({@link #plan()})
* Execution ({@link #execute()})
- * Logging ({@link #emitLogsAndMetrics(Throwable, String, long)})
+ * Logging ({@link #finalizeStateAndEmitLogsAndMetrics(Throwable, String, long)})
*
*
- * Unlike QueryLifecycle, this class is designed to be thread safe so that it can be used in multi-threaded
- * scenario (JDBC) without external synchronization.
+ * Every method in this class must be called by the same thread except for {@link #cancel()}.
*/
public class SqlLifecycle
{
@@ -90,34 +91,33 @@ public class SqlLifecycle
private final PlannerFactory plannerFactory;
private final ServiceEmitter emitter;
private final RequestLogger requestLogger;
+ private final QueryScheduler queryScheduler;
private final long startMs;
private final long startNs;
- private final Object lock = new Object();
- @GuardedBy("lock")
+ /**
+ * This lock coordinates the access to {@link #state} as there is a happens-before relationship
+ * between {@link #cancel} and {@link #transition}.
+ */
+ private final Object stateLock = new Object();
+ @GuardedBy("stateLock")
private State state = State.NEW;
// init during intialize
- @GuardedBy("lock")
private String sql;
- @GuardedBy("lock")
private Map queryContext;
- @GuardedBy("lock")
private List parameters;
// init during plan
- @GuardedBy("lock")
private PlannerContext plannerContext;
- @GuardedBy("lock")
private ValidationResult validationResult;
- @GuardedBy("lock")
private PrepareResult prepareResult;
- @GuardedBy("lock")
private PlannerResult plannerResult;
public SqlLifecycle(
PlannerFactory plannerFactory,
ServiceEmitter emitter,
RequestLogger requestLogger,
+ QueryScheduler queryScheduler,
long startMs,
long startNs
)
@@ -125,6 +125,7 @@ public class SqlLifecycle
this.plannerFactory = plannerFactory;
this.emitter = emitter;
this.requestLogger = requestLogger;
+ this.queryScheduler = queryScheduler;
this.startMs = startMs;
this.startNs = startNs;
this.parameters = Collections.emptyList();
@@ -137,15 +138,12 @@ public class SqlLifecycle
*/
public String initialize(String sql, Map queryContext)
{
- synchronized (lock) {
- transition(State.NEW, State.INITIALIZED);
- this.sql = sql;
- this.queryContext = contextWithSqlId(queryContext);
- return sqlQueryId();
- }
+ transition(State.NEW, State.INITIALIZED);
+ this.sql = sql;
+ this.queryContext = contextWithSqlId(queryContext);
+ return sqlQueryId();
}
- @GuardedBy("lock")
private Map contextWithSqlId(Map queryContext)
{
Map newContext = new HashMap<>();
@@ -161,7 +159,6 @@ public class SqlLifecycle
return newContext;
}
- @GuardedBy("lock")
private String sqlQueryId()
{
return (String) this.queryContext.get(PlannerContext.CTX_SQL_QUERY_ID);
@@ -173,11 +170,9 @@ public class SqlLifecycle
*/
public void setParameters(List parameters)
{
- synchronized (lock) {
- this.parameters = parameters;
- if (this.plannerContext != null) {
- this.plannerContext.setParameters(parameters);
- }
+ this.parameters = parameters;
+ if (this.plannerContext != null) {
+ this.plannerContext.setParameters(parameters);
}
}
@@ -189,21 +184,21 @@ public class SqlLifecycle
*/
public void validateAndAuthorize(AuthenticationResult authenticationResult)
{
- synchronized (lock) {
+ synchronized (stateLock) {
if (state == State.AUTHORIZED) {
return;
}
- transition(State.INITIALIZED, State.AUTHORIZING);
- validate(authenticationResult);
- Access access = doAuthorize(
- AuthorizationUtils.authorizeAllResourceActions(
- authenticationResult,
- Iterables.transform(validationResult.getResources(), AuthorizationUtils.RESOURCE_READ_RA_GENERATOR),
- plannerFactory.getAuthorizerMapper()
- )
- );
- checkAccess(access);
}
+ transition(State.INITIALIZED, State.AUTHORIZING);
+ validate(authenticationResult);
+ Access access = doAuthorize(
+ AuthorizationUtils.authorizeAllResourceActions(
+ authenticationResult,
+ Iterables.transform(validationResult.getResources(), AuthorizationUtils.RESOURCE_READ_RA_GENERATOR),
+ plannerFactory.getAuthorizerMapper()
+ )
+ );
+ checkAccess(access);
}
/**
@@ -215,22 +210,19 @@ public class SqlLifecycle
*/
public void validateAndAuthorize(HttpServletRequest req)
{
- synchronized (lock) {
- transition(State.INITIALIZED, State.AUTHORIZING);
- AuthenticationResult authResult = AuthorizationUtils.authenticationResultFromRequest(req);
- validate(authResult);
- Access access = doAuthorize(
- AuthorizationUtils.authorizeAllResourceActions(
- req,
- Iterables.transform(validationResult.getResources(), AuthorizationUtils.RESOURCE_READ_RA_GENERATOR),
- plannerFactory.getAuthorizerMapper()
- )
- );
- checkAccess(access);
- }
+ transition(State.INITIALIZED, State.AUTHORIZING);
+ AuthenticationResult authResult = AuthorizationUtils.authenticationResultFromRequest(req);
+ validate(authResult);
+ Access access = doAuthorize(
+ AuthorizationUtils.authorizeAllResourceActions(
+ req,
+ Iterables.transform(validationResult.getResources(), AuthorizationUtils.RESOURCE_READ_RA_GENERATOR),
+ plannerFactory.getAuthorizerMapper()
+ )
+ );
+ checkAccess(access);
}
- @GuardedBy("lock")
private ValidationResult validate(AuthenticationResult authenticationResult)
{
try (DruidPlanner planner = plannerFactory.createPlanner(queryContext)) {
@@ -251,7 +243,6 @@ public class SqlLifecycle
}
}
- @GuardedBy("lock")
private Access doAuthorize(final Access authorizationResult)
{
if (!authorizationResult.isAllowed()) {
@@ -263,7 +254,6 @@ public class SqlLifecycle
return authorizationResult;
}
- @GuardedBy("lock")
private void checkAccess(Access access)
{
plannerContext.setAuthorizationResult(access);
@@ -280,22 +270,22 @@ public class SqlLifecycle
*/
public PrepareResult prepare() throws RelConversionException
{
- synchronized (lock) {
+ synchronized (stateLock) {
if (state != State.AUTHORIZED) {
throw new ISE("Cannot prepare because current state[%s] is not [%s].", state, State.AUTHORIZED);
}
- Preconditions.checkNotNull(plannerContext, "Cannot prepare, plannerContext is null");
- try (DruidPlanner planner = plannerFactory.createPlannerWithContext(plannerContext)) {
- this.prepareResult = planner.prepare(sql);
- return prepareResult;
- }
- // we can't collapse catch clauses since SqlPlanningException has type-sensitive constructors.
- catch (SqlParseException e) {
- throw new SqlPlanningException(e);
- }
- catch (ValidationException e) {
- throw new SqlPlanningException(e);
- }
+ }
+ Preconditions.checkNotNull(plannerContext, "Cannot prepare, plannerContext is null");
+ try (DruidPlanner planner = plannerFactory.createPlannerWithContext(plannerContext)) {
+ this.prepareResult = planner.prepare(sql);
+ return prepareResult;
+ }
+ // we can't collapse catch clauses since SqlPlanningException has type-sensitive constructors.
+ catch (SqlParseException e) {
+ throw new SqlPlanningException(e);
+ }
+ catch (ValidationException e) {
+ throw new SqlPlanningException(e);
}
}
@@ -304,23 +294,37 @@ public class SqlLifecycle
*
* If successful, the lifecycle will first transition from {@link State#AUTHORIZED} to {@link State#PLANNED}.
*/
- public PlannerContext plan() throws RelConversionException
+ public void plan() throws RelConversionException
{
- synchronized (lock) {
- transition(State.AUTHORIZED, State.PLANNED);
- Preconditions.checkNotNull(plannerContext, "Cannot plan, plannerContext is null");
- try (DruidPlanner planner = plannerFactory.createPlannerWithContext(plannerContext)) {
- this.plannerResult = planner.plan(sql);
- }
- // we can't collapse catch clauses since SqlPlanningException has type-sensitive constructors.
- catch (SqlParseException e) {
- throw new SqlPlanningException(e);
- }
- catch (ValidationException e) {
- throw new SqlPlanningException(e);
- }
- return plannerContext;
+ transition(State.AUTHORIZED, State.PLANNED);
+ Preconditions.checkNotNull(plannerContext, "Cannot plan, plannerContext is null");
+ try (DruidPlanner planner = plannerFactory.createPlannerWithContext(plannerContext)) {
+ this.plannerResult = planner.plan(sql);
}
+ // we can't collapse catch clauses since SqlPlanningException has type-sensitive constructors.
+ catch (SqlParseException e) {
+ throw new SqlPlanningException(e);
+ }
+ catch (ValidationException e) {
+ throw new SqlPlanningException(e);
+ }
+ }
+
+ /**
+ * This method must be called after {@link #plan()}.
+ */
+ public SqlRowTransformer createRowTransformer()
+ {
+ assert plannerContext != null;
+ assert plannerResult != null;
+
+ return new SqlRowTransformer(plannerContext.getTimeZone(), plannerResult.rowType());
+ }
+
+ @VisibleForTesting
+ PlannerContext getPlannerContext()
+ {
+ return plannerContext;
}
/**
@@ -330,10 +334,8 @@ public class SqlLifecycle
*/
public Sequence