Skip to content

Commit 4e0719f

Browse files
committed
Configure eager reservations per workflow task
1 parent 1e1ad53 commit 4e0719f

11 files changed

Lines changed: 116 additions & 127 deletions

File tree

temporal-sdk/src/main/java/io/temporal/internal/worker/ActivityWorker.java

Lines changed: 8 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,8 @@ public WorkerLifecycleState getLifecycleState() {
218218
return poller.getLifecycleState();
219219
}
220220

221-
public EagerActivityDispatcher getEagerActivityDispatcher(
222-
int maxConcurrentEagerActivityExecutionSize) {
223-
return new EagerActivityDispatcherImpl(maxConcurrentEagerActivityExecutionSize);
221+
public EagerActivityDispatcher getEagerActivityDispatcher() {
222+
return new EagerActivityDispatcherImpl();
224223
}
225224

226225
private PollerOptions getPollerOptions(SingleWorkerOptions options) {
@@ -486,13 +485,6 @@ private void logExceptionDuringResultReporting(
486485
}
487486

488487
private final class EagerActivityDispatcherImpl implements EagerActivityDispatcher {
489-
private final EagerActivitySlotLimiter eagerActivitySlotLimiter;
490-
491-
private EagerActivityDispatcherImpl(int maxConcurrentEagerActivityExecutionSize) {
492-
this.eagerActivitySlotLimiter =
493-
new EagerActivitySlotLimiter(maxConcurrentEagerActivityExecutionSize);
494-
}
495-
496488
@Override
497489
public Optional<SlotPermit> tryReserveActivitySlot(
498490
ScheduleActivityTaskCommandAttributesOrBuilder commandAttributes) {
@@ -501,31 +493,15 @@ public Optional<SlotPermit> tryReserveActivitySlot(
501493
commandAttributes.getTaskQueue().getName(), ActivityWorker.this.taskQueue)) {
502494
return Optional.empty();
503495
}
504-
if (!eagerActivitySlotLimiter.tryReserve()) {
505-
return Optional.empty();
506-
}
507-
Optional<SlotPermit> permit = Optional.empty();
508-
try {
509-
permit =
510-
ActivityWorker.this.slotSupplier.tryReserveSlot(
511-
new SlotReservationData(
512-
ActivityWorker.this.taskQueue, options.getIdentity(), options.getBuildId()));
513-
return permit;
514-
} finally {
515-
if (!permit.isPresent()) {
516-
eagerActivitySlotLimiter.release();
517-
}
518-
}
496+
return ActivityWorker.this.slotSupplier.tryReserveSlot(
497+
new SlotReservationData(
498+
ActivityWorker.this.taskQueue, options.getIdentity(), options.getBuildId()));
519499
}
520500

521501
@Override
522502
public void releaseActivitySlotReservations(Iterable<SlotPermit> permits) {
523503
for (SlotPermit permit : permits) {
524-
try {
525-
ActivityWorker.this.slotSupplier.releaseSlot(SlotReleaseReason.neverUsed(), permit);
526-
} finally {
527-
eagerActivitySlotLimiter.release();
528-
}
504+
ActivityWorker.this.slotSupplier.releaseSlot(SlotReleaseReason.neverUsed(), permit);
529505
}
530506
}
531507

@@ -535,39 +511,9 @@ public void dispatchActivity(PollActivityTaskQueueResponse activity, SlotPermit
535511
new ActivityTask(
536512
activity,
537513
permit,
538-
() -> {
539-
try {
514+
() ->
540515
ActivityWorker.this.slotSupplier.releaseSlot(
541-
SlotReleaseReason.taskComplete(), permit);
542-
} finally {
543-
eagerActivitySlotLimiter.release();
544-
}
545-
}));
546-
}
547-
}
548-
549-
static final class EagerActivitySlotLimiter {
550-
private final int maxConcurrent;
551-
private int heldSlotCount;
552-
553-
EagerActivitySlotLimiter(int maxConcurrent) {
554-
this.maxConcurrent = maxConcurrent;
555-
}
556-
557-
synchronized boolean tryReserve() {
558-
if (maxConcurrent > 0 && heldSlotCount >= maxConcurrent) {
559-
return false;
560-
}
561-
heldSlotCount++;
562-
return true;
563-
}
564-
565-
synchronized void release() {
566-
if (heldSlotCount <= 0) {
567-
throw new IllegalStateException(
568-
"Trying to release an unreserved eager activity slot. This is an SDK bug.");
569-
}
570-
heldSlotCount--;
516+
SlotReleaseReason.taskComplete(), permit)));
571517
}
572518
}
573519
}

