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

org.elasticsearch.action.bulk.BackoffPolicy Maven / Gradle / Ivy

There is a newer version: 8.13.2
Show newest version
/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.action.bulk;

import org.elasticsearch.common.unit.TimeValue;

import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * Provides a backoff policy for bulk requests. Whenever a bulk request is rejected due to resource constraints (i.e. the client's internal
 * thread pool is full), the backoff policy decides how long the bulk processor will wait before the operation is retried internally.
 *
 * Notes for implementing custom subclasses:
 *
 * The underlying mathematical principle of BackoffPolicy are progressions which can be either finite or infinite although
 * the latter should not be used for retrying. A progression can be mapped to a java.util.Iterator with the following
 * semantics:
 *
 * 
    *
  • #hasNext() determines whether the progression has more elements. Return true for infinite progressions *
  • *
  • #next() determines the next element in the progression, i.e. the next wait time period
  • *
* * Note that backoff policies are exposed as Iterables in order to be consumed multiple times. */ public abstract class BackoffPolicy implements Iterable { private static final BackoffPolicy NO_BACKOFF = new NoBackoff(); /** * Creates a backoff policy that will not allow any backoff, i.e. an operation will fail after the first attempt. * * @return A backoff policy without any backoff period. The returned instance is thread safe. */ public static BackoffPolicy noBackoff() { return NO_BACKOFF; } /** * Creates an new constant backoff policy with the provided configuration. * * @param delay The delay defines how long to wait between retry attempts. Must not be null. * Must be <= Integer.MAX_VALUE ms. * @param maxNumberOfRetries The maximum number of retries. Must be a non-negative number. * @return A backoff policy with a constant wait time between retries. The returned instance is thread safe but each * iterator created from it should only be used by a single thread. */ public static BackoffPolicy constantBackoff(TimeValue delay, int maxNumberOfRetries) { return new ConstantBackoff(checkDelay(delay), maxNumberOfRetries); } /** * Creates an new exponential backoff policy with a default configuration of 50 ms initial wait period and 8 retries taking * roughly 5.1 seconds in total. * * @return A backoff policy with an exponential increase in wait time for retries. The returned instance is thread safe but each * iterator created from it should only be used by a single thread. */ public static BackoffPolicy exponentialBackoff() { return exponentialBackoff(TimeValue.timeValueMillis(50), 8); } /** * Creates an new exponential backoff policy with the provided configuration. * * @param initialDelay The initial delay defines how long to wait for the first retry attempt. Must not be null. * Must be <= Integer.MAX_VALUE ms. * @param maxNumberOfRetries The maximum number of retries. Must be a non-negative number. * @return A backoff policy with an exponential increase in wait time for retries. The returned instance is thread safe but each * iterator created from it should only be used by a single thread. */ public static BackoffPolicy exponentialBackoff(TimeValue initialDelay, int maxNumberOfRetries) { return new ExponentialBackoff((int) checkDelay(initialDelay).millis(), maxNumberOfRetries); } /** * Wraps the backoff policy in one that calls a method every time a new backoff is taken from the policy. */ public static BackoffPolicy wrap(BackoffPolicy delegate, Runnable onBackoff) { return new WrappedBackoffPolicy(delegate, onBackoff); } private static TimeValue checkDelay(TimeValue delay) { if (delay.millis() > Integer.MAX_VALUE) { throw new IllegalArgumentException("delay must be <= " + Integer.MAX_VALUE + " ms"); } return delay; } private static class NoBackoff extends BackoffPolicy { @Override public Iterator iterator() { return new Iterator() { @Override public boolean hasNext() { return false; } @Override public TimeValue next() { throw new NoSuchElementException("No backoff"); } }; } } private static class ExponentialBackoff extends BackoffPolicy { private final int start; private final int numberOfElements; private ExponentialBackoff(int start, int numberOfElements) { assert start >= 0; assert numberOfElements >= 0; this.start = start; this.numberOfElements = numberOfElements; } @Override public Iterator iterator() { return new ExponentialBackoffIterator(start, numberOfElements); } } private static class ExponentialBackoffIterator implements Iterator { private final int numberOfElements; private final int start; private int currentlyConsumed; private ExponentialBackoffIterator(int start, int numberOfElements) { this.start = start; this.numberOfElements = numberOfElements; } @Override public boolean hasNext() { return currentlyConsumed < numberOfElements; } @Override public TimeValue next() { if (!hasNext()) { throw new NoSuchElementException("Only up to " + numberOfElements + " elements"); } int result = start + 10 * ((int) Math.exp(0.8d * (currentlyConsumed)) - 1); currentlyConsumed++; return TimeValue.timeValueMillis(result); } } private static final class ConstantBackoff extends BackoffPolicy { private final TimeValue delay; private final int numberOfElements; ConstantBackoff(TimeValue delay, int numberOfElements) { assert numberOfElements >= 0; this.delay = delay; this.numberOfElements = numberOfElements; } @Override public Iterator iterator() { return new ConstantBackoffIterator(delay, numberOfElements); } } private static final class ConstantBackoffIterator implements Iterator { private final TimeValue delay; private final int numberOfElements; private int curr; ConstantBackoffIterator(TimeValue delay, int numberOfElements) { this.delay = delay; this.numberOfElements = numberOfElements; } @Override public boolean hasNext() { return curr < numberOfElements; } @Override public TimeValue next() { if (!hasNext()) { throw new NoSuchElementException(); } curr++; return delay; } } private static final class WrappedBackoffPolicy extends BackoffPolicy { private final BackoffPolicy delegate; private final Runnable onBackoff; WrappedBackoffPolicy(BackoffPolicy delegate, Runnable onBackoff) { this.delegate = delegate; this.onBackoff = onBackoff; } @Override public Iterator iterator() { return new WrappedBackoffIterator(delegate.iterator(), onBackoff); } } private static final class WrappedBackoffIterator implements Iterator { private final Iterator delegate; private final Runnable onBackoff; WrappedBackoffIterator(Iterator delegate, Runnable onBackoff) { this.delegate = delegate; this.onBackoff = onBackoff; } @Override public boolean hasNext() { return delegate.hasNext(); } @Override public TimeValue next() { if (false == delegate.hasNext()) { throw new NoSuchElementException(); } onBackoff.run(); return delegate.next(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy