Skip to content

Commit 18162b7

Browse files
Reset sticky queue backlog on empty response (#1859)
1 parent ac474fa commit 18162b7

File tree

2 files changed

+138
-1
lines changed

2 files changed

+138
-1
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ public WorkflowTask poll() {
144144
} finally {
145145
if (!isSuccessful) {
146146
workflowTaskExecutorSemaphore.release();
147-
stickyQueueBalancer.finishPoll(taskQueueKind);
147+
stickyQueueBalancer.finishPoll(taskQueueKind, 0);
148148
}
149149
}
150150
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
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.worker;
22+
23+
import static java.nio.charset.StandardCharsets.UTF_8;
24+
import static junit.framework.TestCase.assertEquals;
25+
import static org.junit.Assert.*;
26+
import static org.mockito.ArgumentMatchers.any;
27+
import static org.mockito.ArgumentMatchers.eq;
28+
import static org.mockito.Mockito.mock;
29+
import static org.mockito.Mockito.when;
30+
31+
import com.google.protobuf.ByteString;
32+
import com.uber.m3.tally.RootScopeBuilder;
33+
import com.uber.m3.tally.Scope;
34+
import io.temporal.api.common.v1.WorkflowExecution;
35+
import io.temporal.api.common.v1.WorkflowType;
36+
import io.temporal.api.enums.v1.TaskQueueKind;
37+
import io.temporal.api.taskqueue.v1.TaskQueue;
38+
import io.temporal.api.workflowservice.v1.GetSystemInfoResponse;
39+
import io.temporal.api.workflowservice.v1.PollWorkflowTaskQueueRequest;
40+
import io.temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse;
41+
import io.temporal.api.workflowservice.v1.WorkflowServiceGrpc;
42+
import io.temporal.common.reporter.TestStatsReporter;
43+
import io.temporal.serviceclient.WorkflowServiceStubs;
44+
import java.util.concurrent.Semaphore;
45+
import org.junit.Test;
46+
import org.junit.runner.RunWith;
47+
import org.junit.runners.Parameterized;
48+
import org.mockito.stubbing.OngoingStubbing;
49+
50+
@RunWith(Parameterized.class)
51+
public class StickyQueueBacklogTest {
52+
private final TestStatsReporter reporter = new TestStatsReporter();
53+
private static final String WORKFLOW_ID = "test-workflow-id";
54+
private static final String RUN_ID = "test-run-id";
55+
private static final String WORKFLOW_TYPE = "test-workflow-type";
56+
57+
@Parameterized.Parameter public boolean throwOnPoll;
58+
59+
@Parameterized.Parameters()
60+
public static Object[] data() {
61+
return new Object[][] {{true}, {false}};
62+
}
63+
64+
@Test
65+
public void stickyQueueBacklogResetTest() {
66+
// Verify the sticky queue backlog is reset on an empty response or failure
67+
WorkflowServiceStubs client = mock(WorkflowServiceStubs.class);
68+
when(client.getServerCapabilities())
69+
.thenReturn(() -> GetSystemInfoResponse.Capabilities.newBuilder().build());
70+
WorkflowServiceGrpc.WorkflowServiceBlockingStub blockingStub =
71+
mock(WorkflowServiceGrpc.WorkflowServiceBlockingStub.class);
72+
when(client.blockingStub()).thenReturn(blockingStub);
73+
when(blockingStub.withOption(any(), any())).thenReturn(blockingStub);
74+
75+
Semaphore executorSlotsSemaphore = new Semaphore(10);
76+
StickyQueueBalancer stickyQueueBalancer = new StickyQueueBalancer(2, true);
77+
78+
Scope metricsScope =
79+
new RootScopeBuilder()
80+
.reporter(reporter)
81+
.reportEvery(com.uber.m3.util.Duration.ofMillis(1));
82+
WorkflowPollTask poller =
83+
new WorkflowPollTask(
84+
client,
85+
"default",
86+
"taskqueue",
87+
"stickytaskqueue",
88+
"",
89+
"",
90+
false,
91+
executorSlotsSemaphore,
92+
stickyQueueBalancer,
93+
metricsScope,
94+
() -> GetSystemInfoResponse.Capabilities.newBuilder().build());
95+
96+
PollWorkflowTaskQueueResponse pollResponse =
97+
PollWorkflowTaskQueueResponse.newBuilder()
98+
.setTaskToken(ByteString.copyFrom("token", UTF_8))
99+
.setWorkflowExecution(
100+
WorkflowExecution.newBuilder().setWorkflowId(WORKFLOW_ID).setRunId(RUN_ID).build())
101+
.setWorkflowType(WorkflowType.newBuilder().setName(WORKFLOW_TYPE).build())
102+
// Set a large backlog count
103+
.setBacklogCountHint(100)
104+
.build();
105+
106+
OngoingStubbing<PollWorkflowTaskQueueResponse> pollMock =
107+
when(blockingStub.pollWorkflowTaskQueue(
108+
eq(
109+
PollWorkflowTaskQueueRequest.newBuilder()
110+
.setTaskQueue(
111+
TaskQueue.newBuilder()
112+
.setName("stickytaskqueue")
113+
.setNormalName("taskqueue")
114+
.setKind(TaskQueueKind.TASK_QUEUE_KIND_STICKY)
115+
.build())
116+
.setNamespace("default")
117+
.build())))
118+
.thenReturn(pollResponse);
119+
if (throwOnPoll) {
120+
pollMock.thenThrow(new RuntimeException("Poll failed"));
121+
}
122+
pollMock.thenReturn(null);
123+
124+
WorkflowTask task = poller.poll();
125+
// Expect a nonempty poll response, the sticky queue backlog should now be set
126+
assertNotNull(task);
127+
// On a null poll or failure the task queue the backlog should be reset
128+
if (throwOnPoll) {
129+
assertThrows(RuntimeException.class, () -> poller.poll());
130+
} else {
131+
assertNull(poller.poll());
132+
}
133+
assertEquals(TaskQueueKind.TASK_QUEUE_KIND_STICKY, stickyQueueBalancer.makePoll());
134+
// If the backlog was not reset this would be a sticky task
135+
assertEquals(TaskQueueKind.TASK_QUEUE_KIND_NORMAL, stickyQueueBalancer.makePoll());
136+
}
137+
}

0 commit comments

Comments
 (0)