temporal-sdk/src/main/java/io/temporal/internal/worker/EagerActivitySlotsReservation.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import io.temporal.api.workflowservice.v1.PollActivityTaskQueueResponse;
88
import io.temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedRequest;
99
import io.temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedResponse;
10-
import io.temporal.internal.Config;
1110
import io.temporal.worker.tuning.SlotPermit;
1211
import java.io.Closeable;
1312
import java.util.ArrayList;
@@ -19,10 +18,13 @@
1918
@NotThreadSafe
2019
class EagerActivitySlotsReservation implements Closeable {
2120
private final EagerActivityDispatcher eagerActivityDispatcher;
21+
private final int maxReservations;
2222
private final List<SlotPermit> reservedSlots = new ArrayList<>();
2323

24-
EagerActivitySlotsReservation(EagerActivityDispatcher eagerActivityDispatcher) {
24+
EagerActivitySlotsReservation(
25+
EagerActivityDispatcher eagerActivityDispatcher, int maxReservations) {
2526
this.eagerActivityDispatcher = eagerActivityDispatcher;
27+
this.maxReservations = maxReservations;
2628
}
2729

2830
public void applyToRequest(RespondWorkflowTaskCompletedRequest.Builder mutableRequest) {
@@ -33,7 +35,7 @@ public void applyToRequest(RespondWorkflowTaskCompletedRequest.Builder mutableRe
3335
ScheduleActivityTaskCommandAttributes commandAttributes =
3436
command.getScheduleActivityTaskCommandAttributes();
3537
if (!commandAttributes.getRequestEagerExecution()) continue;
36-
boolean atLimit = this.reservedSlots.size() >= Config.EAGER_ACTIVITIES_LIMIT;
38+
boolean atLimit = this.reservedSlots.size() >= this.maxReservations;
3739
Optional<SlotPermit> permit = Optional.empty();
3840
if (!atLimit) {
3941
permit = this.eagerActivityDispatcher.tryReserveActivitySlot(commandAttributes);

temporal-sdk/src/main/java/io/temporal/internal/worker/SyncActivityWorker.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,8 @@ public WorkerLifecycleState getLifecycleState() {
163163
}
164164
}
165165

166-
public EagerActivityDispatcher getEagerActivityDispatcher(
167-
int maxConcurrentEagerActivityExecutionSize) {
168-
return this.worker.getEagerActivityDispatcher(maxConcurrentEagerActivityExecutionSize);
166+
public EagerActivityDispatcher getEagerActivityDispatcher() {
167+
return this.worker.getEagerActivityDispatcher();
169168
}
170169

171170
public boolean isAnyTypeSupported() {

temporal-sdk/src/main/java/io/temporal/internal/worker/SyncWorkflowWorker.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ public SyncWorkflowWorker(
6767
String stickyTaskQueueName,
6868
@Nonnull WorkflowThreadExecutor workflowThreadExecutor,
6969
@Nonnull EagerActivityDispatcher eagerActivityDispatcher,
70+
int maxEagerActivityReservationsPerWorkflowTask,
7071
@Nonnull SlotSupplier<WorkflowSlotInfo> slotSupplier,
7172
@Nonnull SlotSupplier<LocalActivitySlotInfo> laSlotSupplier,
7273
@Nonnull NamespaceCapabilities namespaceCapabilities) {
@@ -123,6 +124,7 @@ public SyncWorkflowWorker(
123124
cache,
124125
taskHandler,
125126
eagerActivityDispatcher,
127+
maxEagerActivityReservationsPerWorkflowTask,
126128
slotSupplier,
127129
namespaceCapabilities);
128130

temporal-sdk/src/main/java/io/temporal/internal/worker/WorkflowWorker.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ final class WorkflowWorker implements SuspendableWorker {
5252
private final Scope workerMetricsScope;
5353
private final GrpcRetryer grpcRetryer;
5454
private final EagerActivityDispatcher eagerActivityDispatcher;
55+
private final int maxEagerActivityReservationsPerWorkflowTask;
5556
private final TrackingSlotSupplier<WorkflowSlotInfo> slotSupplier;
5657

5758
private final TaskCounter taskCounter = new TaskCounter();
@@ -77,6 +78,7 @@ public WorkflowWorker(
7778
@Nonnull WorkflowExecutorCache cache,
7879
@Nonnull WorkflowTaskHandler handler,
7980
@Nonnull EagerActivityDispatcher eagerActivityDispatcher,
81+
int maxEagerActivityReservationsPerWorkflowTask,
8082
@Nonnull SlotSupplier<WorkflowSlotInfo> slotSupplier,
8183
@Nonnull NamespaceCapabilities namespaceCapabilities) {
8284
this.service = Objects.requireNonNull(service);
@@ -92,6 +94,7 @@ public WorkflowWorker(
9294
this.handler = Objects.requireNonNull(handler);
9395
this.grpcRetryer = new GrpcRetryer(service.getServerCapabilities());
9496
this.eagerActivityDispatcher = eagerActivityDispatcher;
97+
this.maxEagerActivityReservationsPerWorkflowTask = maxEagerActivityReservationsPerWorkflowTask;
9598
this.slotSupplier = new TrackingSlotSupplier<>(slotSupplier, this.workerMetricsScope);
9699
this.namespaceCapabilities = namespaceCapabilities;
97100
}
@@ -478,7 +481,8 @@ public void handle(WorkflowTask task) throws Exception {
478481
RespondWorkflowTaskCompletedRequest.Builder requestBuilder =
479482
taskCompleted.toBuilder();
480483
try (EagerActivitySlotsReservation activitySlotsReservation =
481-
new EagerActivitySlotsReservation(eagerActivityDispatcher)) {
484+
new EagerActivitySlotsReservation(
485+
eagerActivityDispatcher, maxEagerActivityReservationsPerWorkflowTask)) {
482486
activitySlotsReservation.applyToRequest(requestBuilder);
483487
RespondWorkflowTaskCompletedResponse response =
484488
sendTaskCompleted(

temporal-sdk/src/main/java/io/temporal/worker/Worker.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,7 @@ private static final class TaskSnapshot {
173173

174174
EagerActivityDispatcher eagerActivityDispatcher =
175175
(activityWorker != null && !this.options.isEagerExecutionDisabled())
176-
? activityWorker.getEagerActivityDispatcher(
177-
this.options.getMaxConcurrentEagerActivityExecutionSize())
176+
? activityWorker.getEagerActivityDispatcher()
178177
: new EagerActivityDispatcher.NoopEagerActivityDispatcher();
179178

180179
SingleWorkerOptions nexusOptions =
@@ -245,6 +244,7 @@ private static final class TaskSnapshot {
245244
stickyTaskQueueName,
246245
workflowThreadExecutor,
247246
eagerActivityDispatcher,
247+
this.options.getMaxEagerActivityReservationsPerWorkflowTask(),
248248
workflowSlotSupplier,
249249
localActivitySlotSupplier,
250250
namespaceCapabilities);

temporal-sdk/src/main/java/io/temporal/worker/WorkerOptions.java

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import com.google.common.base.Preconditions;
66
import io.temporal.common.Experimental;
7+
import io.temporal.internal.Config;
78
import io.temporal.serviceclient.WorkflowServiceStubsOptions;
89
import io.temporal.worker.tuning.*;
910
import java.time.Duration;
@@ -64,7 +65,7 @@ public static final class Builder {
6465
private Duration defaultHeartbeatThrottleInterval;
6566
private Duration stickyQueueScheduleToStartTimeout;
6667
private boolean disableEagerExecution;
67-
private int maxConcurrentEagerActivityExecutionSize;
68+
private int maxEagerActivityReservationsPerWorkflowTask = Config.EAGER_ACTIVITIES_LIMIT;
6869
private String buildId;
6970
private boolean useBuildIdForVersioning;
7071
private Duration stickyTaskQueueDrainTimeout;
@@ -110,7 +111,8 @@ private Builder(WorkerOptions o) {
110111
this.defaultHeartbeatThrottleInterval = o.defaultHeartbeatThrottleInterval;
111112
this.stickyQueueScheduleToStartTimeout = o.stickyQueueScheduleToStartTimeout;
112113
this.disableEagerExecution = o.disableEagerExecution;
113-
this.maxConcurrentEagerActivityExecutionSize = o.maxConcurrentEagerActivityExecutionSize;
114+
this.maxEagerActivityReservationsPerWorkflowTask =
115+
o.maxEagerActivityReservationsPerWorkflowTask;
114116
this.useBuildIdForVersioning = o.useBuildIdForVersioning;
115117
this.buildId = o.buildId;
116118
this.stickyTaskQueueDrainTimeout = o.stickyTaskQueueDrainTimeout;
@@ -403,15 +405,15 @@ public Builder setDisableEagerExecution(boolean disableEagerExecution) {
403405
}
404406

405407
/**
406-
* Sets the maximum number of eager activities that can be running concurrently.
408+
* Sets the maximum number of activity slots that may be reserved for eager execution when
409+
* completing a workflow task.
407410
*
408-
* <p>When nonzero, eager activity execution will not be requested if it would cause the number
409-
* of running eager activities to exceed this value. The default of zero means unlimited and
410-
* therefore only bound by the activity slot supplier.
411+
* <p>The default is 3. Setting this to zero disables eager activity execution.
411412
*/
412-
public Builder setMaxConcurrentEagerActivityExecutionSize(
413-
int maxConcurrentEagerActivityExecutionSize) {
414-
this.maxConcurrentEagerActivityExecutionSize = maxConcurrentEagerActivityExecutionSize;
413+
public Builder setMaxEagerActivityReservationsPerWorkflowTask(
414+
int maxEagerActivityReservationsPerWorkflowTask) {
415+
this.maxEagerActivityReservationsPerWorkflowTask =
416+
maxEagerActivityReservationsPerWorkflowTask;
415417
return this;
416418
}
417419

@@ -638,7 +640,7 @@ public WorkerOptions build() {
638640
defaultHeartbeatThrottleInterval,
639641
stickyQueueScheduleToStartTimeout,
640642
disableEagerExecution,
641-
maxConcurrentEagerActivityExecutionSize,
643+
maxEagerActivityReservationsPerWorkflowTask,
642644
useBuildIdForVersioning,
643645
buildId,
644646
stickyTaskQueueDrainTimeout,
@@ -664,8 +666,8 @@ public WorkerOptions validateAndBuildWithDefaults() {
664666
Preconditions.checkState(
665667
maxConcurrentActivityExecutionSize >= 0, "negative maxConcurrentActivityExecutionSize");
666668
Preconditions.checkState(
667-
maxConcurrentEagerActivityExecutionSize >= 0,
668-
"negative maxConcurrentEagerActivityExecutionSize");
669+
maxEagerActivityReservationsPerWorkflowTask >= 0,
670+
"negative maxEagerActivityReservationsPerWorkflowTask");
669671
Preconditions.checkState(
670672
maxConcurrentWorkflowTaskExecutionSize >= 0,
671673
"negative maxConcurrentWorkflowTaskExecutionSize");
@@ -777,7 +779,7 @@ public WorkerOptions validateAndBuildWithDefaults() {
777779
? DEFAULT_STICKY_SCHEDULE_TO_START_TIMEOUT
778780
: stickyQueueScheduleToStartTimeout,
779781
disableEagerExecution,
780-
maxConcurrentEagerActivityExecutionSize,
782+
maxEagerActivityReservationsPerWorkflowTask,
781783
useBuildIdForVersioning,
782784
buildId,
783785
stickyTaskQueueDrainTimeout == null
@@ -816,7 +818,7 @@ public WorkerOptions validateAndBuildWithDefaults() {
816818
private final Duration defaultHeartbeatThrottleInterval;
817819
private final @Nonnull Duration stickyQueueScheduleToStartTimeout;
818820
private final boolean disableEagerExecution;
819-
private final int maxConcurrentEagerActivityExecutionSize;
821+
private final int maxEagerActivityReservationsPerWorkflowTask;
820822
private final boolean useBuildIdForVersioning;
821823
private final String buildId;
822824
private final Duration stickyTaskQueueDrainTimeout;
@@ -852,7 +854,7 @@ private WorkerOptions(
852854
Duration defaultHeartbeatThrottleInterval,
853855
@Nonnull Duration stickyQueueScheduleToStartTimeout,
854856
boolean disableEagerExecution,
855-
int maxConcurrentEagerActivityExecutionSize,
857+
int maxEagerActivityReservationsPerWorkflowTask,
856858
boolean useBuildIdForVersioning,
857859
String buildId,
858860
Duration stickyTaskQueueDrainTimeout,
@@ -886,7 +888,7 @@ private WorkerOptions(
886888
this.defaultHeartbeatThrottleInterval = defaultHeartbeatThrottleInterval;
887889
this.stickyQueueScheduleToStartTimeout = stickyQueueScheduleToStartTimeout;
888890
this.disableEagerExecution = maxTaskQueueActivitiesPerSecond > 0 ? true : disableEagerExecution;
889-
this.maxConcurrentEagerActivityExecutionSize = maxConcurrentEagerActivityExecutionSize;
891+
this.maxEagerActivityReservationsPerWorkflowTask = maxEagerActivityReservationsPerWorkflowTask;
890892
this.useBuildIdForVersioning = useBuildIdForVersioning;
891893
this.buildId = buildId;
892894
this.stickyTaskQueueDrainTimeout = stickyTaskQueueDrainTimeout;
@@ -1012,8 +1014,8 @@ public boolean isEagerExecutionDisabled() {
10121014
return disableEagerExecution;
10131015
}
10141016

1015-
public int getMaxConcurrentEagerActivityExecutionSize() {
1016-
return maxConcurrentEagerActivityExecutionSize;
1017+
public int getMaxEagerActivityReservationsPerWorkflowTask() {
1018+
return maxEagerActivityReservationsPerWorkflowTask;
10171019
}
10181020

10191021
public boolean isUsingBuildIdForVersioning() {
@@ -1097,7 +1099,8 @@ && compare(maxTaskQueueActivitiesPerSecond, that.maxTaskQueueActivitiesPerSecond
10971099
&& localActivityWorkerOnly == that.localActivityWorkerOnly
10981100
&& defaultDeadlockDetectionTimeout == that.defaultDeadlockDetectionTimeout
10991101
&& disableEagerExecution == that.disableEagerExecution
1100-
&& maxConcurrentEagerActivityExecutionSize == that.maxConcurrentEagerActivityExecutionSize
1102+
&& maxEagerActivityReservationsPerWorkflowTask
1103+
== that.maxEagerActivityReservationsPerWorkflowTask
11011104
&& useBuildIdForVersioning == that.useBuildIdForVersioning
11021105
&& Objects.equals(workerTuner, that.workerTuner)
11031106
&& Objects.equals(maxHeartbeatThrottleInterval, that.maxHeartbeatThrottleInterval)
@@ -1137,7 +1140,7 @@ public int hashCode() {
11371140
defaultHeartbeatThrottleInterval,
11381141
stickyQueueScheduleToStartTimeout,
11391142
disableEagerExecution,
1140-
maxConcurrentEagerActivityExecutionSize,
1143+
maxEagerActivityReservationsPerWorkflowTask,
11411144
useBuildIdForVersioning,
11421145
buildId,
11431146
stickyTaskQueueDrainTimeout,
@@ -1189,8 +1192,8 @@ public String toString() {
11891192
+ stickyQueueScheduleToStartTimeout
11901193
+ ", disableEagerExecution="
11911194
+ disableEagerExecution
1192-
+ ", maxConcurrentEagerActivityExecutionSize="
1193-
+ maxConcurrentEagerActivityExecutionSize
1195+
+ ", maxEagerActivityReservationsPerWorkflowTask="
1196+
+ maxEagerActivityReservationsPerWorkflowTask
11941197
+ ", useBuildIdForVersioning="
11951198
+ useBuildIdForVersioning
11961199
+ ", buildId='"

temporal-sdk/src/test/java/io/temporal/internal/worker/EagerActivitySlotLimiterTest.java

Lines changed: 0 additions & 31 deletions
This file was deleted.

0 commit comments

Comments
 (0)