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

com.google.gerrit.server.update.RetryableAction Maven / Gradle / Ivy

There is a newer version: 3.11.0-rc3
Show newest version
// Copyright (C) 2019 The Android Open Source Project
//
// 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.gerrit.server.update;

import static java.util.Objects.requireNonNull;

import com.github.rholder.retry.RetryListener;
import com.google.common.base.Throwables;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.gerrit.server.ExceptionHook;
import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;

/**
 * An action that is executed with retrying.
 *
 * 

Instances of this class are created via {@link RetryHelper} (see {@link * RetryHelper#action(ActionType, String, Action)}, {@link RetryHelper#accountUpdate(String, * Action)}, {@link RetryHelper#changeUpdate(String, Action)}, {@link * RetryHelper#groupUpdate(String, Action)}, {@link RetryHelper#pluginUpdate(String, Action)}). * *

Which exceptions cause a retry is controlled by {@link ExceptionHook#shouldRetry(String, * String, Throwable)}. In addition callers can specify additional exception that should cause a * retry via {@link #retryOn(Predicate)}. */ public class RetryableAction { /** * Type of an retryable action. * *

The action type is used for two purposes: * *

    *
  • to determine the default timeout for executing the action (see {@link * RetryHelper#getDefaultTimeout(String)}) *
  • as bucket for all retry metrics (see {@link RetryHelper.Metrics}) *
*/ public enum ActionType { ACCOUNT_UPDATE, CHANGE_UPDATE, GIT_UPDATE, GROUP_UPDATE, INDEX_QUERY, PLUGIN_UPDATE, REST_READ_REQUEST, REST_WRITE_REQUEST, SEND_EMAIL, } @FunctionalInterface public interface Action { T call() throws Exception; } private final RetryHelper retryHelper; private final String actionType; private final Action action; private final RetryHelper.Options.Builder options = RetryHelper.options(); private final List> exceptionPredicates = new ArrayList<>(); private int numberOfCalls; RetryableAction( RetryHelper retryHelper, ActionType actionType, String actionName, Action action) { this(retryHelper, requireNonNull(actionType, "actionType").name(), actionName, action); } RetryableAction(RetryHelper retryHelper, String actionType, String actionName, Action action) { this.retryHelper = requireNonNull(retryHelper, "retryHelper"); this.actionType = requireNonNull(actionType, "actionType"); this.action = () -> { numberOfCalls++; try (TraceTimer timer = TraceContext.newTimer( actionName, Metadata.builder().attempt(numberOfCalls).build())) { return requireNonNull(action, "action").call(); } }; options.actionName(requireNonNull(actionName, "actionName")); } /** * Adds an additional condition that should trigger retries. * *

For some exceptions retrying is enabled globally (see {@link * ExceptionHook#shouldRetry(String, String, Throwable)}). Conditions for those exceptions do not * need to be specified here again. * *

This method can be invoked multiple times to add further conditions that should trigger * retries. * * @param exceptionPredicate predicate that decides if the action should be retried for a given * exception * @return this instance to enable chaining of calls */ @CanIgnoreReturnValue public RetryableAction retryOn(Predicate exceptionPredicate) { exceptionPredicates.add(exceptionPredicate); return this; } /** * Sets a condition that should trigger auto-retry with tracing. * *

This condition is only relevant if an exception occurs that doesn't trigger (normal) retry. * *

Auto-retry with tracing automatically captures traces for unexpected exceptions so that they * can be investigated. * *

Every call of this method overwrites any previously set condition for auto-retry with * tracing. * * @param exceptionPredicate predicate that decides if the action should be retried with tracing * for a given exception * @return this instance to enable chaining of calls */ @CanIgnoreReturnValue public RetryableAction retryWithTrace(Predicate exceptionPredicate) { options.retryWithTrace(exceptionPredicate); return this; } /** * Sets a callback that is invoked when auto-retry with tracing is triggered. * *

Via the callback callers can find out with trace ID was used for the retry. * *

Every call of this method overwrites any previously set trace ID consumer. * * @param traceIdConsumer trace ID consumer * @return this instance to enable chaining of calls */ @CanIgnoreReturnValue public RetryableAction onAutoTrace(Consumer traceIdConsumer) { options.onAutoTrace(traceIdConsumer); return this; } /** * Sets a listener that is invoked when the action is retried. * *

Every call of this method overwrites any previously set listener. * * @param retryListener retry listener * @return this instance to enable chaining of calls */ @CanIgnoreReturnValue public RetryableAction listener(RetryListener retryListener) { options.listener(retryListener); return this; } /** * Increases the default timeout by the given multiplier. * *

Every call of this method overwrites any previously set timeout. * * @param multiplier multiplier for the default timeout * @return this instance to enable chaining of calls */ @CanIgnoreReturnValue public RetryableAction defaultTimeoutMultiplier(int multiplier) { options.timeout(retryHelper.getDefaultTimeout(actionType).multipliedBy(multiplier)); return this; } /** * Executes this action with retry. * * @return the result of the action */ public T call() throws Exception { try { return retryHelper.execute( actionType, action, options.build(), t -> exceptionPredicates.stream().anyMatch(p -> p.test(t))); } catch (Exception t) { Throwables.throwIfUnchecked(t); Throwables.throwIfInstanceOf(t, Exception.class); throw new IllegalStateException(t); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy