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

io.activej.promise.RetryPolicy Maven / Gradle / Ivy

Go to download

A convenient way to organize asynchronous code. Promises are a faster and more efficient version of JavaScript's Promise and Java's CompletionStage's.

There is a newer version: 6.0-rc2
Show newest version
/*
 * Copyright (C) 2020 ActiveJ LLC.
 *
 * 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 io.activej.promise;

import io.activej.common.ref.RefInt;
import io.activej.common.ref.RefLong;
import io.activej.common.tuple.Tuple2;

import java.time.Duration;

import static io.activej.common.Checks.checkArgument;
import static java.lang.Math.*;

public interface RetryPolicy {
	S createRetryState();

	long nextRetryTimestamp(long now, Exception lastError, S retryState);

	abstract class StatelessRetryPolicy implements RetryPolicy {
		@Override
		public final Void createRetryState() {
			return null;
		}
	}

	static RetryPolicy noRetry() {
		return new StatelessRetryPolicy() {
			@Override
			public long nextRetryTimestamp(long now, Exception lastError, Void retryState) {
				return 0;
			}
		};
	}

	static RetryPolicy immediateRetry() {
		return new StatelessRetryPolicy() {
			@Override
			public long nextRetryTimestamp(long now, Exception lastError, Void retryState) {
				return now;
			}
		};
	}

	static RetryPolicy fixedDelay(Duration delay) {
		return fixedDelay(delay.toMillis());
	}

	static RetryPolicy fixedDelay(long delay) {
		return new StatelessRetryPolicy() {
			@Override
			public long nextRetryTimestamp(long now, Exception lastError, Void retryState) {
				return now + delay;
			}
		};
	}

	class SimpleRetryState {
		protected Exception lastError;
		protected int retryCount;
		protected long retryFirstTimestamp;
	}

	abstract class SimpleRetryPolicy implements RetryPolicy {
		@Override
		public final SimpleRetryState createRetryState() {
			return new SimpleRetryState();
		}

		@Override
		public final long nextRetryTimestamp(long now, Exception lastError, SimpleRetryState retryState) {
			retryState.lastError = lastError;
			retryState.retryCount++;
			if (retryState.retryFirstTimestamp == 0L) {
				retryState.retryFirstTimestamp = now;
			}
			return nextRetryTimestamp(now, lastError, retryState.retryCount, retryState.retryFirstTimestamp);
		}

		public abstract long nextRetryTimestamp(long now, Exception lastError, int retryCount, long firstRetryTimestamp);
	}

	static RetryPolicy exponentialBackoff(Duration initialDelay, Duration maxDelay, double exponent) {
		return exponentialBackoff(initialDelay.toMillis(), maxDelay.toMillis(), exponent);
	}

	static RetryPolicy exponentialBackoff(long initialDelay, long maxDelay, double exponent) {
		checkArgument(maxDelay > initialDelay && exponent > 1.0,
			"Max delay should be greater than initial delay and exponent should be greater than 1.0");
		int maxRetryCount = (int) ceil(log((double) maxDelay / initialDelay) / log(exponent));
		return new SimpleRetryPolicy() {
			@Override
			public long nextRetryTimestamp(long now, Exception lastError, int retryCount, long firstRetryTimestamp) {
				return
					now +
					(retryCount > maxRetryCount ?
						maxDelay :
						min(maxDelay, (long) (initialDelay * pow(exponent, retryCount))));
			}
		};
	}

	static RetryPolicy exponentialBackoff(Duration initialDelay, Duration maxDelay) {
		return exponentialBackoff(initialDelay.toMillis(), maxDelay.toMillis());
	}

	static RetryPolicy exponentialBackoff(long initialDelay, long maxDelay) {
		return exponentialBackoff(initialDelay, maxDelay, 2.0);
	}

	abstract class DelegatingRetryPolicy implements RetryPolicy> {
		private final RetryPolicy delegateRetryPolicy;

		protected DelegatingRetryPolicy(RetryPolicy policy) {delegateRetryPolicy = policy;}

		@Override
		public final Tuple2 createRetryState() {
			DS delegateRetryState = delegateRetryPolicy.createRetryState();
			S retryState = doCreateRetryState();
			return new Tuple2<>(retryState, delegateRetryState);
		}

		protected abstract S doCreateRetryState();

		@Override
		public final long nextRetryTimestamp(long now, Exception lastError, Tuple2 retryState) {
			return nextRetryTimestamp(now, lastError, retryState.value1(), retryState.value2());
		}

		public abstract long nextRetryTimestamp(long now, Exception lastError, S retryState, DS delegateRetryState);
	}

	default RetryPolicy> withMaxTotalRetryCount(int maxRetryCount) {
		return new DelegatingRetryPolicy<>(this) {
			@Override
			protected RefInt doCreateRetryState() {
				return new RefInt(0);
			}

			@Override
			public long nextRetryTimestamp(long now, Exception lastError, RefInt retryState, S delegateRetryState) {
				if (retryState.value++ < maxRetryCount) {
					return RetryPolicy.this.nextRetryTimestamp(now, lastError, delegateRetryState);
				} else {
					return 0L;
				}
			}
		};
	}

	default RetryPolicy withMaxTotalRetryTimeout(Duration maxRetryTimeout) {
		long maxRetryTimeoutMillis = maxRetryTimeout.toMillis();
		return new DelegatingRetryPolicy(this) {
			@Override
			protected RefLong doCreateRetryState() {
				return new RefLong(0);
			}

			@Override
			public long nextRetryTimestamp(long now, Exception lastError, RefLong retryState, S delegateRetryState) {
				if (retryState.value == 0) retryState.value = now;
				if (now < retryState.value + maxRetryTimeoutMillis) {
					return RetryPolicy.this.nextRetryTimestamp(now, lastError, delegateRetryState);
				} else {
					return 0L;
				}
			}
		};
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy