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

temporalio / sdk-java / #175

pending completion
#175

push

github-actions

web-flow
Worker / Build Id versioning (#1786)

Implement new worker build id based versioning feature

236 of 236 new or added lines in 24 files covered. (100.0%)

18343 of 23697 relevant lines covered (77.41%)

0.81 hits per line

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

90.18
/temporal-sdk/src/main/java/io/temporal/activity/ActivityOptions.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.activity;
22

23
import com.google.common.base.Objects;
24
import io.temporal.client.WorkflowClientOptions;
25
import io.temporal.common.MethodRetry;
26
import io.temporal.common.RetryOptions;
27
import io.temporal.common.VersioningIntent;
28
import io.temporal.common.context.ContextPropagator;
29
import io.temporal.failure.CanceledFailure;
30
import java.time.Duration;
31
import java.util.List;
32

33
/** Options used to configure how an activity is invoked. */
34
public final class ActivityOptions {
35

36
  public static Builder newBuilder() {
37
    return new Builder();
1✔
38
  }
39

40
  public static Builder newBuilder(ActivityOptions options) {
41
    return new Builder(options);
1✔
42
  }
43

44
  @Deprecated
45
  public static ActivityOptions getDefaultInstance() {
46
    return DEFAULT_INSTANCE;
×
47
  }
48

49
  private static final ActivityOptions DEFAULT_INSTANCE;
50

51
  static {
52
    DEFAULT_INSTANCE = ActivityOptions.newBuilder().validateAndBuildWithDefaults();
1✔
53
  }
1✔
54

55
  public static final class Builder {
56

57
    private Duration heartbeatTimeout;
58
    private Duration scheduleToCloseTimeout;
59
    private Duration scheduleToStartTimeout;
60
    private Duration startToCloseTimeout;
61
    private String taskQueue;
62
    private RetryOptions retryOptions;
63
    private List<ContextPropagator> contextPropagators;
64
    private ActivityCancellationType cancellationType;
65
    private boolean disableEagerExecution;
66
    private VersioningIntent versioningIntent;
67

68
    private Builder() {}
69

70
    private Builder(ActivityOptions options) {
1✔
71
      if (options == null) {
1✔
72
        return;
×
73
      }
74
      this.taskQueue = options.taskQueue;
1✔
75
      this.heartbeatTimeout = options.heartbeatTimeout;
1✔
76
      this.retryOptions = options.retryOptions;
1✔
77
      this.contextPropagators = options.contextPropagators;
1✔
78
      this.scheduleToCloseTimeout = options.scheduleToCloseTimeout;
1✔
79
      this.startToCloseTimeout = options.startToCloseTimeout;
1✔
80
      this.scheduleToStartTimeout = options.scheduleToStartTimeout;
1✔
81
      this.cancellationType = options.cancellationType;
1✔
82
      this.disableEagerExecution = options.disableEagerExecution;
1✔
83
      this.versioningIntent = options.versioningIntent;
1✔
84
    }
1✔
85

86
    /**
87
     * Total time that a workflow is willing to wait for an Activity to complete.
88
     *
89
     * <p>ScheduleToCloseTimeout limits the total time of an Activity's execution including retries
90
     * (use {@link #setStartToCloseTimeout(Duration)} to limit the time of a single attempt).
91
     *
92
     * <p>Either this option or {@link #setStartToCloseTimeout(Duration)} is required.
93
     *
94
     * <p>Defaults to unlimited, which is chosen if set to null.
95
     */
96
    public Builder setScheduleToCloseTimeout(Duration scheduleToCloseTimeout) {
97
      this.scheduleToCloseTimeout = scheduleToCloseTimeout;
1✔
98
      return this;
1✔
99
    }
100

101
    /**
102
     * Time that the Activity Task can stay in the Task Queue before it is picked up by a Worker.
103
     *
104
     * <p>ScheduleToStartTimeout is always non-retryable. Retrying after this timeout doesn't make
105
     * sense as it would just put the Activity Task back into the same Task Queue.
106
     *
107
     * <p>Defaults to unlimited.
108
     */
109
    public Builder setScheduleToStartTimeout(Duration scheduleToStartTimeout) {
110
      this.scheduleToStartTimeout = scheduleToStartTimeout;
1✔
111
      return this;
1✔
112
    }
113

114
    /**
115
     * Maximum time of a single Activity attempt.
116
     *
117
     * <p>Note that the Temporal Server doesn't detect Worker process failures directly. It relies
118
     * on this timeout to detect that an Activity that didn't complete on time. So this timeout
119
     * should be as short as the longest possible execution of the Activity body. Potentially
120
     * long-running Activities must specify HeartbeatTimeout and call {@link
121
     * ActivityExecutionContext#heartbeat(Object)} periodically for timely failure detection.
122
     *
123
     * <p>If {@link #setScheduleToCloseTimeout(Duration)} is not provided, then this timeout is
124
     * required.
125
     */
126
    public Builder setStartToCloseTimeout(Duration startToCloseTimeout) {
127
      this.startToCloseTimeout = startToCloseTimeout;
1✔
128
      return this;
1✔
129
    }
130

131
    /**
132
     * Heartbeat interval. Activity must call {@link ActivityExecutionContext#heartbeat(Object)}
133
     * before this interval passes after the last heartbeat or the Activity starts.
134
     */
135
    public Builder setHeartbeatTimeout(Duration heartbeatTimeoutSeconds) {
136
      this.heartbeatTimeout = heartbeatTimeoutSeconds;
1✔
137
      return this;
1✔
138
    }
139

140
    /**
141
     * Task queue to use when dispatching activity task to a worker. By default, it is the same task
142
     * list name the workflow was started with. Default is used if set to {@code null}.
143
     */
144
    public Builder setTaskQueue(String taskQueue) {
145
      this.taskQueue = taskQueue;
1✔
146
      return this;
1✔
147
    }
148

149
    /**
150
     * RetryOptions that define how an Activity is retried in case of failure.
151
     *
152
     * <p>If not provided, the server-defined default activity retry policy will be used. If not
153
     * overridden, the server default activity retry policy is:
154
     *
155
     * <pre><code>
156
     *   InitialInterval:         1 second
157
     *   BackoffCoefficient:      2
158
     *   MaximumInterval:         100 seconds   // 100 * InitialInterval
159
     *   MaximumAttempts:         0             // Unlimited
160
     *   NonRetryableErrorTypes:  []
161
     * </pre></code>
162
     *
163
     * <p>If both {@link #setScheduleToCloseTimeout(Duration)} and {@link
164
     * RetryOptions.Builder#setMaximumAttempts(int)} are not set, the Activity will not be retried.
165
     *
166
     * <p>To ensure zero retries, set {@link RetryOptions.Builder#setMaximumAttempts(int)} to 1.
167
     */
168
    public Builder setRetryOptions(RetryOptions retryOptions) {
169
      this.retryOptions = retryOptions;
1✔
170
      return this;
1✔
171
    }
172

173
    /**
174
     * Note: <br>
175
     * This method has extremely limited usage. The majority of users should just set {@link
176
     * WorkflowClientOptions#getContextPropagators()}
177
     *
178
     * <p>Both "client" (workflow worker) and "server" (activity worker) sides of context
179
     * propagation from a workflow to an activity exist in a worker process (potentially the same
180
     * one), so they typically share the same worker options. Specifically, {@code
181
     * ContextPropagator}s specified on {@link
182
     * io.temporal.client.WorkflowClientOptions#getContextPropagators()}. {@link
183
     * io.temporal.client.WorkflowClientOptions.Builder#setContextPropagators(List)} is the right
184
     * place to specify {@code ContextPropagator}s between Workflow and an Activity. <br>
185
     * Specifying context propagators with this method overrides them only on the "client"
186
     * (workflow) side and can't be automatically promoted to the "server" (activity worker), which
187
     * always uses {@code ContextPropagator}s from {@link
188
     * io.temporal.client.WorkflowClientOptions#getContextPropagators()} <br>
189
     * The only legitimate usecase for this method is probably a situation when the specific
190
     * activity is implemented in a different language and in a completely different worker codebase
191
     * and in that case setting a {@code ContextPropagator} that is applied only on a "client" side
192
     * could make sense. <br>
193
     * This is also why there is no equivalent method on {@link LocalActivityOptions}.
194
     *
195
     * @see <a href="https://github.com/temporalio/sdk-java/issues/490">Rejected feature reqest for
196
     *     LocalActivityOption#contextPropagators</a>
197
     * @param contextPropagators specifies the list of context propagators to use during propagation
198
     *     from a workflow to the activity with these {@link ActivityOptions}. This list overrides
199
     *     the list specified on {@link
200
     *     io.temporal.client.WorkflowClientOptions#getContextPropagators()}, {@code null} means no
201
     *     overriding
202
     */
203
    public Builder setContextPropagators(List<ContextPropagator> contextPropagators) {
204
      this.contextPropagators = contextPropagators;
1✔
205
      return this;
1✔
206
    }
207

208
    /**
209
     * In case of an activity's scope cancellation the corresponding activity stub call fails with a
210
     * {@link CanceledFailure}.
211
     *
212
     * @param cancellationType Defines the activity's stub cancellation mode. The default value is
213
     *     {@link ActivityCancellationType#TRY_CANCEL}
214
     * @see ActivityCancellationType
215
     */
216
    public Builder setCancellationType(ActivityCancellationType cancellationType) {
217
      this.cancellationType = cancellationType;
1✔
218
      return this;
1✔
219
    }
220

221
    /**
222
     * If set to true, will not request eager execution regardless of worker settings. If false,
223
     * eager execution may still be disabled at the worker level or eager execution may not be
224
     * requested due to lack of available slots.
225
     *
226
     * <p>Eager activity execution means the server returns requested eager activities directly from
227
     * the workflow task back to this worker which is faster than non-eager which may be dispatched
228
     * to a separate worker.
229
     *
230
     * <p>Defaults to false, meaning that eager activity execution will be requested if possible.
231
     */
232
    public Builder setDisableEagerExecution(boolean disableEagerExecution) {
233
      this.disableEagerExecution = disableEagerExecution;
×
234
      return this;
×
235
    }
236

237
    /**
238
     * Specifies whether this activity should run on a worker with a compatible Build Id or not. See
239
     * the variants of {@link VersioningIntent}.
240
     */
241
    public Builder setVersioningIntent(VersioningIntent versioningIntent) {
242
      this.versioningIntent = versioningIntent;
×
243
      return this;
×
244
    }
245

246
    public Builder mergeActivityOptions(ActivityOptions override) {
247
      if (override == null) {
1✔
248
        return this;
1✔
249
      }
250
      this.taskQueue = (override.taskQueue == null) ? this.taskQueue : override.taskQueue;
1✔
251
      this.heartbeatTimeout =
1✔
252
          (override.heartbeatTimeout == null) ? this.heartbeatTimeout : override.heartbeatTimeout;
1✔
253
      this.retryOptions =
1✔
254
          (override.retryOptions == null) ? this.retryOptions : override.retryOptions;
1✔
255
      this.scheduleToCloseTimeout =
1✔
256
          (override.scheduleToCloseTimeout == null)
1✔
257
              ? this.scheduleToCloseTimeout
1✔
258
              : override.scheduleToCloseTimeout;
1✔
259
      this.startToCloseTimeout =
1✔
260
          (override.startToCloseTimeout == null)
1✔
261
              ? this.startToCloseTimeout
1✔
262
              : override.startToCloseTimeout;
1✔
263
      this.scheduleToStartTimeout =
1✔
264
          (override.scheduleToStartTimeout == null)
1✔
265
              ? this.scheduleToStartTimeout
1✔
266
              : override.scheduleToStartTimeout;
1✔
267
      this.cancellationType =
1✔
268
          (override.cancellationType == null) ? this.cancellationType : override.cancellationType;
1✔
269
      if (this.contextPropagators == null) {
1✔
270
        this.contextPropagators = override.contextPropagators;
1✔
271
      } else if (override.contextPropagators != null) {
×
272
        this.contextPropagators.addAll(override.contextPropagators);
×
273
      }
274
      if (override.versioningIntent != VersioningIntent.VERSIONING_INTENT_UNSPECIFIED) {
1✔
275
        this.versioningIntent = override.versioningIntent;
1✔
276
      }
277
      return this;
1✔
278
    }
279

280
    /**
281
     * Properties that are set on this builder take precedence over ones found in the annotation.
282
     */
283
    public Builder mergeMethodRetry(MethodRetry r) {
284
      retryOptions = RetryOptions.merge(r, retryOptions);
1✔
285
      return this;
1✔
286
    }
287

288
    public ActivityOptions build() {
289
      return new ActivityOptions(
1✔
290
          heartbeatTimeout,
291
          scheduleToCloseTimeout,
292
          scheduleToStartTimeout,
293
          startToCloseTimeout,
294
          taskQueue,
295
          retryOptions,
296
          contextPropagators,
297
          cancellationType,
298
          disableEagerExecution,
299
          versioningIntent);
300
    }
301

302
    public ActivityOptions validateAndBuildWithDefaults() {
303
      return new ActivityOptions(
1✔
304
          heartbeatTimeout,
305
          scheduleToCloseTimeout,
306
          scheduleToStartTimeout,
307
          startToCloseTimeout,
308
          taskQueue,
309
          retryOptions,
310
          contextPropagators,
311
          cancellationType == null ? ActivityCancellationType.TRY_CANCEL : cancellationType,
1✔
312
          disableEagerExecution,
313
          versioningIntent == null
1✔
314
              ? VersioningIntent.VERSIONING_INTENT_UNSPECIFIED
1✔
315
              : versioningIntent);
1✔
316
    }
317
  }
318

319
  private final Duration heartbeatTimeout;
320
  private final Duration scheduleToCloseTimeout;
321
  private final Duration scheduleToStartTimeout;
322
  private final Duration startToCloseTimeout;
323
  private final String taskQueue;
324
  private final RetryOptions retryOptions;
325
  private final List<ContextPropagator> contextPropagators;
326
  private final ActivityCancellationType cancellationType;
327
  private final boolean disableEagerExecution;
328
  private final VersioningIntent versioningIntent;
329

330
  private ActivityOptions(
331
      Duration heartbeatTimeout,
332
      Duration scheduleToCloseTimeout,
333
      Duration scheduleToStartTimeout,
334
      Duration startToCloseTimeout,
335
      String taskQueue,
336
      RetryOptions retryOptions,
337
      List<ContextPropagator> contextPropagators,
338
      ActivityCancellationType cancellationType,
339
      boolean disableEagerExecution,
340
      VersioningIntent versioningIntent) {
1✔
341
    this.heartbeatTimeout = heartbeatTimeout;
1✔
342
    this.scheduleToStartTimeout = scheduleToStartTimeout;
1✔
343
    this.scheduleToCloseTimeout = scheduleToCloseTimeout;
1✔
344
    this.startToCloseTimeout = startToCloseTimeout;
1✔
345
    this.taskQueue = taskQueue;
1✔
346
    this.retryOptions = retryOptions;
1✔
347
    this.contextPropagators = contextPropagators;
1✔
348
    this.cancellationType = cancellationType;
1✔
349
    this.disableEagerExecution = disableEagerExecution;
1✔
350
    this.versioningIntent = versioningIntent;
1✔
351
  }
1✔
352

353
  /**
354
   * @see ActivityOptions.Builder#setHeartbeatTimeout(Duration)
355
   */
356
  public Duration getHeartbeatTimeout() {
357
    return heartbeatTimeout;
1✔
358
  }
359

360
  /**
361
   * @see ActivityOptions.Builder#setScheduleToCloseTimeout(Duration)
362
   */
363
  public Duration getScheduleToCloseTimeout() {
364
    return scheduleToCloseTimeout;
1✔
365
  }
366

367
  /**
368
   * @see ActivityOptions.Builder#setScheduleToStartTimeout(Duration)
369
   */
370
  public Duration getScheduleToStartTimeout() {
371
    return scheduleToStartTimeout;
1✔
372
  }
373

374
  /**
375
   * @see ActivityOptions.Builder#setStartToCloseTimeout(Duration)
376
   */
377
  public Duration getStartToCloseTimeout() {
378
    return startToCloseTimeout;
1✔
379
  }
380

381
  /**
382
   * @see ActivityOptions.Builder#setTaskQueue(String)
383
   */
384
  public String getTaskQueue() {
385
    return taskQueue;
1✔
386
  }
387

388
  /**
389
   * @see ActivityOptions.Builder#setRetryOptions(RetryOptions)
390
   */
391
  public RetryOptions getRetryOptions() {
392
    return retryOptions;
1✔
393
  }
394

395
  /**
396
   * @see ActivityOptions.Builder#setContextPropagators(List)
397
   */
398
  public List<ContextPropagator> getContextPropagators() {
399
    return contextPropagators;
1✔
400
  }
401

402
  /**
403
   * @see ActivityOptions.Builder#setCancellationType(ActivityCancellationType)
404
   */
405
  public ActivityCancellationType getCancellationType() {
406
    return cancellationType;
1✔
407
  }
408

409
  public boolean isEagerExecutionDisabled() {
410
    return disableEagerExecution;
1✔
411
  }
412

413
  /**
414
   * @see ActivityOptions.Builder#setVersioningIntent(VersioningIntent)
415
   */
416
  public VersioningIntent getVersioningIntent() {
417
    return versioningIntent;
1✔
418
  }
419

420
  public Builder toBuilder() {
421
    return new Builder(this);
1✔
422
  }
423

424
  @Override
425
  public boolean equals(Object o) {
426
    if (this == o) return true;
1✔
427
    if (o == null || getClass() != o.getClass()) return false;
1✔
428
    ActivityOptions that = (ActivityOptions) o;
1✔
429
    return cancellationType == that.cancellationType
1✔
430
        && Objects.equal(heartbeatTimeout, that.heartbeatTimeout)
1✔
431
        && Objects.equal(scheduleToCloseTimeout, that.scheduleToCloseTimeout)
1✔
432
        && Objects.equal(scheduleToStartTimeout, that.scheduleToStartTimeout)
1✔
433
        && Objects.equal(startToCloseTimeout, that.startToCloseTimeout)
1✔
434
        && Objects.equal(taskQueue, that.taskQueue)
1✔
435
        && Objects.equal(retryOptions, that.retryOptions)
1✔
436
        && Objects.equal(contextPropagators, that.contextPropagators)
1✔
437
        && disableEagerExecution == that.disableEagerExecution
438
        && versioningIntent == that.versioningIntent;
439
  }
440

441
  @Override
442
  public int hashCode() {
443
    return Objects.hashCode(
×
444
        heartbeatTimeout,
445
        scheduleToCloseTimeout,
446
        scheduleToStartTimeout,
447
        startToCloseTimeout,
448
        taskQueue,
449
        retryOptions,
450
        contextPropagators,
451
        cancellationType,
452
        disableEagerExecution,
×
453
        versioningIntent);
454
  }
455

456
  @Override
457
  public String toString() {
458
    return "ActivityOptions{"
×
459
        + "heartbeatTimeout="
460
        + heartbeatTimeout
461
        + ", scheduleToCloseTimeout="
462
        + scheduleToCloseTimeout
463
        + ", scheduleToStartTimeout="
464
        + scheduleToStartTimeout
465
        + ", startToCloseTimeout="
466
        + startToCloseTimeout
467
        + ", taskQueue='"
468
        + taskQueue
469
        + '\''
470
        + ", retryOptions="
471
        + retryOptions
472
        + ", contextPropagators="
473
        + contextPropagators
474
        + ", abandonOnCancellation="
475
        + cancellationType
476
        + ", disableEagerExecution="
477
        + disableEagerExecution
478
        + ", versioningIntent="
479
        + versioningIntent
480
        + '}';
481
  }
482
}
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