use of io.confluent.ksql.query.PullQueryQueue in project ksql by confluentinc.
the class HARouting method executeRounds.
private void executeRounds(final ServiceContext serviceContext, final PullPhysicalPlan pullPhysicalPlan, final ConfiguredStatement<Query> statement, final RoutingOptions routingOptions, final LogicalSchema outputSchema, final QueryId queryId, final List<KsqlPartitionLocation> locations, final PullQueryQueue pullQueryQueue, final CompletableFuture<Void> shouldCancelRequests, final Optional<ConsistencyOffsetVector> consistencyOffsetVector) throws InterruptedException {
final ExecutorCompletionService<PartitionFetchResult> completionService = new ExecutorCompletionService<>(routerExecutorService);
final int totalPartitions = locations.size();
int processedPartitions = 0;
final Map<Integer, List<Exception>> exceptionsPerPartition = new HashMap<>();
for (final KsqlPartitionLocation partition : locations) {
final KsqlNode node = getNodeForRound(partition, routingOptions);
pullQueryMetrics.ifPresent(queryExecutorMetrics -> queryExecutorMetrics.recordPartitionFetchRequest(1));
completionService.submit(() -> routeQuery.routeQuery(node, partition, statement, serviceContext, routingOptions, pullQueryMetrics, pullPhysicalPlan, outputSchema, queryId, pullQueryQueue, shouldCancelRequests, consistencyOffsetVector));
}
while (processedPartitions < totalPartitions) {
final Future<PartitionFetchResult> future = completionService.take();
try {
final PartitionFetchResult fetchResult = future.get();
if (fetchResult.isError()) {
exceptionsPerPartition.computeIfAbsent(fetchResult.location.getPartition(), v -> new ArrayList<>()).add(fetchResult.exception.get());
final KsqlPartitionLocation nextRoundPartition = nextNode(fetchResult.getLocation());
final KsqlNode node = getNodeForRound(nextRoundPartition, routingOptions);
pullQueryMetrics.ifPresent(queryExecutorMetrics -> queryExecutorMetrics.recordResubmissionRequest(1));
completionService.submit(() -> routeQuery.routeQuery(node, nextRoundPartition, statement, serviceContext, routingOptions, pullQueryMetrics, pullPhysicalPlan, outputSchema, queryId, pullQueryQueue, shouldCancelRequests, consistencyOffsetVector));
} else {
Preconditions.checkState(fetchResult.getResult() == RoutingResult.SUCCESS);
processedPartitions++;
}
} catch (final Exception e) {
final MaterializationException exception = new MaterializationException("Unable to execute pull query: " + e.getMessage());
for (Entry<Integer, List<Exception>> entry : exceptionsPerPartition.entrySet()) {
for (Exception excp : entry.getValue()) {
exception.addSuppressed(excp);
}
}
throw exception;
}
}
pullQueryQueue.close();
}
use of io.confluent.ksql.query.PullQueryQueue in project ksql by confluentinc.
the class HARouting method executeOrRouteQuery.
@SuppressWarnings("ParameterNumber")
@VisibleForTesting
static PartitionFetchResult executeOrRouteQuery(final KsqlNode node, final KsqlPartitionLocation location, final ConfiguredStatement<Query> statement, final ServiceContext serviceContext, final RoutingOptions routingOptions, final Optional<PullQueryExecutorMetrics> pullQueryMetrics, final PullPhysicalPlan pullPhysicalPlan, final LogicalSchema outputSchema, final QueryId queryId, final PullQueryQueue pullQueryQueue, final CompletableFuture<Void> shouldCancelRequests, final Optional<ConsistencyOffsetVector> consistencyOffsetVector) {
final BiFunction<List<?>, LogicalSchema, PullQueryRow> rowFactory = (rawRow, schema) -> new PullQueryRow(rawRow, schema, Optional.ofNullable(routingOptions.getIsDebugRequest() ? node : null), Optional.empty());
if (node.isLocal()) {
try {
LOG.debug("Query {} executed locally at host {} at timestamp {}.", statement.getStatementText(), node.location(), System.currentTimeMillis());
pullQueryMetrics.ifPresent(queryExecutorMetrics -> queryExecutorMetrics.recordLocalRequests(1));
synchronized (pullPhysicalPlan) {
pullPhysicalPlan.execute(ImmutableList.of(location), pullQueryQueue, rowFactory);
return new PartitionFetchResult(RoutingResult.SUCCESS, location, Optional.empty());
}
} catch (StandbyFallbackException | NotUpToBoundException e) {
LOG.warn("Error executing query locally at node {}. Falling back to standby state which " + "may return stale results. Cause {}", node, e.getMessage());
return new PartitionFetchResult(RoutingResult.STANDBY_FALLBACK, location, Optional.of(e));
} catch (Exception e) {
throw new KsqlException(String.format("Error executing query locally at node %s: %s", node.location(), e.getMessage()), e);
}
} else {
try {
LOG.debug("Query {} routed to host {} at timestamp {}.", statement.getStatementText(), node.location(), System.currentTimeMillis());
pullQueryMetrics.ifPresent(queryExecutorMetrics -> queryExecutorMetrics.recordRemoteRequests(1));
forwardTo(node, ImmutableList.of(location), statement, serviceContext, pullQueryQueue, rowFactory, outputSchema, shouldCancelRequests, consistencyOffsetVector);
return new PartitionFetchResult(RoutingResult.SUCCESS, location, Optional.empty());
} catch (StandbyFallbackException e) {
LOG.warn("Error forwarding query to node {}. Falling back to standby state which may " + "return stale results", node.location(), e.getCause());
return new PartitionFetchResult(RoutingResult.STANDBY_FALLBACK, location, Optional.of(e));
} catch (Exception e) {
throw new KsqlException(String.format("Error forwarding query to node %s: %s", node.location(), e.getMessage()), e);
}
}
}
use of io.confluent.ksql.query.PullQueryQueue in project ksql by confluentinc.
the class HARouting method handlePullQuery.
public CompletableFuture<Void> handlePullQuery(final ServiceContext serviceContext, final PullPhysicalPlan pullPhysicalPlan, final ConfiguredStatement<Query> statement, final RoutingOptions routingOptions, final LogicalSchema outputSchema, final QueryId queryId, final PullQueryQueue pullQueryQueue, final CompletableFuture<Void> shouldCancelRequests, final Optional<ConsistencyOffsetVector> consistencyOffsetVector) {
final List<KsqlPartitionLocation> allLocations = pullPhysicalPlan.getMaterialization().locator().locate(pullPhysicalPlan.getKeys(), routingOptions, routingFilterFactory, pullPhysicalPlan.getPlanType() == PullPhysicalPlanType.RANGE_SCAN);
final Map<Integer, List<Host>> emptyPartitions = allLocations.stream().filter(loc -> loc.getNodes().stream().noneMatch(node -> node.getHost().isSelected())).collect(Collectors.toMap(KsqlPartitionLocation::getPartition, loc -> loc.getNodes().stream().map(KsqlNode::getHost).collect(Collectors.toList())));
if (!emptyPartitions.isEmpty()) {
final MaterializationException materializationException = new MaterializationException("Unable to execute pull query. " + emptyPartitions.entrySet().stream().map(kv -> String.format("Partition %s failed to find valid host. Hosts scanned: %s", kv.getKey(), kv.getValue())).collect(Collectors.joining(", ", "[", "]")));
LOG.debug(materializationException.getMessage());
throw materializationException;
}
// at this point we should filter out the hosts that we should not route to
final List<KsqlPartitionLocation> locations = allLocations.stream().map(KsqlPartitionLocation::removeFilteredHosts).collect(Collectors.toList());
final CompletableFuture<Void> completableFuture = new CompletableFuture<>();
coordinatorExecutorService.submit(() -> {
try {
executeRounds(serviceContext, pullPhysicalPlan, statement, routingOptions, outputSchema, queryId, locations, pullQueryQueue, shouldCancelRequests, consistencyOffsetVector);
completableFuture.complete(null);
} catch (Throwable t) {
completableFuture.completeExceptionally(t);
}
});
return completableFuture;
}
use of io.confluent.ksql.query.PullQueryQueue in project ksql by confluentinc.
the class HARouting method streamedRowsHandler.
private static Consumer<List<StreamedRow>> streamedRowsHandler(final KsqlNode owner, final PullQueryQueue pullQueryQueue, final BiFunction<List<?>, LogicalSchema, PullQueryRow> rowFactory, final LogicalSchema outputSchema, final Optional<ConsistencyOffsetVector> consistencyOffsetVector) {
final AtomicInteger processedRows = new AtomicInteger(0);
final AtomicReference<Header> header = new AtomicReference<>();
return streamedRows -> {
try {
if (streamedRows == null || streamedRows.isEmpty()) {
return;
}
final List<PullQueryRow> rows = new ArrayList<>();
// If this is the first row overall, skip the header
final int previousProcessedRows = processedRows.getAndAdd(streamedRows.size());
for (int i = 0; i < streamedRows.size(); i++) {
final StreamedRow row = streamedRows.get(i);
if (i == 0 && previousProcessedRows == 0) {
final Optional<Header> optionalHeader = row.getHeader();
optionalHeader.ifPresent(h -> validateSchema(outputSchema, h.getSchema(), owner));
optionalHeader.ifPresent(header::set);
continue;
}
if (row.getErrorMessage().isPresent()) {
// If we receive an error that's not a network error, we let that bubble up.
throw new KsqlException(row.getErrorMessage().get().getMessage());
}
if (!row.getRow().isPresent()) {
parseNonDataRows(row, i, consistencyOffsetVector);
continue;
}
final List<?> r = row.getRow().get().getColumns();
Preconditions.checkNotNull(header.get());
rows.add(rowFactory.apply(r, header.get().getSchema()));
}
if (!pullQueryQueue.acceptRows(rows)) {
LOG.error("Failed to queue all rows");
}
} catch (Exception e) {
throw new KsqlException(e.getMessage(), e);
}
};
}
use of io.confluent.ksql.query.PullQueryQueue in project ksql by confluentinc.
the class HARoutingTest method shouldCallRouteQuery_twoRound_networkError.
@Test
public void shouldCallRouteQuery_twoRound_networkError() throws InterruptedException, ExecutionException {
// Given:
locate(location2);
when(ksqlClient.makeQueryRequest(eq(node2.location()), any(), any(), any(), any(), any())).thenAnswer(i -> {
throw new RuntimeException("Network error!");
});
doAnswer(i -> {
final PullQueryQueue queue = i.getArgument(1);
queue.acceptRow(PQ_ROW1);
return null;
}).when(pullPhysicalPlan).execute(eq(ImmutableList.of(location2.removeHeadHost())), any(), any());
// When:
CompletableFuture<Void> future = haRouting.handlePullQuery(serviceContext, pullPhysicalPlan, statement, routingOptions, logicalSchema, queryId, pullQueryQueue, disconnect, Optional.empty());
future.get();
// Then:
verify(ksqlClient, times(1)).makeQueryRequest(eq(node2.location()), any(), any(), any(), any(), any());
verify(pullPhysicalPlan).execute(eq(ImmutableList.of(location2.removeHeadHost())), any(), any());
assertThat(pullQueryQueue.size(), is(1));
assertThat(pullQueryQueue.pollRow(1, TimeUnit.SECONDS).getRow(), is(ROW1));
final double fetch_count = getMetricValue("-partition-fetch-count");
final double resubmission_count = getMetricValue("-partition-fetch-resubmission-count");
assertThat(fetch_count, is(2.0));
assertThat(resubmission_count, is(1.0));
}
Aggregations