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

com.google.firebase.database.connection.util.RetryHelper Maven / Gradle / Ivy

Go to download

This is the official Firebase Admin Java SDK. Build extraordinary native JVM apps in minutes with Firebase. The Firebase platform can power your app’s backend, user authentication, static hosting, and more.

There is a newer version: 9.2.0
Show newest version
/*
 * Copyright 2017 Google Inc.
 *
 * 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.google.firebase.database.connection.util;

import java.util.Random;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RetryHelper {

  private static final Logger logger = LoggerFactory.getLogger(RetryHelper.class);

  private final ScheduledExecutorService executorService;
  /** The minimum delay for a retry in ms. */
  private final long minRetryDelayAfterFailure;
  /** The maximum retry delay in ms. */
  private final long maxRetryDelay;
  /**
   * The range of the delay that will be used at random. 0 => no randomness 0.5 => at least half the
   * current delay 1 => any delay between [min, max)
   */
  private final double jitterFactor;
  /** The backoff exponent. */
  private final double retryExponent;

  private final Random random = new Random();

  private ScheduledFuture scheduledRetry;

  private long currentRetryDelay;
  private boolean lastWasSuccess = true;

  private RetryHelper(
      ScheduledExecutorService executorService,
      long minRetryDelayAfterFailure,
      long maxRetryDelay,
      double retryExponent,
      double jitterFactor) {
    this.executorService = executorService;
    this.minRetryDelayAfterFailure = minRetryDelayAfterFailure;
    this.maxRetryDelay = maxRetryDelay;
    this.retryExponent = retryExponent;
    this.jitterFactor = jitterFactor;
  }

  public void retry(final Runnable runnable) {    
    long delay;
    if (this.scheduledRetry != null) {
      logger.debug("Cancelling previous scheduled retry");
      this.scheduledRetry.cancel(false);
      this.scheduledRetry = null;
    }
    if (this.lastWasSuccess) {
      delay = 0;
    } else {
      if (this.currentRetryDelay == 0) {
        this.currentRetryDelay = this.minRetryDelayAfterFailure;
      } else {
        long newDelay = (long) (this.currentRetryDelay * this.retryExponent);
        this.currentRetryDelay = Math.min(newDelay, this.maxRetryDelay);
      }
      delay =
          (long)
              (((1 - jitterFactor) * this.currentRetryDelay)
                  + (jitterFactor * currentRetryDelay * random.nextDouble()));
    }
    this.lastWasSuccess = false;
    logger.debug("Scheduling retry in {}ms", delay);
    Runnable wrapped =
        new Runnable() {
          @Override
          public void run() {
            scheduledRetry = null;
            runnable.run();
          }
        };
    this.scheduledRetry = this.executorService.schedule(wrapped, delay, TimeUnit.MILLISECONDS);
  }

  public void signalSuccess() {
    this.lastWasSuccess = true;
    this.currentRetryDelay = 0;
  }

  public void setMaxDelay() {
    this.currentRetryDelay = this.maxRetryDelay;
  }

  public void cancel() {
    if (this.scheduledRetry != null) {
      logger.debug("Cancelling existing retry attempt");
      this.scheduledRetry.cancel(false);
      this.scheduledRetry = null;
    } else {
      logger.debug("No existing retry attempt to cancel");
    }
    this.currentRetryDelay = 0;
  }

  public static class Builder {

    private final ScheduledExecutorService service;
    private long minRetryDelayAfterFailure = 1000;
    private double jitterFactor = 0.5;
    private long retryMaxDelay = 30 * 1000;
    private double retryExponent = 1.3;

    public Builder(ScheduledExecutorService service, Class tag) {
      this.service = service;
    }

    public Builder withMinDelayAfterFailure(long delay) {
      this.minRetryDelayAfterFailure = delay;
      return this;
    }

    public Builder withMaxDelay(long delay) {
      this.retryMaxDelay = delay;
      return this;
    }

    public Builder withRetryExponent(double exponent) {
      this.retryExponent = exponent;
      return this;
    }

    public Builder withJitterFactor(double random) {
      if (random < 0 || random > 1) {
        throw new IllegalArgumentException("Argument out of range: " + random);
      }
      this.jitterFactor = random;
      return this;
    }

    public RetryHelper build() {
      return new RetryHelper(
          this.service,
          this.minRetryDelayAfterFailure,
          this.retryMaxDelay,
          this.retryExponent,
          this.jitterFactor);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy