diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 4cd9d61cd..92a9f8980 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -4,7 +4,7 @@ steps: queue: "default" docker: "*" command: "./gradlew --no-daemon test -x checkLicenseMain -x checkLicenses -x spotlessCheck -x spotlessApply -x spotlessJava -P edgeDepsTest" - timeout_in_minutes: 15 + timeout_in_minutes: 20 plugins: - docker-compose#v3.8.0: run: unit-test-test-service-edge @@ -15,7 +15,7 @@ steps: queue: "default" docker: "*" command: "./gradlew --no-daemon test -x checkLicenseMain -x checkLicenses -x spotlessCheck -x spotlessApply -x spotlessJava" - timeout_in_minutes: 15 + timeout_in_minutes: 20 plugins: - docker-compose#v3.8.0: run: unit-test-docker-jdk8 @@ -26,7 +26,7 @@ steps: queue: "default" docker: "*" command: "./gradlew --no-daemon checkLicenseMain checkLicenses spotlessCheck" - timeout_in_minutes: 15 + timeout_in_minutes: 20 plugins: - docker-compose#v3.8.0: run: jdk11 diff --git a/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowRunTaskHandler.java b/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowRunTaskHandler.java index 1f6571dbf..65dd81316 100644 --- a/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowRunTaskHandler.java +++ b/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowRunTaskHandler.java @@ -163,7 +163,9 @@ public WorkflowTaskResult handleWorkflowTask( } handleWorkflowTaskImpl(workflowTask, historyIterator); - processLocalActivityRequests(wftHearbeatDeadline); + if (!context.isWorkflowMethodCompleted()) { + processLocalActivityRequests(wftHearbeatDeadline); + } List commands = workflowStateMachines.takeCommands(); List messages = workflowStateMachines.takeMessages(); EnumSet newFlags = workflowStateMachines.takeNewSdkFlags(); diff --git a/temporal-sdk/src/main/java/io/temporal/internal/sync/SyncWorkflow.java b/temporal-sdk/src/main/java/io/temporal/internal/sync/SyncWorkflow.java index bfab45e04..c5428e01b 100644 --- a/temporal-sdk/src/main/java/io/temporal/internal/sync/SyncWorkflow.java +++ b/temporal-sdk/src/main/java/io/temporal/internal/sync/SyncWorkflow.java @@ -161,9 +161,6 @@ public void handleUpdate( // Skip validator on replay if (!callbacks.isReplaying()) { try { - // TODO(https://github.com/temporalio/sdk-java/issues/1748) handleValidateUpdate - // should not just be run - // in a workflow thread workflowContext.setReadOnly(true); workflowProc.handleValidateUpdate(updateName, input, eventId, header); } catch (Exception e) { diff --git a/temporal-sdk/src/test/java/io/temporal/workflow/CommandInTheLastWorkflowTaskTest.java b/temporal-sdk/src/test/java/io/temporal/workflow/CommandInTheLastWorkflowTaskTest.java new file mode 100644 index 000000000..5cf9bb651 --- /dev/null +++ b/temporal-sdk/src/test/java/io/temporal/workflow/CommandInTheLastWorkflowTaskTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved. + * + * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Modifications copyright (C) 2017 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this material except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.temporal.workflow; + +import static org.junit.Assert.assertEquals; + +import io.temporal.testing.internal.SDKTestWorkflowRule; +import io.temporal.workflow.shared.TestActivities; +import io.temporal.workflow.shared.TestWorkflows; +import java.util.Objects; +import org.junit.Rule; +import org.junit.Test; + +public class CommandInTheLastWorkflowTaskTest { + @Rule + public SDKTestWorkflowRule testWorkflowRule = + SDKTestWorkflowRule.newBuilder() + .setWorkflowTypes(TestWorkflowImpl.class) + .setActivityImplementations(new TestActivities.TestActivitiesImpl()) + .build(); + + @Test + public void testCommandInTheLastWorkflowTask() { + TestWorkflows.TestWorkflowReturnString client = + testWorkflowRule.newWorkflowStub(TestWorkflows.TestWorkflowReturnString.class); + assertEquals("done", client.execute()); + } + + public static class TestWorkflowImpl implements TestWorkflows.TestWorkflowReturnString { + + @Override + public String execute() { + Async.procedure( + () -> { + Workflow.mutableSideEffect( + "id1", Integer.class, (o, n) -> Objects.equals(n, o), () -> 0); + }); + + return "done"; + } + } +} diff --git a/temporal-sdk/src/test/java/io/temporal/workflow/activityTests/ActivityInTheLastWorkflowTaskTest.java b/temporal-sdk/src/test/java/io/temporal/workflow/activityTests/ActivityInTheLastWorkflowTaskTest.java new file mode 100644 index 000000000..aa6d101fc --- /dev/null +++ b/temporal-sdk/src/test/java/io/temporal/workflow/activityTests/ActivityInTheLastWorkflowTaskTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved. + * + * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Modifications copyright (C) 2017 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this material except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.temporal.workflow.activityTests; + +import static org.junit.Assert.assertEquals; + +import io.temporal.activity.ActivityOptions; +import io.temporal.testing.internal.SDKTestWorkflowRule; +import io.temporal.workflow.Async; +import io.temporal.workflow.Workflow; +import io.temporal.workflow.shared.TestActivities; +import io.temporal.workflow.shared.TestWorkflows; +import java.time.Duration; +import org.junit.Rule; +import org.junit.Test; + +public class ActivityInTheLastWorkflowTaskTest { + @Rule + public SDKTestWorkflowRule testWorkflowRule = + SDKTestWorkflowRule.newBuilder() + .setWorkflowTypes(TestWorkflowImpl.class) + .setActivityImplementations(new TestActivities.TestActivitiesImpl()) + .build(); + + @Test + public void testActivityInTheLastWorkflowTask() { + TestWorkflows.TestWorkflowReturnString client = + testWorkflowRule.newWorkflowStub(TestWorkflows.TestWorkflowReturnString.class); + assertEquals("done", client.execute()); + } + + public static class TestWorkflowImpl implements TestWorkflows.TestWorkflowReturnString { + + private final TestActivities.VariousTestActivities activities = + Workflow.newActivityStub( + TestActivities.VariousTestActivities.class, + ActivityOptions.newBuilder() + .setScheduleToCloseTimeout(Duration.ofSeconds(200)) + .build()); + + @Override + public String execute() { + Async.procedure(activities::sleepActivity, (long) 1000, 0); + return "done"; + } + } +} diff --git a/temporal-sdk/src/test/java/io/temporal/workflow/activityTests/LocalActivityInTheLastWorkflowTaskTest.java b/temporal-sdk/src/test/java/io/temporal/workflow/activityTests/LocalActivityInTheLastWorkflowTaskTest.java new file mode 100644 index 000000000..66b0db840 --- /dev/null +++ b/temporal-sdk/src/test/java/io/temporal/workflow/activityTests/LocalActivityInTheLastWorkflowTaskTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved. + * + * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Modifications copyright (C) 2017 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this material except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.temporal.workflow.activityTests; + +import static org.junit.Assert.assertEquals; + +import io.temporal.activity.LocalActivityOptions; +import io.temporal.testing.internal.SDKTestWorkflowRule; +import io.temporal.workflow.Async; +import io.temporal.workflow.Workflow; +import io.temporal.workflow.shared.TestActivities; +import io.temporal.workflow.shared.TestWorkflows; +import java.time.Duration; +import org.junit.Rule; +import org.junit.Test; + +public class LocalActivityInTheLastWorkflowTaskTest { + @Rule + public SDKTestWorkflowRule testWorkflowRule = + SDKTestWorkflowRule.newBuilder() + .setWorkflowTypes(TestWorkflowImpl.class) + .setActivityImplementations(new TestActivities.TestActivitiesImpl()) + .build(); + + @Test + public void testLocalActivityInTheLastWorkflowTask() { + TestWorkflows.TestWorkflowReturnString client = + testWorkflowRule.newWorkflowStub(TestWorkflows.TestWorkflowReturnString.class); + assertEquals("done", client.execute()); + } + + public static class TestWorkflowImpl implements TestWorkflows.TestWorkflowReturnString { + + private final TestActivities.VariousTestActivities activities = + Workflow.newLocalActivityStub( + TestActivities.VariousTestActivities.class, + LocalActivityOptions.newBuilder() + .setScheduleToCloseTimeout(Duration.ofSeconds(200)) + .build()); + + @Override + public String execute() { + Async.procedure(activities::sleepActivity, (long) 1000, 0); + return "done"; + } + } +} diff --git a/temporal-sdk/src/test/java/io/temporal/workflow/shared/TestWorkflows.java b/temporal-sdk/src/test/java/io/temporal/workflow/shared/TestWorkflows.java index ba3853261..8adbd04be 100644 --- a/temporal-sdk/src/test/java/io/temporal/workflow/shared/TestWorkflows.java +++ b/temporal-sdk/src/test/java/io/temporal/workflow/shared/TestWorkflows.java @@ -177,6 +177,16 @@ public interface QueryableWorkflow { void mySignal(String value); } + @WorkflowInterface + public interface SimpleWorkflowWithUpdate { + + @WorkflowMethod + String execute(); + + @UpdateMethod + String update(String value); + } + @WorkflowInterface public interface WorkflowWithUpdate { diff --git a/temporal-sdk/src/test/java/io/temporal/workflow/signalTests/SignalWithLocalActivityInTheLastWorkflowTaskTest.java b/temporal-sdk/src/test/java/io/temporal/workflow/signalTests/SignalWithLocalActivityInTheLastWorkflowTaskTest.java new file mode 100644 index 000000000..069935029 --- /dev/null +++ b/temporal-sdk/src/test/java/io/temporal/workflow/signalTests/SignalWithLocalActivityInTheLastWorkflowTaskTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved. + * + * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Modifications copyright (C) 2017 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this material except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.temporal.workflow.signalTests; + +import static org.junit.Assert.assertEquals; + +import io.temporal.activity.LocalActivityOptions; +import io.temporal.client.WorkflowStub; +import io.temporal.testing.internal.SDKTestWorkflowRule; +import io.temporal.workflow.Workflow; +import io.temporal.workflow.shared.TestActivities; +import io.temporal.workflow.shared.TestWorkflows; +import java.time.Duration; +import org.junit.Rule; +import org.junit.Test; + +public class SignalWithLocalActivityInTheLastWorkflowTaskTest { + @Rule + public SDKTestWorkflowRule testWorkflowRule = + SDKTestWorkflowRule.newBuilder() + .setWorkflowTypes(TestSignalWorkflowImpl.class) + .setActivityImplementations(new TestActivities.TestActivitiesImpl()) + .build(); + + @Test + public void testSignalWithLocalActivityInTheLastWorkflowTask() { + TestWorkflows.TestSignaledWorkflow client = + testWorkflowRule.newWorkflowStub(TestWorkflows.TestSignaledWorkflow.class); + WorkflowStub.fromTyped(client) + .signalWithStart("testSignal", new String[] {"signalValue"}, new String[] {}); + assertEquals("done", client.execute()); + } + + public static class TestSignalWorkflowImpl implements TestWorkflows.TestSignaledWorkflow { + + private final TestActivities.VariousTestActivities activities = + Workflow.newLocalActivityStub( + TestActivities.VariousTestActivities.class, + LocalActivityOptions.newBuilder() + .setScheduleToCloseTimeout(Duration.ofSeconds(200)) + .build()); + + @Override + public String execute() { + return "done"; + } + + @Override + public void signal(String arg) { + activities.sleepActivity(1000, 0); + } + } +} diff --git a/temporal-sdk/src/test/java/io/temporal/workflow/updateTest/UpdateWithLocalActivityInTheLastWorkflowTaskTest.java b/temporal-sdk/src/test/java/io/temporal/workflow/updateTest/UpdateWithLocalActivityInTheLastWorkflowTaskTest.java new file mode 100644 index 000000000..f3c084c2a --- /dev/null +++ b/temporal-sdk/src/test/java/io/temporal/workflow/updateTest/UpdateWithLocalActivityInTheLastWorkflowTaskTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved. + * + * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Modifications copyright (C) 2017 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this material except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.temporal.workflow.updateTest; + +import static org.junit.Assert.assertEquals; + +import io.temporal.activity.LocalActivityOptions; +import io.temporal.client.WorkflowStub; +import io.temporal.testing.internal.SDKTestWorkflowRule; +import io.temporal.workflow.Workflow; +import io.temporal.workflow.shared.TestActivities; +import io.temporal.workflow.shared.TestWorkflows; +import java.time.Duration; +import org.junit.Rule; +import org.junit.Test; + +public class UpdateWithLocalActivityInTheLastWorkflowTaskTest { + @Rule + public SDKTestWorkflowRule testWorkflowRule = + SDKTestWorkflowRule.newBuilder() + .setWorkflowTypes(TestSimpleWorkflowWithUpdateImpl.class) + .setActivityImplementations(new TestActivities.TestActivitiesImpl()) + .build(); + + @Test + public void testUpdateWithLocalActivityInTheLastWorkflowTask() throws InterruptedException { + TestWorkflows.SimpleWorkflowWithUpdate client = + testWorkflowRule.newWorkflowStub(TestWorkflows.SimpleWorkflowWithUpdate.class); + + WorkflowStub.fromTyped(client).start(); + Thread asyncUpdate = + new Thread( + () -> { + try { + System.out.println("Sending update"); + client.update("Update"); + } catch (Exception e) { + } + }); + asyncUpdate.start(); + assertEquals("done", client.execute()); + asyncUpdate.interrupt(); + } + + public static class TestSimpleWorkflowWithUpdateImpl + implements TestWorkflows.SimpleWorkflowWithUpdate { + Boolean finish = false; + + private final TestActivities.VariousTestActivities activities = + Workflow.newLocalActivityStub( + TestActivities.VariousTestActivities.class, + LocalActivityOptions.newBuilder() + .setScheduleToCloseTimeout(Duration.ofSeconds(200)) + .build()); + + @Override + public String execute() { + Workflow.await(() -> finish); + return "done"; + } + + @Override + public String update(String value) { + finish = true; + activities.sleepActivity(1000, 0); + return "update"; + } + } +}