From b0d54fe2688b8bdbbe2e0186ba95f17eeb2b36ea Mon Sep 17 00:00:00 2001 From: Spencer Judge Date: Thu, 20 Feb 2025 12:07:37 -0800 Subject: [PATCH 1/3] Add sdk name/version to completions when unset or changed --- .../java/io/temporal/internal/Config.java | 2 +- .../replay/ReplayWorkflowRunTaskHandler.java | 28 ++++-- .../replay/ReplayWorkflowTaskHandler.java | 16 ++-- .../internal/replay/WorkflowTaskResult.java | 32 ++++++- .../statemachines/WorkflowStateMachines.java | 30 ++++++ ...orkflowRunTaskHandlerTaskHandlerTests.java | 29 ++++++ .../statemachines/TestHistoryBuilder.java | 4 + .../WorkflowStateMachinesTest.java | 93 +++++++++++++++++++ ...dRightBeforeWorkflowTaskHeartbeatTest.java | 2 +- .../serviceclient/ChannelManager.java | 4 +- .../io/temporal/serviceclient/Version.java | 5 + 11 files changed, 222 insertions(+), 23 deletions(-) create mode 100644 temporal-sdk/src/test/java/io/temporal/internal/statemachines/WorkflowStateMachinesTest.java diff --git a/temporal-sdk/src/main/java/io/temporal/internal/Config.java b/temporal-sdk/src/main/java/io/temporal/internal/Config.java index 08b04d4e7f..d0699968a6 100644 --- a/temporal-sdk/src/main/java/io/temporal/internal/Config.java +++ b/temporal-sdk/src/main/java/io/temporal/internal/Config.java @@ -24,7 +24,7 @@ public final class Config { private Config() {} /** Force new workflow task after workflow task timeout multiplied by this coefficient. */ - public static final double WORKFLOW_TAK_HEARTBEAT_COEFFICIENT = 4d / 5d; + public static final double WORKFLOW_TASK_HEARTBEAT_COEFFICIENT = 4d / 5d; /** * Limit how many eager activities can be requested by the SDK in one workflow task completion 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 0fa1485c0c..32308d15ea 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 @@ -146,7 +146,7 @@ public WorkflowTaskResult handleWorkflowTask( Deadline.after( (long) (Durations.toNanos(startedEvent.getWorkflowTaskTimeout()) - * Config.WORKFLOW_TAK_HEARTBEAT_COEFFICIENT), + * Config.WORKFLOW_TASK_HEARTBEAT_COEFFICIENT), TimeUnit.NANOSECONDS); if (workflowTask.getPreviousStartedEventId() @@ -180,15 +180,23 @@ public WorkflowTaskResult handleWorkflowTask( throw context.getWorkflowTaskFailure(); } Map queryResults = executeQueries(workflowTask.getQueriesMap()); - return WorkflowTaskResult.newBuilder() - .setCommands(commands) - .setMessages(messages) - .setQueryResults(queryResults) - .setFinalCommand(context.isWorkflowMethodCompleted()) - .setForceWorkflowTask(localActivityTaskCount > 0 && !context.isWorkflowMethodCompleted()) - .setNonfirstLocalActivityAttempts(localActivityMeteringHelper.getNonfirstAttempts()) - .setSdkFlags(newSdkFlags) - .build(); + WorkflowTaskResult.Builder result = + WorkflowTaskResult.newBuilder() + .setCommands(commands) + .setMessages(messages) + .setQueryResults(queryResults) + .setFinalCommand(context.isWorkflowMethodCompleted()) + .setForceWorkflowTask( + localActivityTaskCount > 0 && !context.isWorkflowMethodCompleted()) + .setNonfirstLocalActivityAttempts(localActivityMeteringHelper.getNonfirstAttempts()) + .setSdkFlags(newSdkFlags); + if (workflowStateMachines.sdkNameToWrite() != null) { + result.setWriteSdkName(workflowStateMachines.sdkNameToWrite()); + } + if (workflowStateMachines.sdkVersionToWrite() != null) { + result.setWriteSdkVersion(workflowStateMachines.sdkVersionToWrite()); + } + return result.build(); } finally { lock.unlock(); } diff --git a/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowTaskHandler.java b/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowTaskHandler.java index 7e381199f2..7b358a7be4 100644 --- a/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowTaskHandler.java +++ b/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowTaskHandler.java @@ -247,12 +247,16 @@ private Result createCompletedWFTRequest( } completedRequest.setStickyAttributes(attributes); } - if (!result.getSdkFlags().isEmpty()) { - completedRequest = - completedRequest.setSdkMetadata( - WorkflowTaskCompletedMetadata.newBuilder() - .addAllLangUsedFlags(result.getSdkFlags()) - .build()); + List sdkFlags = result.getSdkFlags(); + String writeSdkName = result.getWriteSdkName(); + String writeSdkVersion = result.getWriteSdkVersion(); + if (!sdkFlags.isEmpty() || writeSdkName != null || writeSdkVersion != null) { + completedRequest.setSdkMetadata( + WorkflowTaskCompletedMetadata.newBuilder() + .addAllLangUsedFlags(sdkFlags) + .setSdkName(writeSdkName) + .setSdkVersion(writeSdkVersion) + .build()); } return new Result( workflowType, diff --git a/temporal-sdk/src/main/java/io/temporal/internal/replay/WorkflowTaskResult.java b/temporal-sdk/src/main/java/io/temporal/internal/replay/WorkflowTaskResult.java index bb07ee3fe6..31f6848832 100644 --- a/temporal-sdk/src/main/java/io/temporal/internal/replay/WorkflowTaskResult.java +++ b/temporal-sdk/src/main/java/io/temporal/internal/replay/WorkflowTaskResult.java @@ -41,6 +41,8 @@ public static final class Builder { private boolean forceWorkflowTask; private int nonfirstLocalActivityAttempts; private List sdkFlags; + private String writeSdkName; + private String writeSdkVersion; public Builder setCommands(List commands) { this.commands = commands; @@ -77,6 +79,16 @@ public Builder setSdkFlags(List sdkFlags) { return this; } + public Builder setWriteSdkName(String writeSdkName) { + this.writeSdkName = writeSdkName; + return this; + } + + public Builder setWriteSdkVersion(String writeSdkVersion) { + this.writeSdkVersion = writeSdkVersion; + return this; + } + public WorkflowTaskResult build() { return new WorkflowTaskResult( commands == null ? Collections.emptyList() : commands, @@ -85,7 +97,9 @@ public WorkflowTaskResult build() { finalCommand, forceWorkflowTask, nonfirstLocalActivityAttempts, - sdkFlags == null ? Collections.emptyList() : sdkFlags); + sdkFlags == null ? Collections.emptyList() : sdkFlags, + writeSdkName, + writeSdkVersion); } } @@ -96,6 +110,8 @@ public WorkflowTaskResult build() { private final boolean forceWorkflowTask; private final int nonfirstLocalActivityAttempts; private final List sdkFlags; + private final String writeSdkName; + private final String writeSdkVersion; private WorkflowTaskResult( List commands, @@ -104,7 +120,9 @@ private WorkflowTaskResult( boolean finalCommand, boolean forceWorkflowTask, int nonfirstLocalActivityAttempts, - List sdkFlags) { + List sdkFlags, + String writeSdkName, + String writeSdkVersion) { this.commands = commands; this.messages = messages; this.nonfirstLocalActivityAttempts = nonfirstLocalActivityAttempts; @@ -115,6 +133,8 @@ private WorkflowTaskResult( this.finalCommand = finalCommand; this.forceWorkflowTask = forceWorkflowTask; this.sdkFlags = sdkFlags; + this.writeSdkName = writeSdkName; + this.writeSdkVersion = writeSdkVersion; } public List getCommands() { @@ -145,4 +165,12 @@ public int getNonfirstLocalActivityAttempts() { public List getSdkFlags() { return sdkFlags; } + + public String getWriteSdkName() { + return writeSdkName; + } + + public String getWriteSdkVersion() { + return writeSdkVersion; + } } diff --git a/temporal-sdk/src/main/java/io/temporal/internal/statemachines/WorkflowStateMachines.java b/temporal-sdk/src/main/java/io/temporal/internal/statemachines/WorkflowStateMachines.java index 5742bfecb2..fb8651f636 100644 --- a/temporal-sdk/src/main/java/io/temporal/internal/statemachines/WorkflowStateMachines.java +++ b/temporal-sdk/src/main/java/io/temporal/internal/statemachines/WorkflowStateMachines.java @@ -43,11 +43,13 @@ import io.temporal.internal.history.VersionMarkerUtils; import io.temporal.internal.sync.WorkflowThread; import io.temporal.internal.worker.LocalActivityResult; +import io.temporal.serviceclient.Version; import io.temporal.worker.NonDeterministicException; import io.temporal.workflow.ChildWorkflowCancellationType; import io.temporal.workflow.Functions; import java.nio.charset.StandardCharsets; import java.util.*; +import javax.annotation.Nonnull; import javax.annotation.Nullable; public final class WorkflowStateMachines { @@ -179,6 +181,8 @@ enum HandleEventStatus { private final Set acceptedUpdates = new HashSet<>(); private final SdkFlags flags; + @Nonnull private String lastSeenSdkName = ""; + @Nonnull private String lastSeenSdkVersion = ""; public WorkflowStateMachines( StatesMachinesCallback callbacks, GetSystemInfoResponse.Capabilities capabilities) { @@ -384,6 +388,12 @@ private void handleSingleEventLookahead(HistoryEvent event) { } flags.setSdkFlag(sdkFlag); } + if (!Strings.isNullOrEmpty(completedEvent.getSdkMetadata().getSdkName())) { + lastSeenSdkName = completedEvent.getSdkMetadata().getSdkName(); + } + if (!Strings.isNullOrEmpty(completedEvent.getSdkMetadata().getSdkVersion())) { + lastSeenSdkVersion = completedEvent.getSdkMetadata().getSdkVersion(); + } // Remove any finished update protocol state machines. We can't remove them on an event like // other state machines because a rejected update produces no event in history. protocolStateMachines.entrySet().removeIf(entry -> entry.getValue().isFinalState()); @@ -675,6 +685,26 @@ public EnumSet takeNewSdkFlags() { return flags.takeNewSdkFlags(); } + /** + * @return If we need to write the SDK name upon WFT completion, return it + */ + public String sdkNameToWrite() { + if (!lastSeenSdkName.equals(Version.SDK_NAME)) { + return Version.SDK_NAME; + } + return null; + } + + /** + * @return If we need to write the SDK version upon WFT completion, return it + */ + public String sdkVersionToWrite() { + if (!lastSeenSdkVersion.equals(Version.LIBRARY_VERSION)) { + return Version.LIBRARY_VERSION; + } + return null; + } + private void prepareCommands() { if (preparing) { return; diff --git a/temporal-sdk/src/test/java/io/temporal/internal/replay/ReplayWorkflowRunTaskHandlerTaskHandlerTests.java b/temporal-sdk/src/test/java/io/temporal/internal/replay/ReplayWorkflowRunTaskHandlerTaskHandlerTests.java index f4ffd57c3a..a3b39217df 100644 --- a/temporal-sdk/src/test/java/io/temporal/internal/replay/ReplayWorkflowRunTaskHandlerTaskHandlerTests.java +++ b/temporal-sdk/src/test/java/io/temporal/internal/replay/ReplayWorkflowRunTaskHandlerTaskHandlerTests.java @@ -43,6 +43,7 @@ import io.temporal.internal.worker.WorkflowExecutorCache; import io.temporal.internal.worker.WorkflowRunLockManager; import io.temporal.internal.worker.WorkflowTaskHandler; +import io.temporal.serviceclient.Version; import io.temporal.serviceclient.WorkflowServiceStubs; import io.temporal.testUtils.HistoryUtils; import io.temporal.testing.internal.SDKTestWorkflowRule; @@ -208,6 +209,34 @@ public void ifStickyExecutionAttributesAreSetThenWorkflowsAreCached() throws Thr assertEquals(Durations.fromSeconds(5), attributes.getScheduleToStartTimeout()); } + @Test + public void setsSdkNameAndVersionIfNotSetInHistory() throws Throwable { + assumeFalse("skipping for docker tests", SDKTestWorkflowRule.useExternalService); + + WorkflowExecutorCache cache = + new WorkflowExecutorCache(10, new WorkflowRunLockManager(), new NoopScope()); + WorkflowTaskHandler taskHandler = + new ReplayWorkflowTaskHandler( + "namespace", + setUpMockWorkflowFactory(), + cache, + SingleWorkerOptions.newBuilder().build(), + InternalUtils.createStickyTaskQueue("sticky", "taskQueue"), + Duration.ofSeconds(5), + testWorkflowRule.getWorkflowServiceStubs(), + null); + + PollWorkflowTaskQueueResponse workflowTask = + HistoryUtils.generateWorkflowTaskWithInitialHistory(); + + WorkflowTaskHandler.Result result = taskHandler.handleWorkflowTask(workflowTask); + + assertTrue(result.isCompletionCommand()); + assertEquals(Version.SDK_NAME, result.getTaskCompleted().getSdkMetadata().getSdkName()); + assertEquals( + Version.LIBRARY_VERSION, result.getTaskCompleted().getSdkMetadata().getSdkVersion()); + } + private ReplayWorkflowFactory setUpMockWorkflowFactory() throws Throwable { ReplayWorkflow mockWorkflow = mock(ReplayWorkflow.class); ReplayWorkflowFactory mockFactory = mock(ReplayWorkflowFactory.class); diff --git a/temporal-sdk/src/test/java/io/temporal/internal/statemachines/TestHistoryBuilder.java b/temporal-sdk/src/test/java/io/temporal/internal/statemachines/TestHistoryBuilder.java index dc72e7abc9..fd3186dc9b 100644 --- a/temporal-sdk/src/test/java/io/temporal/internal/statemachines/TestHistoryBuilder.java +++ b/temporal-sdk/src/test/java/io/temporal/internal/statemachines/TestHistoryBuilder.java @@ -616,6 +616,10 @@ public long getPreviousStartedEventId() { return previousStartedEventId; } + public long getWorkflowTaskScheduledEventId() { + return workflowTaskScheduledEventId; + } + public long getWorkflowTaskStartedEventId() { return workflowTaskScheduledEventId + 1; } diff --git a/temporal-sdk/src/test/java/io/temporal/internal/statemachines/WorkflowStateMachinesTest.java b/temporal-sdk/src/test/java/io/temporal/internal/statemachines/WorkflowStateMachinesTest.java new file mode 100644 index 0000000000..bd12e4477b --- /dev/null +++ b/temporal-sdk/src/test/java/io/temporal/internal/statemachines/WorkflowStateMachinesTest.java @@ -0,0 +1,93 @@ +/* + * 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.internal.statemachines; + +import static org.junit.Assert.assertEquals; + +import io.temporal.api.enums.v1.EventType; +import io.temporal.api.history.v1.WorkflowTaskCompletedEventAttributes; +import io.temporal.api.sdk.v1.WorkflowTaskCompletedMetadata; +import io.temporal.serviceclient.Version; +import java.util.Optional; +import org.junit.Test; + +public class WorkflowStateMachinesTest { + private WorkflowStateMachines stateMachines; + + private WorkflowStateMachines newStateMachines(TestEntityManagerListenerBase listener) { + return new WorkflowStateMachines(listener, m -> {}); + } + + private class TestActivityListener extends TestEntityManagerListenerBase { + @Override + public void buildWorkflow(AsyncWorkflowBuilder builder) { + builder.add(v -> stateMachines.completeWorkflow(Optional.empty())); + } + } + + private void sdkNameAndVersionTest( + String inputSdkVersion, + String inputSdkName, + String expectedSdkName, + String expectedSdkVersion) { + TestHistoryBuilder h = new TestHistoryBuilder(); + TestEntityManagerListenerBase listener = new TestActivityListener(); + stateMachines = newStateMachines(listener); + + h.add(EventType.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED); + h.addWorkflowTaskScheduledAndStarted(); + h.add( + EventType.EVENT_TYPE_WORKFLOW_TASK_COMPLETED, + WorkflowTaskCompletedEventAttributes.newBuilder() + .setScheduledEventId(h.getWorkflowTaskScheduledEventId()) + .setSdkMetadata( + WorkflowTaskCompletedMetadata.newBuilder() + .setSdkVersion(inputSdkVersion) + .setSdkName(inputSdkName))); + h.addWorkflowTaskScheduledAndStarted(); + assertEquals(2, h.getWorkflowTaskCount()); + + h.handleWorkflowTaskTakeCommands(stateMachines, 2); + + assertEquals(expectedSdkName, stateMachines.sdkNameToWrite()); + assertEquals(expectedSdkVersion, stateMachines.sdkVersionToWrite()); + } + + @Test + public void testWritesSdkNameAndVersionWhenDifferent() { + sdkNameAndVersionTest("hi", "skflajk", Version.SDK_NAME, Version.LIBRARY_VERSION); + } + + @Test + public void doesNotWriteSdkNameAndVersionWhenSame() { + sdkNameAndVersionTest(Version.LIBRARY_VERSION, Version.SDK_NAME, null, null); + } + + @Test + public void writesOnlyNameIfChanged() { + sdkNameAndVersionTest(Version.LIBRARY_VERSION, "sakflasjklf", Version.SDK_NAME, null); + } + + @Test + public void writesOnlyVersionIfChanged() { + sdkNameAndVersionTest("safklasjf", Version.SDK_NAME, null, Version.LIBRARY_VERSION); + } +} diff --git a/temporal-sdk/src/test/java/io/temporal/workflow/activityTests/LocalActivityGettingScheduledRightBeforeWorkflowTaskHeartbeatTest.java b/temporal-sdk/src/test/java/io/temporal/workflow/activityTests/LocalActivityGettingScheduledRightBeforeWorkflowTaskHeartbeatTest.java index 189e3b01b5..84739a4f3d 100644 --- a/temporal-sdk/src/test/java/io/temporal/workflow/activityTests/LocalActivityGettingScheduledRightBeforeWorkflowTaskHeartbeatTest.java +++ b/temporal-sdk/src/test/java/io/temporal/workflow/activityTests/LocalActivityGettingScheduledRightBeforeWorkflowTaskHeartbeatTest.java @@ -88,7 +88,7 @@ public String execute(String taskQueue) { VariousTestActivities.class, SDKTestOptions.newLocalActivityOptions()); long firstLocalActivityDurationMs = - (long) (WORKFLOW_TASK_TIMEOUT.toMillis() * Config.WORKFLOW_TAK_HEARTBEAT_COEFFICIENT) + (long) (WORKFLOW_TASK_TIMEOUT.toMillis() * Config.WORKFLOW_TASK_HEARTBEAT_COEFFICIENT) - SLEEP_DURATION.toMillis() / 2; localActivities.sleepActivity(firstLocalActivityDurationMs, 0); diff --git a/temporal-serviceclient/src/main/java/io/temporal/serviceclient/ChannelManager.java b/temporal-serviceclient/src/main/java/io/temporal/serviceclient/ChannelManager.java index 9c16d0d0ec..0b87380240 100644 --- a/temporal-serviceclient/src/main/java/io/temporal/serviceclient/ChannelManager.java +++ b/temporal-serviceclient/src/main/java/io/temporal/serviceclient/ChannelManager.java @@ -79,8 +79,6 @@ final class ChannelManager { private static final Metadata.Key CLOUD_VERSION_HEADER_KEY = Metadata.Key.of("temporal-cloud-api-version", Metadata.ASCII_STRING_MARSHALLER); - private static final String CLIENT_NAME_HEADER_VALUE = "temporal-java"; - private final ServiceStubsOptions options; private final AtomicBoolean shutdownRequested = new AtomicBoolean(); @@ -169,7 +167,7 @@ private Channel applyHeadStandardInterceptors(Channel channel) { headers.merge(options.getHeaders()); headers.put(LIBRARY_VERSION_HEADER_KEY, Version.LIBRARY_VERSION); headers.put(SUPPORTED_SERVER_VERSIONS_HEADER_KEY, Version.SUPPORTED_SERVER_VERSIONS); - headers.put(CLIENT_NAME_HEADER_KEY, CLIENT_NAME_HEADER_VALUE); + headers.put(CLIENT_NAME_HEADER_KEY, Version.SDK_NAME); if (options instanceof CloudServiceStubsOptions) { String version = ((CloudServiceStubsOptions) options).getVersion(); if (version != null) { diff --git a/temporal-serviceclient/src/main/java/io/temporal/serviceclient/Version.java b/temporal-serviceclient/src/main/java/io/temporal/serviceclient/Version.java index f12611aef2..01554aea51 100644 --- a/temporal-serviceclient/src/main/java/io/temporal/serviceclient/Version.java +++ b/temporal-serviceclient/src/main/java/io/temporal/serviceclient/Version.java @@ -41,6 +41,11 @@ public class Version { */ public static final String LIBRARY_VERSION; + /** + * The named used to represent this SDK as client identities as well as worker task completions. + */ + public static final String SDK_NAME = "temporal-java"; + /** * Supported server versions defines a semver range of server versions that client is compatible * with. From 55e3bb9d5e2bcf3c5afceaeb63cb4e9b46505b1e Mon Sep 17 00:00:00 2001 From: Spencer Judge Date: Thu, 20 Feb 2025 15:09:44 -0800 Subject: [PATCH 2/3] Classic Java verbosity --- .../replay/ReplayWorkflowTaskHandler.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowTaskHandler.java b/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowTaskHandler.java index 7b358a7be4..2b593024d6 100644 --- a/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowTaskHandler.java +++ b/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowTaskHandler.java @@ -251,12 +251,17 @@ private Result createCompletedWFTRequest( String writeSdkName = result.getWriteSdkName(); String writeSdkVersion = result.getWriteSdkVersion(); if (!sdkFlags.isEmpty() || writeSdkName != null || writeSdkVersion != null) { - completedRequest.setSdkMetadata( - WorkflowTaskCompletedMetadata.newBuilder() - .addAllLangUsedFlags(sdkFlags) - .setSdkName(writeSdkName) - .setSdkVersion(writeSdkVersion) - .build()); + WorkflowTaskCompletedMetadata.Builder md = WorkflowTaskCompletedMetadata.newBuilder(); + if (!sdkFlags.isEmpty()) { + md.addAllLangUsedFlags(sdkFlags); + } + if (writeSdkName != null) { + md.setSdkName(writeSdkName); + } + if (writeSdkVersion != null) { + md.setSdkVersion(writeSdkVersion); + } + completedRequest.setSdkMetadata(md.build()); } return new Result( workflowType, From 077ffe61d82acfd4b08ba14251d0290b1d60db23 Mon Sep 17 00:00:00 2001 From: Spencer Judge Date: Fri, 21 Feb 2025 09:25:06 -0800 Subject: [PATCH 3/3] Add integ test --- .../workflow/WritesSDKNameVersionTest.java | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 temporal-sdk/src/test/java/io/temporal/workflow/WritesSDKNameVersionTest.java diff --git a/temporal-sdk/src/test/java/io/temporal/workflow/WritesSDKNameVersionTest.java b/temporal-sdk/src/test/java/io/temporal/workflow/WritesSDKNameVersionTest.java new file mode 100644 index 0000000000..861f662d8d --- /dev/null +++ b/temporal-sdk/src/test/java/io/temporal/workflow/WritesSDKNameVersionTest.java @@ -0,0 +1,106 @@ +/* + * 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 io.temporal.api.enums.v1.EventType; +import io.temporal.api.history.v1.HistoryEvent; +import io.temporal.client.WorkflowOptions; +import io.temporal.client.WorkflowStub; +import io.temporal.serviceclient.Version; +import io.temporal.testing.internal.SDKTestWorkflowRule; +import io.temporal.workflow.shared.TestWorkflows.TestWorkflow1; +import java.time.Duration; +import java.util.List; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; + +public class WritesSDKNameVersionTest { + + private static boolean hasFailedWFT = false; + + @Rule + public SDKTestWorkflowRule testWorkflowRule = + SDKTestWorkflowRule.newBuilder() + .setWorkflowTypes(TestWorkflowTaskFailureBackoff.class) + .build(); + + @Test + public void writesSdkNameAndVersion() { + WorkflowOptions options = + WorkflowOptions.newBuilder() + .setWorkflowRunTimeout(Duration.ofSeconds(10)) + .setWorkflowTaskTimeout(Duration.ofSeconds(1)) + .setTaskQueue(testWorkflowRule.getTaskQueue()) + .build(); + + TestWorkflow1 workflowStub = + testWorkflowRule.getWorkflowClient().newWorkflowStub(TestWorkflow1.class, options); + String result = workflowStub.execute(testWorkflowRule.getTaskQueue()); + Assert.assertEquals("result1", result); + + List completedEvents = + testWorkflowRule.getHistoryEvents( + WorkflowStub.fromTyped(workflowStub).getExecution().getWorkflowId(), + EventType.EVENT_TYPE_WORKFLOW_TASK_COMPLETED); + Assert.assertEquals( + Version.SDK_NAME, + completedEvents + .get(0) + .getWorkflowTaskCompletedEventAttributes() + .getSdkMetadata() + .getSdkName()); + Assert.assertEquals( + Version.LIBRARY_VERSION, + completedEvents + .get(0) + .getWorkflowTaskCompletedEventAttributes() + .getSdkMetadata() + .getSdkVersion()); + Assert.assertEquals( + "", + completedEvents + .get(1) + .getWorkflowTaskCompletedEventAttributes() + .getSdkMetadata() + .getSdkName()); + Assert.assertEquals( + "", + completedEvents + .get(1) + .getWorkflowTaskCompletedEventAttributes() + .getSdkMetadata() + .getSdkVersion()); + } + + public static class TestWorkflowTaskFailureBackoff implements TestWorkflow1 { + @Override + public String execute(String taskQueue) { + // Complete one wft first + Workflow.sleep(1); + if (!hasFailedWFT) { + hasFailedWFT = true; + throw new Error("fail"); + } + return "result1"; + } + } +}