blob: 1b18e943909945fa7e2ade1fbc4162885de246db [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.druid.query;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import org.apache.druid.guice.annotations.PublicApi;
import org.apache.druid.java.util.common.guava.Sequence;
import org.apache.druid.query.context.ResponseContext;
import javax.annotation.Nullable;
/**
* An immutable composite object of {@link Query} + extra stuff needed in {@link QueryRunner}s.
*/
@PublicApi
public final class QueryPlus<T>
{
/**
* Returns the minimum bare QueryPlus object with the given query. {@link #getQueryMetrics()} of the QueryPlus object,
* returned from this factory method, returns {@code null}.
*/
public static <T> QueryPlus<T> wrap(Query<T> query)
{
Preconditions.checkNotNull(query);
return new QueryPlus<>(query, null, null);
}
private final Query<T> query;
private final QueryMetrics<?> queryMetrics;
private final String identity;
private QueryPlus(Query<T> query, QueryMetrics<?> queryMetrics, String identity)
{
this.query = query;
this.queryMetrics = queryMetrics;
this.identity = identity;
}
public Query<T> getQuery()
{
return query;
}
@Nullable
public QueryMetrics<?> getQueryMetrics()
{
return queryMetrics;
}
/**
* Returns the same QueryPlus object with the identity replaced. This new identity will affect future calls to
* {@link #withoutQueryMetrics()} but will not affect any currently-existing queryMetrics.
*/
public QueryPlus<T> withIdentity(String identity)
{
return new QueryPlus<>(query, queryMetrics, identity);
}
/**
* Returns the same QueryPlus object, if it already has {@link QueryMetrics} ({@link #getQueryMetrics()} returns not
* null), or returns a new QueryPlus object with {@link Query} from this QueryPlus and QueryMetrics created using the
* given {@link QueryToolChest}, via {@link QueryToolChest#makeMetrics(Query)} method.
*
* By convention, callers of {@code withQueryMetrics()} must also call .getQueryMetrics().emit() on the returned
* QueryMetrics object, regardless if this object is the same as the object on which .withQueryMetrics() was initially
* called (i. e. it already had non-null QueryMetrics), or if it is a new QueryPlus object. See {@link
* MetricsEmittingQueryRunner} for example.
*/
public QueryPlus<T> withQueryMetrics(QueryToolChest<T, ? extends Query<T>> queryToolChest)
{
if (queryMetrics != null) {
return this;
} else {
final QueryMetrics metrics = ((QueryToolChest) queryToolChest).makeMetrics(query);
if (identity != null) {
metrics.identity(identity);
}
return new QueryPlus<>(query, metrics, identity);
}
}
/**
* Returns a QueryPlus object without the components which are unsafe for concurrent use from multiple threads,
* therefore couldn't be passed down in concurrent or async {@link QueryRunner}s.
*
* Currently the only unsafe component is {@link QueryMetrics}, i. e. {@code withoutThreadUnsafeState()} call is
* equivalent to {@link #withoutQueryMetrics()}.
*/
public QueryPlus<T> withoutThreadUnsafeState()
{
return withoutQueryMetrics();
}
/**
* Returns the same QueryPlus object, if it doesn't have {@link QueryMetrics} ({@link #getQueryMetrics()} returns
* null), or returns a new QueryPlus object with {@link Query} from this QueryPlus and null as QueryMetrics.
*/
private QueryPlus<T> withoutQueryMetrics()
{
if (queryMetrics == null) {
return this;
} else {
return new QueryPlus<>(query, null, identity);
}
}
/**
* Equivalent of withQuery(getQuery().withOverriddenContext(ImmutableMap.of(MAX_QUEUED_BYTES_KEY, maxQueuedBytes))).
*/
public QueryPlus<T> withMaxQueuedBytes(long maxQueuedBytes)
{
return new QueryPlus<>(
query.withOverriddenContext(ImmutableMap.of(QueryContexts.MAX_QUEUED_BYTES_KEY, maxQueuedBytes)),
queryMetrics,
identity
);
}
/**
* Returns a QueryPlus object with {@link QueryMetrics} from this QueryPlus object, and the provided {@link Query}.
*/
public <U> QueryPlus<U> withQuery(Query<U> replacementQuery)
{
return new QueryPlus<>(replacementQuery, queryMetrics, identity);
}
public Sequence<T> run(QuerySegmentWalker walker, ResponseContext context)
{
return query.getRunner(walker).run(this, context);
}
public QueryPlus<T> optimizeForSegment(PerSegmentQueryOptimizationContext optimizationContext)
{
return new QueryPlus<>(query.optimizeForSegment(optimizationContext), queryMetrics, identity);
}
}