001/* 002 * Copyright 2015-2022 Transmogrify LLC, 2022-2026 Revetware LLC. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.pyranid; 018 019import org.jspecify.annotations.NonNull; 020import org.jspecify.annotations.Nullable; 021 022import javax.annotation.concurrent.ThreadSafe; 023import java.util.List; 024import java.util.Optional; 025 026import static java.util.Objects.requireNonNull; 027 028/** 029 * Result of a successful {@link Database#transactionWithRetry(RetryPolicy, ReturningTransactionalOperation)} call. 030 * <p> 031 * Retry methods return this type instead of a bare {@link Optional} so callers can inspect failures that were recovered 032 * before the transaction eventually succeeded. 033 * <p> 034 * If retry attempts are exhausted, Pyranid throws the final {@link DatabaseException} instead of returning this type. Prior 035 * failed attempts are attached to the thrown exception as suppressed exceptions. 036 * 037 * @author <a href="https://www.revetkn.com">Mark Allen</a> 038 * @since 4.4.0 039 */ 040@ThreadSafe 041public final class TransactionRetryResult<T> { 042 @Nullable 043 private final T value; 044 @NonNull 045 private final List<@NonNull DatabaseException> failures; 046 047 private TransactionRetryResult(@Nullable T value, 048 @NonNull List<@NonNull DatabaseException> failures) { 049 requireNonNull(failures); 050 051 this.value = value; 052 this.failures = List.copyOf(failures); 053 } 054 055 @NonNull 056 static <T> TransactionRetryResult<T> of(@NonNull Optional<T> value, 057 @NonNull List<@NonNull DatabaseException> failures) { 058 requireNonNull(value); 059 requireNonNull(failures); 060 061 return new TransactionRetryResult<>(value.orElse(null), failures); 062 } 063 064 /** 065 * Gets the value returned by the successful transaction attempt. 066 * 067 * @return successful transaction value, or empty if no value was returned 068 */ 069 @NonNull 070 public Optional<T> getValue() { 071 return Optional.ofNullable(this.value); 072 } 073 074 /** 075 * Gets the failed attempts that were retried before the transaction eventually succeeded. 076 * <p> 077 * Failures are returned in occurrence order. The returned list is immutable. 078 * 079 * @return failed attempts that were retried before success 080 */ 081 @NonNull 082 public List<@NonNull DatabaseException> getFailures() { 083 return this.failures; 084 } 085 086 /** 087 * Gets the number of transaction attempts, including the successful final attempt. 088 * 089 * @return attempt count 090 */ 091 @NonNull 092 public Integer getAttemptCount() { 093 return getFailures().size() + 1; 094 } 095 096 /** 097 * Indicates whether at least one failed attempt was retried before success. 098 * 099 * @return {@code true} if the transaction succeeded after retrying, {@code false} otherwise 100 */ 101 @NonNull 102 public Boolean wasRetried() { 103 return !getFailures().isEmpty(); 104 } 105}