use of io.confluent.ksql.schema.ksql.LogicalSchema in project ksql by confluentinc.
the class FinalProjectNode method throwOnEmptyValueOrUnknownColumns.
private void throwOnEmptyValueOrUnknownColumns() {
final LogicalSchema schema = getSchema();
if (schema.value().isEmpty()) {
throw new KsqlException("The projection contains no value columns.");
}
validateProjection();
}
use of io.confluent.ksql.schema.ksql.LogicalSchema in project ksql by confluentinc.
the class FinalProjectNode method build.
private Pair<LogicalSchema, List<SelectExpression>> build(final MetaStore metaStore, final KsqlConfig ksqlConfig) {
final LogicalSchema parentSchema = getSource().getSchema();
final Optional<LogicalSchema> targetSchema = getTargetSchema(metaStore);
final List<SelectExpression> selectExpressions = SelectionUtil.buildSelectExpressions(getSource(), projection.selectItems(), targetSchema);
final LogicalSchema schema = SelectionUtil.buildProjectionSchema(parentSchema, selectExpressions, metaStore);
if (into.isPresent()) {
// Persistent queries have key columns as value columns - final projection can exclude them:
final Map<ColumnName, Set<ColumnName>> seenKeyColumns = new HashMap<>();
selectExpressions.removeIf(se -> {
if (se.getExpression() instanceof UnqualifiedColumnReferenceExp) {
final ColumnName columnName = ((UnqualifiedColumnReferenceExp) se.getExpression()).getColumnName();
// Window bounds columns are currently removed if not aliased:
if (SystemColumns.isWindowBound(columnName) && se.getAlias().equals(columnName)) {
return true;
}
if (parentSchema.isKeyColumn(columnName)) {
seenKeyColumns.computeIfAbsent(columnName, k -> new HashSet<>()).add(se.getAlias());
return true;
}
}
return false;
});
for (final Entry<ColumnName, Set<ColumnName>> seenKey : seenKeyColumns.entrySet()) {
if (seenKey.getValue().size() > 1) {
final String keys = GrammaticalJoiner.and().join(seenKey.getValue().stream().map(Name::text).sorted());
throw new KsqlException("The projection contains a key column (" + seenKey.getKey() + ") more than once, aliased as: " + keys + "." + System.lineSeparator() + "Each key column must only be in the projection once. " + "If you intended to copy the key into the value, then consider using the " + AsValue.NAME + " function to indicate which key reference should be copied.");
}
}
}
final LogicalSchema nodeSchema;
if (into.isPresent()) {
nodeSchema = schema.withoutPseudoAndKeyColsInValue(ksqlConfig);
} else {
// Transient queries return key columns in the value, so the projection includes them, and
// the schema needs to include them too:
final Builder builder = LogicalSchema.builder();
builder.keyColumns(parentSchema.key());
schema.columns().forEach(builder::valueColumn);
nodeSchema = builder.build();
}
return Pair.of(nodeSchema, selectExpressions);
}
use of io.confluent.ksql.schema.ksql.LogicalSchema in project ksql by confluentinc.
the class LogicalPlanner method buildInternalRepartitionNode.
private PreJoinRepartitionNode buildInternalRepartitionNode(final PlanNode source, final String side, final Expression joinExpression, final BiFunction<Expression, Context<Void>, Optional<Expression>> plugin) {
final Expression rewrittenPartitionBy = ExpressionTreeRewriter.rewriteWith(plugin, joinExpression);
final LogicalSchema schema = buildRepartitionedSchema(source, Collections.singletonList(rewrittenPartitionBy));
return new PreJoinRepartitionNode(new PlanNodeId(side + "SourceKeyed"), source, schema, rewrittenPartitionBy);
}
use of io.confluent.ksql.schema.ksql.LogicalSchema in project ksql by confluentinc.
the class PushRouting method connectToHosts.
/**
* Connects to all of the hosts provided.
* @return A future for a PushConnectionsHandle, which can be used to terminate connections.
*/
@SuppressWarnings("checkstyle:ParameterNumber")
private CompletableFuture<PushConnectionsHandle> connectToHosts(final ServiceContext serviceContext, final PushPhysicalPlanManager pushPhysicalPlanManager, final ConfiguredStatement<Query> statement, final Collection<KsqlNode> hosts, final LogicalSchema outputSchema, final TransientQueryQueue transientQueryQueue, final PushConnectionsHandle pushConnectionsHandle, final boolean dynamicallyAddedNode, final Optional<ScalablePushQueryMetrics> scalablePushQueryMetrics, final Set<KsqlNode> catchupHosts, final PushRoutingOptions pushRoutingOptions, final String thisHostName) {
final Map<KsqlNode, CompletableFuture<RoutingResult>> futureMap = new LinkedHashMap<>();
for (final KsqlNode node : hosts) {
pushConnectionsHandle.add(node, new RoutingResult(RoutingResultStatus.IN_PROGRESS, () -> {
}));
final CompletableFuture<Void> callback = new CompletableFuture<>();
callback.handle((v, t) -> {
if (t == null) {
pushConnectionsHandle.get(node).ifPresent(result -> {
result.close();
result.updateStatus(RoutingResultStatus.COMPLETE);
});
LOG.info("Host {} completed request {}.", node, pushPhysicalPlanManager.getQueryId());
} else if (t instanceof GapFoundException) {
pushConnectionsHandle.get(node).ifPresent(result -> {
result.close();
result.updateStatus(RoutingResultStatus.OFFSET_GAP_FOUND);
});
} else {
pushConnectionsHandle.completeExceptionally(t);
}
return null;
});
futureMap.put(node, executeOrRouteQuery(node, statement, serviceContext, pushPhysicalPlanManager, outputSchema, transientQueryQueue, callback, scalablePushQueryMetrics, pushConnectionsHandle.getOffsetsTracker(), catchupHosts.contains(node), pushRoutingOptions, thisHostName));
}
return CompletableFuture.allOf(futureMap.values().toArray(new CompletableFuture[0])).thenApply(v -> {
for (final KsqlNode node : hosts) {
final CompletableFuture<RoutingResult> future = futureMap.get(node);
final RoutingResult routingResult = future.join();
pushConnectionsHandle.add(node, routingResult);
}
return pushConnectionsHandle;
}).exceptionally(t -> {
final KsqlNode node = futureMap.entrySet().stream().filter(e -> e.getValue().isCompletedExceptionally()).map(Entry::getKey).findFirst().orElse(null);
for (KsqlNode n : hosts) {
final CompletableFuture<RoutingResult> future = futureMap.get(n);
// Take whatever completed exceptionally and mark it as failed
if (future.isCompletedExceptionally()) {
pushConnectionsHandle.get(n).ifPresent(result -> result.updateStatus(RoutingResultStatus.FAILED));
} else {
final RoutingResult routingResult = future.join();
pushConnectionsHandle.add(node, routingResult);
}
}
LOG.warn("Error routing query {} id {} to host {} at timestamp {} with exception {}", statement.getStatementText(), pushPhysicalPlanManager.getQueryId(), node, System.currentTimeMillis(), t.getCause());
// retries in that case and don't fail the original request.
if (!dynamicallyAddedNode) {
pushConnectionsHandle.completeExceptionally(new KsqlException(String.format("Unable to execute push query \"%s\". %s", statement.getStatementText(), t.getCause().getMessage())));
}
return pushConnectionsHandle;
}).exceptionally(t -> {
LOG.error("Unexpected error handing exception", t);
return pushConnectionsHandle;
});
}
use of io.confluent.ksql.schema.ksql.LogicalSchema in project ksql by confluentinc.
the class PushRouting method checkForNewHosts.
private void checkForNewHosts(final ServiceContext serviceContext, final PushPhysicalPlanManager pushPhysicalPlanManager, final ConfiguredStatement<Query> statement, final LogicalSchema outputSchema, final TransientQueryQueue transientQueryQueue, final PushConnectionsHandle pushConnectionsHandle, final Optional<ScalablePushQueryMetrics> scalablePushQueryMetrics, final PushRoutingOptions pushRoutingOptions, final String thisHostName) {
VertxUtils.checkContext(pushPhysicalPlanManager.getContext());
if (pushConnectionsHandle.isClosed()) {
return;
}
final Set<KsqlNode> updatedHosts = registryToNodes.apply(pushPhysicalPlanManager.getScalablePushRegistry());
final Set<KsqlNode> hosts = pushConnectionsHandle.getActiveHosts();
final Set<KsqlNode> newHosts = Sets.difference(updatedHosts, hosts).stream().filter(node -> pushConnectionsHandle.get(node).map(routingResult -> routingResult.getStatus() != RoutingResultStatus.IN_PROGRESS).orElse(true)).collect(Collectors.toSet());
final Set<KsqlNode> removedHosts = Sets.difference(hosts, updatedHosts);
if (newHosts.size() > 0) {
LOG.info("Dynamically adding new hosts {} for {}", newHosts, pushPhysicalPlanManager.getQueryId());
final Set<KsqlNode> catchupHosts = newHosts.stream().filter(node -> pushConnectionsHandle.get(node).map(routingResult -> routingResult.getStatus() == RoutingResultStatus.OFFSET_GAP_FOUND).orElse(false)).collect(Collectors.toSet());
connectToHosts(serviceContext, pushPhysicalPlanManager, statement, newHosts, outputSchema, transientQueryQueue, pushConnectionsHandle, true, scalablePushQueryMetrics, catchupHosts, pushRoutingOptions, thisHostName);
}
if (removedHosts.size() > 0) {
LOG.info("Dynamically removing hosts {} for {}", removedHosts, pushPhysicalPlanManager.getQueryId());
for (final KsqlNode node : removedHosts) {
final RoutingResult result = pushConnectionsHandle.remove(node);
result.close();
result.updateStatus(RoutingResultStatus.REMOVED);
}
}
pushPhysicalPlanManager.getContext().owner().setTimer(clusterCheckInterval, timerId -> checkForNewHosts(serviceContext, pushPhysicalPlanManager, statement, outputSchema, transientQueryQueue, pushConnectionsHandle, scalablePushQueryMetrics, pushRoutingOptions, thisHostName));
}
Aggregations