Java tutorial
/* * Copyright 2014 Cask Data, 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 co.cask.tigon.internal.app.runtime.flow; import com.google.common.primitives.Longs; import java.util.concurrent.TimeUnit; /** * Data structure for tracking retry and backing off calls to process method in flowlet. */ final class FlowletProcessEntry<T> implements Comparable<FlowletProcessEntry> { // Minimum back-off time in nanoseconds, 1ms. private static final long BACKOFF_MIN = TimeUnit.MILLISECONDS.toNanos(1); // Maximum back-off time in nanoseconds when increasing exponentially, 100ms. private static final long BACKOFF_MAX = TimeUnit.MILLISECONDS.toNanos(100); // Start time for switching from constant to exponentially increasing back-off time, 20ms. private static final long BACKOFF_EXP_START = TimeUnit.MILLISECONDS.toNanos(20); // Incrementing back-off time by this until reaching exponential increase range. private static final long BACKOFF_CONSTANT_INCREMENT = TimeUnit.MILLISECONDS.toNanos(1); // Doubling back-off time during exponential increase, up to maximum back-off time. private static final int BACKOFF_EXP = 2; private final ProcessSpecification<T> processSpec; private final ProcessSpecification<T> retrySpec; private final boolean isTick; private long nextDeque; private long currentBackOff = BACKOFF_MIN; static <T> FlowletProcessEntry<T> create(ProcessSpecification<T> processSpec) { return new FlowletProcessEntry<T>(processSpec, null, processSpec.getInitialCallDelay()); } static <T> FlowletProcessEntry<T> create(ProcessSpecification<T> processSpec, ProcessSpecification<T> retrySpec) { return new FlowletProcessEntry<T>(processSpec, retrySpec, 0); } private FlowletProcessEntry(ProcessSpecification<T> processSpec, ProcessSpecification<T> retrySpec, long nextDeque) { this.processSpec = processSpec; this.retrySpec = retrySpec; this.nextDeque = nextDeque; this.isTick = processSpec.isTick(); } public boolean isRetry() { return retrySpec != null; } public void await() throws InterruptedException { long waitTime = nextDeque - System.nanoTime(); if (waitTime > 0) { TimeUnit.NANOSECONDS.sleep(waitTime); } } public boolean shouldProcess() { return nextDeque - System.nanoTime() <= 0; } @Override public int compareTo(FlowletProcessEntry o) { return Longs.compare(nextDeque, o.nextDeque); } public void resetBackOff() { nextDeque = System.nanoTime() + processSpec.getCallDelay(); currentBackOff = BACKOFF_MIN; } public void backOff() { nextDeque = System.nanoTime() + currentBackOff; if (currentBackOff < BACKOFF_EXP_START) { currentBackOff += BACKOFF_CONSTANT_INCREMENT; } else { currentBackOff = Math.min(currentBackOff * BACKOFF_EXP, BACKOFF_MAX); } } public ProcessSpecification<T> getProcessSpec() { return retrySpec == null ? processSpec : retrySpec; } public FlowletProcessEntry<T> resetRetry() { return retrySpec == null ? this : new FlowletProcessEntry<T>(processSpec, null, processSpec.getCallDelay()); } public boolean isTick() { return isTick; } }