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

com.wavefront.agent.queueing.TaskSizeEstimator Maven / Gradle / Ivy

There is a newer version: 13.4
Show newest version
package com.wavefront.agent.queueing;

import com.google.common.util.concurrent.RateLimiter;
import com.wavefront.agent.SharedMetricsRegistry;
import com.wavefront.agent.data.DataSubmissionTask;
import com.wavefront.common.NamedThreadFactory;
import com.wavefront.common.TaggedMetricName;
import com.yammer.metrics.Metrics;
import com.yammer.metrics.core.Gauge;
import com.yammer.metrics.core.Histogram;
import com.yammer.metrics.core.Meter;
import com.yammer.metrics.core.MetricsRegistry;
import java.io.ByteArrayOutputStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;

/**
 * Calculates approximate task sizes to estimate how quickly we would run out of disk space if we
 * are no longer able to send data to the server endpoint (i.e. network outage).
 *
 * @author [email protected].
 */
public class TaskSizeEstimator {
  private static final MetricsRegistry REGISTRY = SharedMetricsRegistry.getInstance();
  /**
   * Biases result sizes to the last 5 minutes heavily. This histogram does not see all result
   * sizes. The executor only ever processes one posting at any given time and drops the rest.
   * {@link #resultPostingMeter} records the actual rate (i.e. sees all posting calls).
   */
  private final Histogram resultPostingSizes;

  private final Meter resultPostingMeter;
  /** A single threaded bounded work queue to update result posting sizes. */
  private final ExecutorService resultPostingSizerExecutorService;

  @SuppressWarnings("rawtypes")
  private final TaskConverter taskConverter;

  /** Only size postings once every 5 seconds. */
  @SuppressWarnings("UnstableApiUsage")
  private final RateLimiter resultSizingRateLimier = RateLimiter.create(0.2);

  /** @param handle metric pipeline handle (usually port number). */
  public TaskSizeEstimator(String handle) {
    this.resultPostingSizes =
        REGISTRY.newHistogram(
            new TaggedMetricName("post-result", "result-size", "port", handle), true);
    this.resultPostingMeter =
        REGISTRY.newMeter(
            new TaggedMetricName("post-result", "results", "port", handle),
            "results",
            TimeUnit.MINUTES);
    this.resultPostingSizerExecutorService =
        new ThreadPoolExecutor(
            1,
            1,
            60L,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(1),
            new NamedThreadFactory("result-posting-sizer-" + handle));
    // for now, we can just use a generic task converter with default lz4 compression method
    this.taskConverter = new RetryTaskConverter<>(handle, TaskConverter.CompressionType.LZ4);
    Metrics.newGauge(
        new TaggedMetricName("buffer", "fill-rate", "port", handle),
        new Gauge() {
          @Override
          public Long value() {
            return getBytesPerMinute();
          }
        });
  }

  /**
   * Submit a candidate task to be sized. The task may or may not be accepted, depending on the rate
   * limiter
   *
   * @param task task to be sized.
   */
  public > void scheduleTaskForSizing(T task) {
    resultPostingMeter.mark();
    try {
      //noinspection UnstableApiUsage
      if (resultSizingRateLimier.tryAcquire()) {
        resultPostingSizerExecutorService.submit(getPostingSizerTask(task));
      }
    } catch (Exception ex) {
      // ignored.
    }
  }

  /**
   * Calculates the bytes per minute buffer usage rate. Needs at
   *
   * @return bytes per minute for requests submissions. Null if no data is available yet (needs at
   *     least
   */
  @Nullable
  public Long getBytesPerMinute() {
    if (resultPostingSizes.count() < 50) return null;
    if (resultPostingMeter.fifteenMinuteRate() == 0 || resultPostingSizes.mean() == 0) return null;
    return (long) (resultPostingSizes.mean() * resultPostingMeter.fifteenMinuteRate());
  }

  public void shutdown() {
    resultPostingSizerExecutorService.shutdown();
  }

  private > Runnable getPostingSizerTask(final T task) {
    return () -> {
      try {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        taskConverter.serializeToStream(task, outputStream);
        resultPostingSizes.update(outputStream.size());
      } catch (Throwable t) {
        // ignored. this is a stats task.
      }
    };
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy