All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.teradata.benchto.driver.execution.ExecutionSynchronizer Maven / Gradle / Ivy

/*
 * 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.execution;

import com.teradata.benchto.driver.Benchmark;
import com.teradata.benchto.driver.graphite.GraphiteProperties;
import com.teradata.benchto.driver.utils.TimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;

import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import static java.util.concurrent.CompletableFuture.completedFuture;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;

/**
 * This class is responsible for synchronizing threads in driver if graphite metrics collection
 * is enabled. Graphite collects metrics with predefined resolution, ex. 10 s.
 * 

* After query/benchmark is finished we should wait at least 2 resolutions before we execute * next query/benchmark, so runs does not interfere with each other. *

* Graphite metrics loading should be delayed at least 1 resolution to make sure that last * probe was stored in graphite. */ @Component public class ExecutionSynchronizer { private static final Logger LOGGER = LoggerFactory.getLogger(ExecutionSynchronizer.class); private static final double GRAPHITE_WAIT_BETWEEN_REPORTING_RESOLUTION_COUNT = 2; private static final Duration SHUTDOWN_ASYNC_TASKS_WAIT_TIMEOUT = Duration.ofMinutes(20); private static final int SHUTDOWN_ASYNC_TASKS_WAIT_REPORT_TIMES = 20; @Autowired private GraphiteProperties properties; private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2); @PreDestroy public void shutdown() throws InterruptedException { /* * Request shutdown but let the planned ones complete. */ executorService.shutdown(); executorService.awaitTermination(100, MILLISECONDS); for (int i = 0; i < SHUTDOWN_ASYNC_TASKS_WAIT_REPORT_TIMES && !executorService.isTerminated(); i++) { LOGGER.info("Waiting for asynchronous tasks to complete ..."); executorService.awaitTermination(SHUTDOWN_ASYNC_TASKS_WAIT_TIMEOUT.dividedBy(SHUTDOWN_ASYNC_TASKS_WAIT_REPORT_TIMES).toMillis(), TimeUnit.MILLISECONDS); } if (!executorService.isTerminated()) { throw new RuntimeException("Some tasks did not finish on time"); } } /** * If metrics collection is enabled and we are doing serial benchmark, we should wait * between queries, so measurements are accurate. */ public void awaitAfterQueryExecutionAndBeforeResultReport(QueryExecutionResult queryExecutionResult) { if (properties.isGraphiteMetricsCollectionEnabled() && queryExecutionResult.getBenchmark().isSerial()) { int waitSecondsBetweenRuns = waitSecondsBetweenRuns(); LOGGER.info("Waiting {}s between queries - thread ({})", waitSecondsBetweenRuns, currThreadName()); TimeUtils.sleep(waitSecondsBetweenRuns, SECONDS); } } /** * If metrics collection is enabled and we are doing concurrent benchmark, we should wait * between benchmarks, so measurements are accurate. */ public void awaitAfterBenchmarkExecutionAndBeforeResultReport(Benchmark benchmark) { if (properties.isGraphiteMetricsCollectionEnabled() && benchmark.isConcurrent()) { int waitSecondsBetweenRuns = waitSecondsBetweenRuns(); LOGGER.info("Waiting {}s between benchmarks - thread ({})", waitSecondsBetweenRuns, currThreadName()); TimeUtils.sleep(waitSecondsBetweenRuns, SECONDS); } } /** * Executes {@code callable} when time comes. The {@code callable} gets executed immediately, without * offloading to a backghround thread, if execution time requested has already passed. */ public CompletableFuture execute(Instant when, Callable callable) { if (!Instant.now().isBefore(when)) { // Run immediately. try { return completedFuture(callable.call()); } catch (Exception e) { CompletableFuture future = new CompletableFuture<>(); future.completeExceptionally(e); return future; } } long delay = Instant.now().until(when, ChronoUnit.MILLIS); CompletableFuture future = new CompletableFuture<>(); executorService.schedule(() -> { try { future.complete(callable.call()); } catch (Throwable e) { future.completeExceptionally(e); throw e; } return null; }, delay, MILLISECONDS); return future; } private int waitSecondsBetweenRuns() { return (int) (properties.getGraphiteResolutionSeconds() * GRAPHITE_WAIT_BETWEEN_REPORTING_RESOLUTION_COUNT); } private String currThreadName() { return Thread.currentThread().getName(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy