• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

temporalio / sdk-java / #257

29 May 2024 08:30PM UTC coverage: 77.442% (-0.02%) from 77.463%
#257

push

github

web-flow
Add support for nextRetryDelay (#2081)

Add support for nextRetryDelay

38 of 43 new or added lines in 6 files covered. (88.37%)

11 existing lines in 5 files now uncovered.

19204 of 24798 relevant lines covered (77.44%)

0.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.65
/temporal-test-server/src/main/java/io/temporal/internal/testservice/TestServiceRetryState.java
1
/*
2
 * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
3
 *
4
 * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5
 *
6
 * Modifications copyright (C) 2017 Uber Technologies, Inc.
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this material except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *   http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20

21
package io.temporal.internal.testservice;
22

23
import com.google.protobuf.Timestamp;
24
import com.google.protobuf.util.Durations;
25
import com.google.protobuf.util.Timestamps;
26
import io.grpc.Status;
27
import io.temporal.api.common.v1.RetryPolicy;
28
import io.temporal.api.enums.v1.RetryState;
29
import io.temporal.api.failure.v1.ApplicationFailureInfo;
30
import io.temporal.api.failure.v1.Failure;
31
import io.temporal.internal.common.ProtobufTimeUtils;
32
import java.time.Duration;
33
import java.util.List;
34
import java.util.Optional;
35

36
final class TestServiceRetryState {
37

38
  static class BackoffInterval {
39
    private final Duration interval;
40
    private final RetryState retryState;
41

42
    BackoffInterval(Duration interval) {
1✔
43
      this.interval = interval;
1✔
44
      this.retryState = RetryState.RETRY_STATE_IN_PROGRESS;
1✔
45
    }
1✔
46

47
    BackoffInterval(RetryState retryState) {
1✔
48
      this.interval = Duration.ofMillis(-1000);
1✔
49
      this.retryState = retryState;
1✔
50
    }
1✔
51

52
    public Duration getInterval() {
53
      return interval;
1✔
54
    }
55

56
    public RetryState getRetryState() {
57
      return retryState;
1✔
58
    }
59
  }
60

61
  private final RetryPolicy retryPolicy;
62
  private final Timestamp expirationTime;
63
  private final int attempt;
64
  private final Optional<Failure> lastFailure;
65

66
  TestServiceRetryState(RetryPolicy retryPolicy, Timestamp expirationTime) {
67
    this(validateAndOverrideRetryPolicy(retryPolicy), expirationTime, 1, Optional.empty());
1✔
68
  }
1✔
69

70
  private TestServiceRetryState(
71
      RetryPolicy retryPolicy,
72
      Timestamp expirationTime,
73
      int attempt,
74
      Optional<Failure> lastFailure) {
1✔
75
    this.retryPolicy = retryPolicy;
1✔
76
    this.expirationTime =
1✔
77
        Timestamps.toMillis(expirationTime) == 0 ? Timestamps.MAX_VALUE : expirationTime;
1✔
78
    this.attempt = attempt;
1✔
79
    this.lastFailure = lastFailure;
1✔
80
  }
1✔
81

82
  RetryPolicy getRetryPolicy() {
83
    return retryPolicy;
1✔
84
  }
85

86
  Timestamp getExpirationTime() {
87
    return expirationTime;
1✔
88
  }
89

90
  int getAttempt() {
91
    return attempt;
1✔
92
  }
93

94
  public Optional<Failure> getPreviousRunFailure() {
95
    return lastFailure;
1✔
96
  }
97

98
  TestServiceRetryState getNextAttempt(Optional<Failure> failure) {
99
    return new TestServiceRetryState(retryPolicy, expirationTime, attempt + 1, failure);
1✔
100
  }
101

102
  BackoffInterval getBackoffIntervalInSeconds(
103
      Optional<String> errorType, Timestamp currentTime, Optional<Duration> nextRetryDelay) {
104
    RetryPolicy retryPolicy = getRetryPolicy();
1✔
105
    // check if error is non-retryable
106
    List<String> nonRetryableErrorTypes = retryPolicy.getNonRetryableErrorTypesList();
1✔
107
    if (nonRetryableErrorTypes != null && errorType.isPresent()) {
1✔
108
      String type = errorType.get();
1✔
109
      for (String err : nonRetryableErrorTypes) {
1✔
110
        if (type.equals(err)) {
1✔
111
          return new BackoffInterval(RetryState.RETRY_STATE_NON_RETRYABLE_FAILURE);
1✔
112
        }
113
      }
1✔
114
    }
115
    Timestamp expirationTime = getExpirationTime();
1✔
116
    if (retryPolicy.getMaximumAttempts() == 0 && Timestamps.toMillis(expirationTime) == 0) {
1✔
117
      return new BackoffInterval(RetryState.RETRY_STATE_RETRY_POLICY_NOT_SET);
×
118
    }
119

120
    if (retryPolicy.getMaximumAttempts() > 0 && getAttempt() >= retryPolicy.getMaximumAttempts()) {
1✔
121
      // currAttempt starts from 1.
122
      // MaximumAttempts is the total attempts, including initial (non-retry) attempt.
123
      return new BackoffInterval(RetryState.RETRY_STATE_MAXIMUM_ATTEMPTS_REACHED);
1✔
124
    }
125

126
    Optional<ApplicationFailureInfo> info = lastFailure.map(Failure::getApplicationFailureInfo);
1✔
127
    Duration backoffDuration;
128
    if (nextRetryDelay.isPresent()) {
1✔
129
      backoffDuration = nextRetryDelay.get();
1✔
130
    } else {
131
      long initInterval = Durations.toMillis(retryPolicy.getInitialInterval());
1✔
132
      long nextInterval =
1✔
133
          (long) (initInterval * Math.pow(retryPolicy.getBackoffCoefficient(), getAttempt() - 1));
1✔
134
      long maxInterval = Durations.toMillis(retryPolicy.getMaximumInterval());
1✔
135
      if (nextInterval <= 0) {
1✔
136
        // math.Pow() could overflow
NEW
137
        if (maxInterval > 0) {
×
NEW
138
          nextInterval = maxInterval;
×
139
        }
140
      }
141
      if (maxInterval > 0 && nextInterval > maxInterval) {
1✔
142
        // cap next interval to MaxInterval
143
        nextInterval = maxInterval;
1✔
144
      } else if (nextInterval <= 0) {
1✔
145
        return new BackoffInterval(RetryState.RETRY_STATE_TIMEOUT);
×
146
      }
147
      backoffDuration = Duration.ofMillis(nextInterval);
1✔
148
    }
149

150
    Timestamp nextScheduleTime =
1✔
151
        Timestamps.add(currentTime, ProtobufTimeUtils.toProtoDuration(backoffDuration));
1✔
152
    if (expirationTime.getNanos() != 0
1✔
153
        && Timestamps.compare(nextScheduleTime, expirationTime) > 0) {
1✔
154
      return new BackoffInterval(RetryState.RETRY_STATE_TIMEOUT);
1✔
155
    }
156
    return new BackoffInterval(backoffDuration);
1✔
157
  }
158

159
  static RetryPolicy validateAndOverrideRetryPolicy(RetryPolicy p) {
160
    RetryPolicy.Builder policy = p.toBuilder();
1✔
161
    if (Durations.compare(policy.getInitialInterval(), Durations.ZERO) < 0) {
1✔
162
      throw Status.INVALID_ARGUMENT
×
163
          .withDescription("InitialIntervalInSeconds must be greater than 0 on retry policy.")
×
164
          .asRuntimeException();
×
165
    }
166
    if (Durations.compare(policy.getInitialInterval(), Durations.ZERO) == 0) {
1✔
167
      policy.setInitialInterval(Durations.fromSeconds(1));
1✔
168
    }
169
    if (policy.getBackoffCoefficient() != 0 && policy.getBackoffCoefficient() < 1) {
1✔
170
      throw Status.INVALID_ARGUMENT
×
171
          .withDescription("BackoffCoefficient cannot be less than 1 on retry policy.")
×
172
          .asRuntimeException();
×
173
    }
174
    if (policy.getBackoffCoefficient() == 0) {
1✔
175
      policy.setBackoffCoefficient(2d);
1✔
176
    }
177
    if (Durations.compare(policy.getMaximumInterval(), Durations.ZERO) < 0) {
1✔
178
      throw Status.INVALID_ARGUMENT
×
179
          .withDescription("MaximumIntervalInSeconds cannot be less than 0 on retry policy.")
×
180
          .asRuntimeException();
×
181
    }
182
    if (Durations.compare(policy.getMaximumInterval(), Durations.ZERO) > 0
1✔
183
        && Durations.compare(policy.getMaximumInterval(), policy.getInitialInterval()) < 0) {
1✔
184
      throw Status.INVALID_ARGUMENT
×
185
          .withDescription(
×
186
              "MaximumIntervalInSeconds cannot be less than InitialIntervalInSeconds on retry policy.")
187
          .asRuntimeException();
×
188
    }
189
    if (policy.getMaximumAttempts() < 0) {
1✔
190
      throw Status.INVALID_ARGUMENT
×
191
          .withDescription("MaximumAttempts cannot be less than 0 on retry policy.")
×
192
          .asRuntimeException();
×
193
    }
194
    return policy.build();
1✔
195
  }
196
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc