// TransactionCacheEntry
// or any histograms because they will have been updated when
// the QueryCacheEntry is created
for (String table_key : query_entry.getTableKeys()) {
if (!temp_stmtPartitions.containsKey(table_key)) {
temp_stmtPartitions.put(table_key, new PartitionSet());
}
temp_stmtPartitions.get(table_key).addAll(query_entry.getPartitions(table_key));
} // FOR
txn_entry.examined_queries += query_weight;
query_partitions += query_entry.getAllPartitions().size();
// Create a new QueryCacheEntry
} else {
if (trace.val)
LOG.trace(String.format("Calculating new cost information for %s - Weight:%d", query_trace, query_weight));
if (this.use_caching == false || query_entry == null) {
query_entry = new QueryCacheEntry(txn_trace, query_trace, query_weight);
}
this.query_ctr.addAndGet(query_weight);
query_entry.invalid = false;
// QUERY XREF
if (this.use_caching) {
txn_entry.query_entries[query_idx] = query_entry;
String stmt_key = CatalogKey.createKey(catalog_stmt);
Set<QueryCacheEntry> cache = this.cache_stmtXref.get(stmt_key);
if (cache == null) {
cache = new HashSet<QueryCacheEntry>();
this.cache_stmtXref.put(stmt_key, cache);
}
cache.add(query_entry);
}
// Give the QueryTrace to the PartitionEstimator to get back a
// mapping from TableKeys
// to sets of partitions that were touched by the query.
// XXX: What should we do if the TransactionCacheEntry's base
// partition hasn't been calculated yet?
// Let's just throw it at the PartitionEstimator and let it figure out what to do...
Map<String, PartitionSet> table_partitions = this.p_estimator.getTablePartitions(query_trace, txn_entry.base_partition);
StringBuilder sb = null;
if (trace.val) {
sb = new StringBuilder();
sb.append("\n" + StringUtil.SINGLE_LINE + query_trace + " Table Partitions:");
}
assert (!table_partitions.isEmpty()) : "Failed to get back table partitions for " + query_trace;
for (Entry<String, PartitionSet> e : table_partitions.entrySet()) {
assert (e.getValue() != null) : "Null table partitions for '" + e.getKey() + "'";
// If we didn't get anything back, then that means that we
// know we need to touch this
// table but the PartitionEstimator doesn't have enough
// information yet
if (e.getValue().isEmpty())
continue;
if (trace.val)
sb.append("\n " + e.getKey() + ": " + e.getValue());
// Update the cache xref mapping so that we know this Table
// is referenced by this QueryTrace
// This will allow us to quickly find the QueryCacheEntry in
// invalidate()
if (this.use_caching) {
Set<QueryCacheEntry> cache = this.cache_tableXref.get(e.getKey());
if (cache == null) {
cache = new HashSet<QueryCacheEntry>();
this.cache_tableXref.put(e.getKey(), cache);
}
cache.add(query_entry);
}
// Ok, so now update the variables in our QueryCacheEntry
query_entry.singlesited = (query_entry.singlesited && e.getValue().size() == 1);
query_entry.addAllPartitions(e.getKey(), e.getValue());
// And then update the Statement partitions map to include
// all of the partitions
// that this query touched
if (!temp_stmtPartitions.containsKey(e.getKey())) {
temp_stmtPartitions.put(e.getKey(), e.getValue());
} else {
temp_stmtPartitions.get(e.getKey()).addAll(e.getValue());
}
} // FOR (Entry<TableKey, Set<Partitions>>
if (trace.val)
LOG.trace(sb.toString() + "\n" + query_trace.debug(catalogContext.database) + "\n" + StringUtil.SINGLE_LINE.trim());
// Lastly, update the various histogram that keep track of which
// partitions are accessed:
// (1) The global histogram for the cost model of partitions touched by all queries
// (2) The TransactionCacheEntry histogram of which partitions are touched by all queries
//
// Note that we do not want to update the global histogram for the cost model on the
// partitions touched by txns, because we don't want to count the same partition multiple times
// Note also that we want to do this *outside* of the loop above, otherwise we will count
// the same partition multiple times if the query references more than one table!
this.histogram_query_partitions.put(query_entry.getAllPartitions(), query_weight * txn_weight);
txn_entry.touched_partitions.put(query_entry.getAllPartitions(), query_weight);
int query_num_partitions = query_entry.getAllPartitions().size();
query_partitions += query_num_partitions;
// If the number of partitions is zero, then we will classify
// this query as currently
// being unknown. This can happen if the query needs does a
// single look-up on a replicated
// table but we don't know the base partition yet
if (query_num_partitions == 0) {
if (trace.val)
LOG.trace("# of Partitions for " + query_trace + " is zero. Marking as unknown for now");
txn_entry.unknown_queries += query_weight;
query_entry.unknown = true;
query_entry.invalid = true;
} else {
txn_entry.examined_queries += query_weight;
query_entry.unknown = false;
}
} // if (new cache entry)
// If we're not single-sited, well then that ruins it for everyone
// else now doesn't it??
if (query_entry.getAllPartitions().size() > 1) {
if (debug.val)
LOG.debug(query_trace + " is being marked as multi-partition: " + query_entry.getAllPartitions());
query_entry.singlesited = false;
txn_entry.singlesited = false;
txn_entry.multisite_queries += query_weight;
} else {
if (debug.val)
LOG.debug(query_trace + " is marked as single-partition");
query_entry.singlesited = true;
txn_entry.singlesite_queries += query_weight;
}
if (debug_txn)
LOG.info(query_entry);
} // FOR (QueryTrace)
// Now just check whether this sucker has queries that touch more than one partition
// We do this one first because it's the fastest and will pick up enough of them
if (txn_entry.touched_partitions.getValueCount() > 1) {
if (trace.val)
LOG.trace(txn_trace + " touches " + txn_entry.touched_partitions.getValueCount() + " different partitions");
txn_entry.singlesited = false;
// Otherwise, now that we have processed all of queries that we
// could, we need to check whether the values of the StmtParameters used on the partitioning
// column of each table all hash to the same value. If they don't, then we know we can't
// be single-partition
} else {
for (Entry<String, PartitionSet> e : temp_stmtPartitions.entrySet()) {
String table_key = e.getKey();
Table catalog_tbl = CatalogKey.getFromKey(catalogContext.database, table_key, Table.class);
if (catalog_tbl.getIsreplicated()) {
continue;
}
Column table_partition_col = catalog_tbl.getPartitioncolumn();
PartitionSet partitions = e.getValue();
// If there is more than one partition, then we'll never be multi-partition
// so we can stop our search right here.
if (partitions.size() > 1) {
if (trace.val)
LOG.trace(String.format("%s references %s's partitioning attribute %s on %d different partitions -- VALUES %s", catalog_proc.getName(), catalog_tbl.getName(),
table_partition_col.fullName(), partitions.size(), partitions));
txn_entry.singlesited = false;
break;
// Make sure that the partitioning ProcParameter hashes to the same
// site as the value used on the partitioning column for this table
} else if (!partitions.isEmpty() && txn_entry.base_partition != HStoreConstants.NULL_PARTITION_ID) {
int tbl_partition = CollectionUtil.first(partitions);
if (txn_entry.base_partition != tbl_partition) {
if (trace.val)
LOG.trace(txn_trace + " executes on Partition #" + txn_entry.base_partition + " " + "but partitioning column " + CatalogUtil.getDisplayName(table_partition_col) + " "
+ "references Partition #" + tbl_partition);