Merge "ExceptionHook: Add method to skip auto-retrying of an operation with tracing"
diff --git a/java/com/google/gerrit/server/ExceptionHook.java b/java/com/google/gerrit/server/ExceptionHook.java
index a2bade0..019a715 100644
--- a/java/com/google/gerrit/server/ExceptionHook.java
+++ b/java/com/google/gerrit/server/ExceptionHook.java
@@ -34,6 +34,12 @@
* <p>Only affects operations that are executed with {@link
* com.google.gerrit.server.update.RetryHelper}.
*
+ * <p>Should return {@code true} only for exceptions that are caused by temporary issues where a
+ * retry of the operation has a chance to succeed.
+ *
+ * <p>If {@code false} is returned the operation is still retried once to capture a trace, unless
+ * {@link #skipRetryWithTrace(Throwable)} skips the auto-retry.
+ *
* @param throwable throwable that was thrown while executing the operation
* @return whether the operation should be retried
*/
@@ -42,6 +48,34 @@
}
/**
+ * Whether auto-retrying of an operation with tracing should be skipped for the given throwable.
+ *
+ * <p>Only affects operations that are executed with {@link
+ * com.google.gerrit.server.update.RetryHelper}.
+ *
+ * <p>This method is only called for exceptions for which the operation should not be retried
+ * ({@link #shouldRetry(Throwable)} returned {@code false}).
+ *
+ * <p>By default this method returns {@code false}, so that by default traces for unexpected
+ * exceptions are captured, which allows to investigate them.
+ *
+ * <p>Implementors may use this method to skip retry with tracing for exceptions that occur due to
+ * known causes that are permanent and where a trace is not needed for the investigation. For
+ * example, if an operation fails because persisted data is corrupt, it makes no sense to retry
+ * the operation with a trace, because the trace will not help with fixing the corrupt data.
+ *
+ * <p>This method is only invoked if retry with tracing is enabled on the server ({@code
+ * retry.retryWithTraceOnFailure} in {@code gerrit.config} is set to {@code true}).
+ *
+ * @param throwable throwable that was thrown while executing the operation
+ * @return whether auto-retrying of an operation with tracing should be skipped for the given
+ * throwable
+ */
+ default boolean skipRetryWithTrace(Throwable throwable) {
+ return false;
+ }
+
+ /**
* Formats the cause of an exception for use in metrics.
*
* <p>This method allows implementors to group exceptions that have the same cause into one metric
diff --git a/java/com/google/gerrit/server/update/RetryHelper.java b/java/com/google/gerrit/server/update/RetryHelper.java
index 4f9a67c..f3636c9 100644
--- a/java/com/google/gerrit/server/update/RetryHelper.java
+++ b/java/com/google/gerrit/server/update/RetryHelper.java
@@ -335,6 +335,12 @@
if (retryWithTraceOnFailure
&& opts.retryWithTrace().isPresent()
&& opts.retryWithTrace().get().test(t)) {
+ // Exception hooks may identify exceptions for which retrying with trace should be
+ // skipped.
+ if (exceptionHooks.stream().anyMatch(h -> h.skipRetryWithTrace(t))) {
+ return false;
+ }
+
String caller = opts.caller().orElse("N/A");
String cause = formatCause(t);
if (!traceContext.isTracing()) {