com.teradata.benchto.driver.listeners.BenchmarkServiceExecutionListener.java Source code

Java tutorial

Introduction

Here is the source code for com.teradata.benchto.driver.listeners.BenchmarkServiceExecutionListener.java

Source

/*
 * 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
 *
 *     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 com.teradata.benchto.driver.listeners;

import com.google.common.collect.ImmutableList;
import com.google.common.math.LongMath;
import com.teradata.benchto.driver.Benchmark;
import com.teradata.benchto.driver.Measurable;
import com.teradata.benchto.driver.execution.BenchmarkExecutionResult;
import com.teradata.benchto.driver.execution.QueryExecution;
import com.teradata.benchto.driver.execution.QueryExecutionResult;
import com.teradata.benchto.driver.listeners.benchmark.BenchmarkExecutionListener;
import com.teradata.benchto.driver.listeners.measurements.PostExecutionMeasurementProvider;
import com.teradata.benchto.driver.service.BenchmarkServiceClient;
import com.teradata.benchto.driver.service.BenchmarkServiceClient.BenchmarkStartRequest.BenchmarkStartRequestBuilder;
import com.teradata.benchto.driver.service.BenchmarkServiceClient.ExecutionStartRequest;
import com.teradata.benchto.driver.service.BenchmarkServiceClient.ExecutionStartRequest.ExecutionStartRequestBuilder;
import com.teradata.benchto.driver.service.BenchmarkServiceClient.FinishRequest;
import com.teradata.benchto.driver.service.BenchmarkServiceClient.FinishRequest.FinishRequestBuilder;
import com.teradata.benchto.driver.service.Measurement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.stereotype.Component;

import java.sql.SQLException;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;

import static com.teradata.benchto.driver.loader.BenchmarkDescriptor.RESERVED_KEYWORDS;
import static com.teradata.benchto.driver.service.BenchmarkServiceClient.FinishRequest.Status.ENDED;
import static com.teradata.benchto.driver.service.BenchmarkServiceClient.FinishRequest.Status.FAILED;
import static com.teradata.benchto.driver.utils.ExceptionUtils.stackTraceToString;
import static java.lang.String.format;

@Component
public class BenchmarkServiceExecutionListener implements BenchmarkExecutionListener {
    private static final Duration MAX_CLOCK_DRIFT = Duration.of(1, ChronoUnit.SECONDS);

    @Autowired
    private AsyncTaskExecutor taskExecutor;

    @Value("${benchmark-service.url}")
    private String serviceUrl;

    @Autowired
    private BenchmarkServiceClient benchmarkServiceClient;

    @Autowired
    private List<PostExecutionMeasurementProvider> measurementProviders;

    @Override
    public int getOrder() {
        return 0;
    }

    @Override
    public Future<?> benchmarkStarted(Benchmark benchmark) {
        checkClocksSync();

        return taskExecutor.submit(() -> {
            BenchmarkStartRequestBuilder requestBuilder = new BenchmarkStartRequestBuilder(benchmark.getName())
                    .environmentName(benchmark.getEnvironment());

            for (Map.Entry<String, String> variableEntry : benchmark.getVariables().entrySet()) {
                if (RESERVED_KEYWORDS.contains(variableEntry.getKey())) {
                    requestBuilder.addAttribute(variableEntry.getKey(), variableEntry.getValue());
                } else {
                    requestBuilder.addVariable(variableEntry.getKey(), variableEntry.getValue());
                }
            }

            BenchmarkServiceClient.BenchmarkStartRequest request = requestBuilder.build();

            benchmarkServiceClient.startBenchmark(benchmark.getUniqueName(), benchmark.getSequenceId(), request);
        });
    }

    private void checkClocksSync() {
        long timeBefore = System.currentTimeMillis();
        long serviceTime = benchmarkServiceClient.getServiceCurrentTime().toEpochMilli();
        long timeAfter = System.currentTimeMillis();

        long driftApproximation = Math.abs(LongMath.mean(timeBefore, timeAfter) - serviceTime);
        long approximationPrecision = timeAfter - LongMath.mean(timeBefore, timeAfter);

        Duration driftLowerBound = Duration.of(driftApproximation - approximationPrecision, ChronoUnit.MILLIS);

        if (driftLowerBound.compareTo(MAX_CLOCK_DRIFT) > 1) {
            throw new RuntimeException(
                    format("Detected driver and service clocks drift of at least %s, assumed sane maximum is %s",
                            driftLowerBound, MAX_CLOCK_DRIFT));
        }
    }

    @Override
    public Future<?> benchmarkFinished(BenchmarkExecutionResult benchmarkExecutionResult) {
        return CompletableFuture.supplyAsync(() -> getMeasurements(benchmarkExecutionResult), taskExecutor::execute)
                .thenCompose(future -> future).thenApply(measurements -> {
                    return new FinishRequestBuilder()
                            .withStatus(benchmarkExecutionResult.isSuccessful() ? ENDED : FAILED)
                            .withEndTime(benchmarkExecutionResult.getUtcEnd().toInstant())
                            .addMeasurements(measurements).build();
                }).thenAccept(request -> {
                    benchmarkServiceClient.finishBenchmark(benchmarkExecutionResult.getBenchmark().getUniqueName(),
                            benchmarkExecutionResult.getBenchmark().getSequenceId(), request);
                });
    }

    @Override
    public Future<?> executionStarted(QueryExecution execution) {
        return taskExecutor.submit(() -> {
            ExecutionStartRequest request = new ExecutionStartRequestBuilder().build();

            benchmarkServiceClient.startExecution(execution.getBenchmark().getUniqueName(),
                    execution.getBenchmark().getSequenceId(), executionSequenceId(execution), request);
        });
    }

    @Override
    public Future<?> executionFinished(QueryExecutionResult executionResult) {
        return CompletableFuture.supplyAsync(() -> getMeasurements(executionResult), taskExecutor::execute)
                .thenCompose(future -> future)
                .thenApply(measurements -> buildExecutionFinishedRequest(executionResult, measurements))
                .thenAccept(request -> {
                    benchmarkServiceClient.finishExecution(executionResult.getBenchmark().getUniqueName(),
                            executionResult.getBenchmark().getSequenceId(),
                            executionSequenceId(executionResult.getQueryExecution()), request);
                });
    }

    private FinishRequest buildExecutionFinishedRequest(QueryExecutionResult executionResult,
            List<Measurement> measurements) {
        FinishRequestBuilder requestBuilder = new FinishRequestBuilder()
                .withStatus(executionResult.isSuccessful() ? ENDED : FAILED)
                .withEndTime(executionResult.getUtcEnd().toInstant()).addMeasurements(measurements);

        if (executionResult.getPrestoQueryId().isPresent()) {
            requestBuilder.addAttribute("prestoQueryId", executionResult.getPrestoQueryId().get());
        }

        if (!executionResult.isSuccessful()) {
            requestBuilder.addAttribute("failureMessage", executionResult.getFailureCause().getMessage());
            requestBuilder.addAttribute("failureStackTrace", stackTraceToString(executionResult));

            if (executionResult.getFailureCause() instanceof SQLException) {
                requestBuilder.addAttribute("failureSQLErrorCode",
                        "" + ((SQLException) executionResult.getFailureCause()).getErrorCode());
            }
        }
        return requestBuilder.build();
    }

    private CompletableFuture<List<Measurement>> getMeasurements(Measurable measurable) {
        List<CompletableFuture<?>> providerFutures = new ArrayList<>();
        List<Measurement> measurementsList = Collections.synchronizedList(new ArrayList<>());
        for (PostExecutionMeasurementProvider measurementProvider : measurementProviders) {
            CompletableFuture<?> future = measurementProvider.loadMeasurements(measurable)
                    .thenAccept(measurementsList::addAll);
            providerFutures.add(future);
        }

        return CompletableFuture.allOf(providerFutures.stream().toArray(CompletableFuture[]::new))
                .thenApply(aVoid -> ImmutableList.copyOf(measurementsList));
    }

    private String executionSequenceId(QueryExecution execution) {
        return "" + execution.getRun();
    }
}