Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2021 Google LLC
*
* Licensed 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
*
* https://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 com.google.appengine.api.datastore;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.appengine.api.datastore.Query.FilterPredicate;
import com.google.appengine.api.datastore.Query.SortPredicate;
import com.google.apphosting.datastore.DatastoreV3Pb.Query.Order;
import com.google.auto.value.AutoValue;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Set;
import javax.annotation.Nullable;
/**
* A {@link PreparedQuery} implementation for use with {@link MultiQueryBuilder}.
*
*
We run each successively generated list of filters returned by each {@link MultiQueryBuilder}
* as they are needed and concatenate the result.
*
*
If a list of filters contains more than one entry or there are multiple {@link
* MultiQueryBuilder}s we build a {@link Comparator} based on the sort predicates of the base query.
* We then use this {@link Comparator} to produce an appropriately ordered sequence of results that
* contains the results from each sub-query. As each sub-query produces results that are already
* sorted we simply use a {@link PriorityQueue} to merge the results from the sub-query as new
* results are requested.
*
*/
class PreparedMultiQuery extends BasePreparedQuery {
// MAX_BUFFERED_QUERIES is the target number of simultaneous queries used by a PreparedMultiQuery.
// We attempt to execute additional queries in advance to speed SERIAL queries.
//
// However, we may exceed MAX_BUFFERED_QUERIES if necessary to satisfy PARALLEL queries that
// are mergesorted in the client and thus need to be executed simultaneously.
// 10 is an estimate to avoid the limit of 50 outstanding RPCs per process.
// @VisibleForTesting
static final int MAX_BUFFERED_QUERIES = 10;
private final Query baseQuery;
private final List queryBuilders;
private final EntityComparator entityComparator;
private final Transaction txn;
private final QueryRunner queryRunner;
private final Set projected;
// The number of query iterators each builder can keep running simultaneously (at least 1).
private final int[] maxBufferedIteratorsPerBuilder;
/**
* @param baseQuery the base query on which to apply generate filters filters
* @param queryBuilders the source of filters to use
* @param txn the txn in which all queries should execute, can be {@code null}
* @throws IllegalArgumentException if this multi-query required in memory sorting and the base
* query is both a keys-only query and sorted by anything other than its key.
*/
@SuppressWarnings("deprecation")
PreparedMultiQuery(
Query baseQuery,
List queryBuilders,
Transaction txn,
QueryRunner queryRunner) {
checkArgument(!queryBuilders.isEmpty());
checkArgument(baseQuery.getFilter() == null);
checkArgument(baseQuery.getFilterPredicates().isEmpty());
this.txn = txn;
this.baseQuery = baseQuery;
this.queryBuilders = queryBuilders;
this.queryRunner = queryRunner;
if (baseQuery.getProjections().isEmpty()) {
projected = Collections.emptySet();
} else {
projected = Sets.newHashSet();
for (Projection proj : baseQuery.getProjections()) {
projected.add(proj.getPropertyName());
}
if (!baseQuery.getSortPredicates().isEmpty()) {
// We need to make sure all of the sort orders are projected to compare entities. We
// Also need these values for deduping (we need all index values).
Set localProjected = Sets.newHashSet(projected);
for (SortPredicate sort : baseQuery.getSortPredicates()) {
if (localProjected.add(sort.getPropertyName())) {
baseQuery.addProjection(new PropertyProjection(sort.getPropertyName(), null));
}
}
}
}
if (queryBuilders.size() > 1 || queryBuilders.get(0).getParallelQuerySize() > 1) {
if (baseQuery.isKeysOnly()) {
for (SortPredicate sp : baseQuery.getSortPredicates()) {
if (!sp.getPropertyName().equals(Entity.KEY_RESERVED_PROPERTY)) {
throw new IllegalArgumentException(
"The provided keys-only multi-query needs to perform some "
+ "sorting in memory. As a result, this query can only be "
+ "sorted by the key property as this is the only property "
+ "that is available in memory.");
}
}
}
List sortPredicates = baseQuery.getSortPredicates();
List orders = new ArrayList<>(sortPredicates.size());
for (SortPredicate sp : sortPredicates) {
orders.add(QueryTranslator.convertSortPredicateToPb(sp));
}
this.entityComparator = new EntityComparator(orders);
} else {
this.entityComparator = null; // should never be used if everything is run in serial
}
// Calculate the number of iterators each builder can start simultaneously. We assign
// at least one per builder to prevent starvation.
maxBufferedIteratorsPerBuilder = new int[queryBuilders.size()];
int allocatableQueries = MAX_BUFFERED_QUERIES;
// Allocate one iterator per builder first.
for (int i = 0; i < queryBuilders.size(); i++) {
++maxBufferedIteratorsPerBuilder[i];
allocatableQueries -= queryBuilders.get(i).getParallelQuerySize();
}
// Now allocate available extra queries in round-robin order.
boolean madeEmptyPass = false;
while (allocatableQueries > 0 && !madeEmptyPass) {
madeEmptyPass = true;
for (int i = 0; i < queryBuilders.size(); i++) {
if (queryBuilders.get(i).getParallelQuerySize() <= allocatableQueries) {
++maxBufferedIteratorsPerBuilder[i];
allocatableQueries -= queryBuilders.get(i).getParallelQuerySize();
madeEmptyPass = false;
}
}
}
}
@SuppressWarnings("deprecation")
protected PreparedQuery prepareQuery(List filters, boolean isCountQuery) {
Query query = new Query(baseQuery);
if (isCountQuery && query.getProjections().isEmpty()) {
// Use keys only for normal count queries.
query.setKeysOnly();
}
query.getFilterPredicates().addAll(filters);
return new PreparedQueryImpl(query, txn, queryRunner);
}
protected Object getDedupeValue(Entity entity) {
if (projected.isEmpty()) {
// Keys are deduped.
return KeyAndProperties.create(entity.getKey(), null);
} else {
// The index values + keys are deduped.
// NOTE: Entity.equals() only takes into account the key
return KeyAndProperties.create(entity.getKey(), entity.getProperties());
}
}
@AutoValue
abstract static class KeyAndProperties {
static KeyAndProperties create(Key key, @Nullable Map properties) {
return new AutoValue_PreparedMultiQuery_KeyAndProperties(key, properties);
}
abstract Key key();
@Nullable
abstract Map properties();
}
/**
* A helper function to prepare batches of queries.
*
* @param filtersList list of the filters for each query to prepare
* @return a list of prepared queries
*/
protected List prepareQueries(List> filtersList) {
List preparedQueries = new ArrayList(filtersList.size());
for (List filters : filtersList) {
preparedQueries.add(prepareQuery(filters, false));
}
return preparedQueries;
}
/**
* An iterator that will correctly process the values returned by a multiquery iterator.
*
*
This iterator in some cases may not respect the provided FetchOptions.limit().
*/
// TODO: consider making this iterator respect limit to
// avoid the need to wrap this iterator in a {@link SlicingIterator}
// as this class keeps track of the number of results returned.
private class FilteredMultiQueryIterator extends AbstractIterator {
private final Iterator>> multiQueryIterator;
private final FetchOptions fetchOptions;
private final Set