Giter Club home page Giter Club logo

sdk-java's Introduction

Temporal Java SDK Build status Coverage Status

Temporal is a Workflow-as-Code platform for building and operating resilient applications using developer-friendly primitives, instead of constantly fighting your infrastructure.

The Java SDK is the framework for authoring Workflows and Activities in Java. (For other languages, see Temporal SDKs.)

Java SDK:

Temporal:

Supported Java runtimes

Build configuration

Find the latest release of the Temporal Java SDK at maven central.

Add temporal-sdk as a dependency to your pom.xml:

<dependency>
  <groupId>io.temporal</groupId>
  <artifactId>temporal-sdk</artifactId>
  <version>N.N.N</version>
</dependency>

or to build.gradle:

compile group: 'io.temporal', name: 'temporal-sdk', version: 'N.N.N'

Contributing

We'd love your help in improving the Temporal Java SDK. Please review our contribution guidelines.

Snapshot release

We also publish snapshot releases during SDK development often under the version 1.x.0-SNAPSHOT. This allows users to test out new SDK features before an official SDK release.

Snapshot releases Find the latest snapsphot release.

To add Sonatype snapshot repository to your pom.xml:

<repositories>
    <repository>
        <id>oss-sonatype</id>
        <name>oss-sonatype</name>
        <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>

Or to build.gradle:

repositories {
    maven {
        url "https://oss.sonatype.org/content/repositories/snapshots/"
    }
  ...
}

Note: Snapshot releases are not official release and normal backwards compatibility, stability or support does not apply

License

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.

sdk-java's People

Contributors

alexshtin avatar andrewjdawson2016 avatar chronos-tachyon avatar cretz avatar dmetzgar avatar emrahs avatar flossypurse avatar greyteardrop avatar halakaraki avatar jackdawm avatar jeffschoner-stripe avatar mastermanu avatar mcbryde avatar meiliang86 avatar mfateev avatar mjameswh avatar mmcshane avatar nagl-stripe avatar nlremco avatar quinn-with-two-ns avatar sdwr98 avatar shawnhathaway avatar spikhalskiy avatar sullis avatar sushisource avatar tsurdilo avatar vancexu avatar vitarb avatar vkoby avatar wxing1292 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sdk-java's Issues

Add workflow deadlock detector

Currently if a workflow code by mistake uses Java synchronization primitives it deadlocks as dispatcher relies on cooperative multithreading. Here is an example stack trace that shows that one thread obtains lock (0x00000006c815ddb8) through synchronized method and blocks other thread from making progress.

The proposal is to implement deadlock detection feature to fail decision task with a clear message that Java locking primitives are prohibited inside the workflow code.

    public static void main(String[] args) {
        ""WorkflowStageProgressListener::stageCompleted" signal handler" #78 prio=5 os_prio=0 tid=0x0000560f1fe02800 nid=0x5e waiting for monitor entry [0x00007f142a435000]
        java.lang.Thread.State: BLOCKED (on object monitor)
        at com.somecompany.platform.cadence.workflows.onboarding.HiringWorkFlowImpl.transitionMainTaskToInProgress(HiringWorkFlowImpl.java:327)
        - waiting to lock <0x00000006c815ddb8> (a com.somecompany.platform.cadence.workflows.onboarding.HiringWorkFlowImpl)
        at com.somecompany.platform.cadence.workflows.onboarding.HiringWorkFlowImpl.stageCompleted(HiringWorkFlowImpl.java:282)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at com.uber.cadence.internal.sync.POJOWorkflowImplementationFactory$POJOWorkflowImplementation.processSignal(POJOWorkflowImplementationFactory.java:312)
        at com.uber.cadence.internal.sync.WorkflowRunnable.processSignal(WorkflowRunnable.java:65)
        at com.uber.cadence.internal.sync.SyncWorkflow.lambda$handleSignal$0(SyncWorkflow.java:106)
        at com.uber.cadence.internal.sync.SyncWorkflow$$Lambda$1231/1900852990.run(Unknown Source)
        at com.uber.cadence.internal.sync.CancellationScopeImpl.run(CancellationScopeImpl.java:102)
        at com.uber.cadence.internal.sync.WorkflowThreadImpl$RunnableWrapper.run(WorkflowThreadImpl.java:85)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
        ""WorkflowStageProgressListener::stageInProgress" signal handler" #77 prio=5 os_prio=0 tid=0x0000560f20a13000 nid=0x5d waiting on condition [0x00007f142a536000]
        java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
                - parking to wait for  <0x00000006c81a5550> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at com.uber.cadence.internal.sync.WorkflowThreadContext.yield(WorkflowThreadContext.java:76)
        at com.uber.cadence.internal.sync.WorkflowThreadImpl.yield(WorkflowThreadImpl.java:400)
        at com.uber.cadence.internal.sync.WorkflowThread.await(WorkflowThread.java:43)
        at com.uber.cadence.internal.sync.CompletablePromiseImpl.get(CompletablePromiseImpl.java:71)
        at com.uber.cadence.internal.sync.ActivityStubBase.execute(ActivityStubBase.java:42)
        at com.uber.cadence.internal.sync.ActivityStubImpl.execute(ActivityStubImpl.java:26)
        at com.uber.cadence.internal.sync.ActivityInvocationHandler.lambda$getActivityFunc$0(ActivityInvocationHandler.java:51)
        at com.uber.cadence.internal.sync.ActivityInvocationHandler$$Lambda$1192/803084529.apply(Unknown Source)
        at com.uber.cadence.internal.sync.ActivityInvocationHandlerBase.invoke(ActivityInvocationHandlerBase.java:76)
        at com.sun.proxy.$Proxy231.updateTaskStatus(Unknown Source)
        at com.somecompany.platform.cadence.workflows.onboarding.HiringWorkFlowImpl.transitionMainTaskToInProgress(HiringWorkFlowImpl.java:328)
        - locked <0x00000006c815ddb8> (a com.somecompany.platform.cadence.workflows.onboarding.HiringWorkFlowImpl)
        at com.somecompany.platform.cadence.workflows.onboarding.HiringWorkFlowImpl.stageInProgress(HiringWorkFlowImpl.java:317)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at com.uber.cadence.internal.sync.POJOWorkflowImplementationFactory$POJOWorkflowImplementation.processSignal(POJOWorkflowImplementationFactory.java:312)
        at com.uber.cadence.internal.sync.WorkflowRunnable.processSignal(WorkflowRunnable.java:65)
        at com.uber.cadence.internal.sync.SyncWorkflow.lambda$handleSignal$0(SyncWorkflow.java:106)
        at com.uber.cadence.internal.sync.SyncWorkflow$$Lambda$1231/1900852990.run(Unknown Source)
        at com.uber.cadence.internal.sync.CancellationScopeImpl.run(CancellationScopeImpl.java:102)
        at com.uber.cadence.internal.sync.WorkflowThreadImpl$RunnableWrapper.run(WorkflowThreadImpl.java:85)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)

Workflow.wrap and Activity.wrap fail on chained Throwable

Expected Behavior

Activity.wrap(new ActivityFailure(100, 1234,"", "234", RetryStatus.RETRY_STATUS_INTERNAL_SERVER_ERROR, "runId", new Throwable()))
should work

Actual Behavior

Fails with ClassCastException:

java.lang.RuntimeException: Failure processing local activity task.
	at io.temporal.internal.worker.LocalActivityWorker$TaskHandlerImpl.wrapFailure(LocalActivityWorker.java:229)
	at io.temporal.internal.worker.LocalActivityWorker$TaskHandlerImpl.wrapFailure(LocalActivityWorker.java:189)
	at io.temporal.internal.worker.PollTaskExecutor.lambda$process$0(PollTaskExecutor.java:79)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.ClassCastException: class java.lang.Throwable cannot be cast to class java.lang.Exception (java.lang.Throwable and java.lang.Exception are in module java.base of loader 'bootstrap')
	at io.temporal.internal.common.CheckedExceptionWrapper.unwrap(CheckedExceptionWrapper.java:94)
	at io.temporal.failure.FailureConverter.exceptionToFailure(FailureConverter.java:182)
	at io.temporal.failure.FailureConverter.exceptionToFailureNoUnwrapping(FailureConverter.java:201)
	at io.temporal.failure.FailureConverter.exceptionToFailure(FailureConverter.java:183)
	at io.temporal.internal.sync.POJOActivityTaskHandler.mapToActivityFailure(POJOActivityTaskHandler.java:128)
	at io.temporal.internal.sync.POJOActivityTaskHandler.access$400(POJOActivityTaskHandler.java:54)
	at io.temporal.internal.sync.POJOActivityTaskHandler$POJOLocalActivityImplementation.execute(POJOActivityTaskHandler.java:257)
	at io.temporal.internal.sync.POJOActivityTaskHandler.handle(POJOActivityTaskHandler.java:176)
	at io.temporal.internal.worker.LocalActivityWorker$TaskHandlerImpl.handleLocalActivity(LocalActivityWorker.java:270)
	at io.temporal.internal.worker.LocalActivityWorker$TaskHandlerImpl.handle(LocalActivityWorker.java:200)
	at io.temporal.internal.worker.LocalActivityWorker$TaskHandlerImpl.handle(LocalActivityWorker.java:189)
	at io.temporal.internal.worker.PollTaskExecutor.lambda$process$0(PollTaskExecutor.java:73)
	... 3 common frames omitted

The bug is in the CheckedExceptionWrapper.unwrap which assumes that a cause of an exception is either Error or Exception, but never any other child of Throwable or Throwable itself.

Workflow versioning marker not respected on replay

Take the following trivial example:

  public interface FooWorkflow {
    @WorkflowMethod
    void exec();
    @SignalMethod
    void input(int value);
  }

With V1 implementation:

  public static class FooWorkflowImpl implements FooWorkflow {
    private static final Logger LOGGER = LoggerFactory.getLogger(FooWorkflowImpl.class);
    private int reincarnation;
    @Override
    public void exec() {
      int changeFoo = Workflow.getVersion("changeFoo", Workflow.DEFAULT_VERSION, 1);
      LOGGER.info("changeFoo version {}", changeFoo);
      Workflow.await(() -> reincarnation > 0);
    }
    @Override
    public void input(int value) {
      this.reincarnation = value;
    }
  }

As expected, this prints:

changeFoo version 1

Now imagine a V2 implementation that looks as follows:

    @Override
    public void exec() {
      int changeBar = Workflow.getVersion("changeBar", Workflow.DEFAULT_VERSION, 1);
      LOGGER.info("changeBar version {}", changeBar);
      int changeFoo = Workflow.getVersion("changeFoo", Workflow.DEFAULT_VERSION, 1);
      LOGGER.info("changeFoo version {}", changeFoo);
      Workflow.await(() -> reincarnation > 0);
    }

If I update the worker code and signal the existing workflow. Then the output it prints is:

changeBar version -1
changeFoo version -1

Why did changeFoo not reuse the previous version 1 from the marker?

It is falling into here, in DecisionsHelper:

    // If we have a version marker in history event but not in decisions, let's add one.
    RecordMarkerDecisionAttributes marker =
        new RecordMarkerDecisionAttributes()
            .setMarkerName(ClockDecisionContext.VERSION_MARKER_NAME)
            .setHeader(event.getMarkerRecordedEventAttributes().getHeader())
            .setDetails(event.getMarkerRecordedEventAttributes().getDetails());
    Decision markerDecision =
        new Decision()
            .setDecisionType(DecisionType.RecordMarker)
            .setRecordMarkerDecisionAttributes(marker);
    DecisionId markerDecisionId = new DecisionId(DecisionTarget.MARKER, nextDecisionEventId);
    decisions.put(
        markerDecisionId, new MarkerDecisionStateMachine(markerDecisionId, markerDecision));
    nextDecisionEventId++;

Based on the comment, it should not be executing this code.

Intermittent testWorkflowMetrics Unit test failure

Expected Behavior

Test always passing

Actual Behavior

When running in a constrained CI/CD environment it sometimes fails:

io.temporal.workflow.MetricsTest > testWorkflowMetrics FAILED
    java.lang.AssertionError: 3.39s
        at org.junit.Assert.fail(Assert.java:89)
        at org.junit.Assert.assertTrue(Assert.java:42)
        at io.temporal.workflow.MetricsTest.testWorkflowMetrics(MetricsTest.java:280)

Change Activity.getTask to Activity.getInfo

ActivityTask is protobuf which is too low level to expose to the activity code.

Also add Activity.getTimeUntilTimeout() to give simple indication when activity is going to timeout.

Support passing activityId to an activity invocation

Is your feature request related to a problem? Please describe.
It is not possible to specify a business level ID for an activity during its invocation. This is a problem for completing activities asynchronously by ID.

Describe the solution you'd like
Provide a way to specify the ID when invoking an activity.

Expose RPC timeout to the client code

From the customer:

It seems that the Worker and WorkflowClient classes from the Java client do not expose a way to set the RPC timeout. It defaults to 1 second, which was not enough in some of my testing scenarios. Would be nice if this can be set by the client code.

Provide shaded release JAR

From slack:

I am exploring temporal as a replacement for our internal workflow engine.
Is there a java uber-jar available with all dependencies shaded to an internal namespace? I see a lot of netty & guava dependencies and if I make a REST call in an activity using a library dependent on a different version of netty, the classes could conflict, right?

So I propose to add the shaded jar to the release artifacts

Block workflow instead of failing on unexpected exceptions

Is your feature request related to a problem? Please describe.
Currently, an exception that is thrown from a workflow method fails the workflow. In a lot of cases, it is not desirable. For example, a NullPointerException usually means a bug that should be fixed. After the bug is fixed manual steps like reset are needed to restart workflows that failed.

An Error thrown from a workflow method already doesn't fail workflow, but fails a decision task, essentially blocking the workflow execution until the error is fixed. As soon as the error is fixed (usually through a code deployment) the workflows continue from the place they were blocked without any additional manual intervention.

Describe the solution you'd like
The proposal is to change the default behavior to block workflow not only on Error, but also on any exception which is not specified in the "FailList".

Describe alternatives you've considered
Keep the current behavior and rely on reset to restart failed workflows.

Rename Worker.register... to Worker.set

The current Worker.registerActivitiesImpl and registerWorkflowImplementationTypes are confusing as it takes a list and fully overrides it which is expected from set<foo> methods.

Add ability to complete workflow from any line

Is your feature request related to a problem? Please describe.
Currently the only way to complete or fail a workflow is through completing (or throwing exception) from the method annotated with @WorkflowMethod. Sometimes it is convinient to complete or fail workflow from some other function or even thread.

Describe the solution you'd like
An API to complete workflow from any place. Strawman:

Workflow.complete(result);
Workflow.fail(Exception); 

Describe alternatives you've considered
Do not introduce this API and rely on completion of main workflow method.l

Additional context
This feature is based on a customer request.

Include local activity input into its marker decision

Is your feature request related to a problem? Please describe.
For troubleshooting having activity input available in the event history is very useful.

Describe the solution you'd like
Include the input into the marker decision/event. Make it configurable through LocalActivityOptions as for some use cases inputs can be large and increase history size beyond acceptable.

Fix getVersion

It was reported that getVersion doesn't return the same value for the same changeID

Support @SignalMethod and @QueryMethod in independent interfaces

Support registering handlers for signal and query independently from the main workflow interface.
The same for invocation.
The strawman:

public interface MySignal {
   @SignalMethod
   foo(String arg);
}

Workflow.registerHandler(new MySignalImpl());

Then to invoke signal or query from outside:


h = client.new..Stub(MySignal.class, workflowId);
h.foo("bar");

This would allow implementing independent reusable libraries which could be shared by multiple workflows.

Fix CancellationException stack trace

When an activity is cancelled the thrown CancellationException contains stack trace of the handleActivityTaskCanceled, not the caller of the activity:

[{"detailMessage":null,"stackTrace":"com.uber.cadence.internal.replay.ActivityDecisionContext.handleActivityTaskCanceled(ActivityDecisionContext.java:134)
com.uber.cadence.internal.replay.DecisionContextImpl.handleActivityTaskCanceled(DecisionContextImpl.java:280)
com.uber.cadence.internal.replay.ReplayDecider.processEvent(ReplayDecider.java:113)
com.uber.cadence.internal.replay.ReplayDecider.decideImpl(ReplayDecider.java:407)
com.uber.cadence.internal.replay.ReplayDecider.decide(ReplayDecider.java:359)
com.uber.cadence.internal.replay.ReplayDecisionTaskHandler.processDecision(ReplayDecisionTaskHandler.java:145)
com.uber.cadence.internal.replay.ReplayDecisionTaskHandler.handleDecisionTaskImpl(ReplayDecisionTaskHandler.java:125)
com.uber.cadence.internal.replay.ReplayDecisionTaskHandler.handleDecisionTask(ReplayDecisionTaskHandler.java:86)
com.uber.cadence.internal.worker.WorkflowWorker$TaskHandlerImpl.handle(WorkflowWorker.java:257)
com.uber.cadence.internal.worker.WorkflowWorker$TaskHandlerImpl.handle(WorkflowWorker.java:229)
com.uber.cadence.internal.worker.PollTaskExecutor.lambda$process$0(PollTaskExecutor.java:71)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
java.lang.Thread.run(Thread.java:748)
","suppressedExceptions":[],"class":"java.util.concurrent.CancellationException"}],

Don't perform a long poll of the execution result if nobody waits on it

Currently WorkflowClient.execute returns CompletableFuture. The problem is that the GetWorkflowExecutionHistory is initiated as soon as the execute is called, not when future.get is called. So if future.get is not ever called the long poll GetWorkflowExecutionHistory is still done.

Customer needs to specify timeout for start workflow call

Expected Behavior

From slack:

Use-Case: If we can't start workflow for an operation in X secs, we have a fallback option. Now, the fallback option AND workflow can't run concurrently. Hence, it's important that the start workflow deterministically succeeds or fails within X secs.

Actual Behavior

Currently retry options are not configurable.

NPE on null input when calling continue as new

Expected Behavior

Should support continue as new with methods without arguments.

@Override
public void run() {
    .....
    continueAsNewWorkflow.run();
}

Actual Behavior

"java.lang.NullPointerException\n\tat io.temporal.proto.decision.ContinueAsNewWorkflowExecutionDecisionAttributes$Builder.setInput(ContinueAsNewWorkflowExecutionDecisionAttributes.java:1494)\n\tat io.temporal.internal.replay.DecisionsHelper.continueAsNewWorkflowExecution(DecisionsHelper.java:442)

Steps to Reproduce the Problem

Call continue as new on a workflow with @WorkflowMethod without arguments.

Specifications

0.23.1 release of Java SDK

Solution

java.time.OffsetDateTime deserialization failing

Expected Behavior

JacksonJsonPayloadConverter should be able to handle OffsetDateTime deserialization

Version 0.26.0 did not encounter any issues with it previously

Actual Behavior

Workflow execution failure

maestro    | WARNING: An illegal reflective access operation has occurred
maestro    | WARNING: Illegal reflective access by com.fasterxml.jackson.databind.util.ClassUtil (file:/service/bin/maestro.jar) to constructor java.time.OffsetDateTime(java.time.LocalDateTime,java.time.ZoneOffset)
maestro    | WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.databind.util.ClassUtil
maestro    | WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
maestro    | WARNING: All illegal access operations will be denied in a future release
maestro    | 2020-07-23T18:38:26.762Z ERROR [i.t.i.sync.POJOWorkflowImplementationFactory] [workflow-method] Workflow execution failure WorkflowId=e4821190-81f8-4eab-8191-406193cbe6ef, RunId=fcf2d818-30a5-4f01-b68c-3e367242ae07, WorkflowType=ServerlessWorkflowMethods
maestro    | io.temporal.common.converter.DataConverterException: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `java.time.ZoneOffset` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
maestro    |  at [Source: (byte[])"{"_type":"ServiceOk","response":{"dateCreated":{"offset":{"totalSeconds":0,"id":"Z","rules":{"fixedOffset":true,"transitions":[],"transitionRules":[]}},"nano":0,"year":2020,"monthValue":7,"dayOfMonth":23,"hour":18,"minute":38,"second":26,"month""[truncated 309 bytes]; line: 1, column: 314] (through reference chain: ServiceOk["response"]-> Service["dateCreated"]->java.time.OffsetDateTime["offset"])
maestro    |    at io.temporal.common.converter.JacksonJsonPayloadConverter.fromData(JacksonJsonPayloadConverter.java:82)
maestro    |    at io.temporal.common.converter.DefaultDataConverter.fromPayload(DefaultDataConverter.java:106)
maestro    |    at io.temporal.common.converter.DefaultDataConverter.fromPayloads(DefaultDataConverter.java:158)
maestro    |    at io.temporal.internal.sync.SyncWorkflowContext.lambda$executeActivity$aba2ce5b$1(SyncWorkflowContext.java:181)
maestro    |    at io.temporal.internal.sync.CompletablePromiseImpl.lambda$thenApply$2df5ef44$1(CompletablePromiseImpl.java:209)
maestro    |    at io.temporal.internal.sync.CompletablePromiseImpl.lambda$handle$6a2a7e3d$1(CompletablePromiseImpl.java:218)
maestro    |    at io.temporal.internal.sync.CompletablePromiseImpl.lambda$then$16b0e4cc$1(CompletablePromiseImpl.java:267)
maestro    |    at io.temporal.internal.sync.CompletablePromiseImpl.invokeHandlers(CompletablePromiseImpl.java:275)
maestro    |    at io.temporal.internal.sync.CompletablePromiseImpl.complete(CompletablePromiseImpl.java:166)
maestro    |    at io.temporal.internal.sync.SyncWorkflowContext$ActivityCallback.lambda$invoke$1(SyncWorkflowContext.java:210)
maestro    |    at io.temporal.internal.sync.CancellationScopeImpl.run(CancellationScopeImpl.java:104)
maestro    |    at io.temporal.internal.sync.WorkflowThreadImpl$RunnableWrapper.run(WorkflowThreadImpl.java:107)
maestro    |    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
maestro    |    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
maestro    |    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
maestro    |    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
maestro    |    at java.base/java.lang.Thread.run(Thread.java:834)
maestro    | Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `java.time.ZoneOffset` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)

Specifications

  • Version: 0.27.0

Found workaround

using jackson-datatype-jsr310 and objectMapper.registerModule(new JavaTimeModule()); seems to alleviate the issue

Delay workflow completion until abandoned workflows start

Currently if a parent workflow exits before a child workflow has started the child is not going to start at all. It is possible to resolve by waiting for a Promise returned from ChildWorkflowStub.getExecution(). But this is a pretty obscure and error prone feature.

The proposal is to automatically delay workflow completion if there is an outstanding start child request that has abandon parent policy until the child reported that it started successfully.

This also applies to cancellation requests to child workflows that have abandon parent close policy.

Add binary checksum to WorkerOptions

Is your feature request related to a problem? Please describe.
Currently there is no way to set binary checksum used for marking bad builds in JavaSDK.

Require explicit annotation for Workflow and Activity interfaces.

We've run into this issue when trying to use Spring beans as activity implementations - if they're proxied with transactional support or other Spring stuff, Cadence gets upset. Would be great to have an @activity or @ActivityMethod annotation that controlled activity discovery

So it looks like having explicit @ActivityInterface and @WorkflowInterface would resolve these issues. It would also help with issue when two activity interfaces extend the same interface.

Log activity failures

Currently activity failures are not logged which is very inconvenient during unit tests that do automatic activity retries.

Exceptions being thrown on execution with Spring related to gRPC and PerfMark

Running Temporal with Spring generates some exceptions that are not thrown in a standalone Temporal instance.

// App.java 

@ComponentScan
@EnableAutoConfiguration(exclude = {MetricFilterAutoConfiguration.class, MetricRepositoryAutoConfiguration.class})
@EnableConfigurationProperties({LiquibaseProperties.class, ApplicationProperties.class, JHipsterExtraProperties.class})
public class App {
  public static void main(String[] args) {
    SpringApplication app = new SpringApplication(App.class);

    // some calls here ...

    // Init Temporal
    WorkflowServiceStubs service = WorkflowServiceStubs.newInstance();
    WorkflowClient client = WorkflowClient.newInstance(service);
    WorkerFactory factory = WorkerFactory.newInstance(client);

    Worker worker = factory.newWorker("Queue1");
    worker.registerWorkflowImplementationTypes(HelloWorldImpl.class); // workflow calls the activity and returns the result
    worker.registerActivitiesImplementations(new SayHiActivityImpl()); // activity returns "Hello world"

    factory.start();

    HelloWorld workflow = client.newWorkflowStub(HelloWorld.class, WorkflowOptions.newBuilder().setTaskQueue("Queue1").build());

    System.out.println("Workflow result: " + workflow.sayHello()); // prints "Hello World"
  }
}

With this code, several exceptions are generated. However, they do not prevent the workflow and the activity from being correctly executed.


This next exceptions are thrown on line 84, which corresponds to the line WorkflowServiceStubs service = WorkflowServiceStubs.newInstance();

2020-07-27 09:43:17.509 [main] DEBUG
        i.g.netty.shaded.io.grpc.netty.Utils - Epoll is not available, using Nio. 
java.lang.ExceptionInInitializerError: null
	at io.grpc.netty.shaded.io.netty.channel.epoll.Epoll.<clinit>(Epoll.java:39)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:264)
	at io.grpc.netty.shaded.io.grpc.netty.Utils.isEpollAvailable(Utils.java:284)
	at io.grpc.netty.shaded.io.grpc.netty.Utils.<clinit>(Utils.java:107)
	at io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder.<clinit>(NettyChannelBuilder.java:74)
	at io.temporal.internal.grpc.WorkflowServiceStubsImpl.<init>(WorkflowServiceStubsImpl.java:108)
	at io.temporal.serviceclient.WorkflowServiceStubs.newInstance(WorkflowServiceStubs.java:34)
	at my.App.main(App.java:84)
Caused by: java.lang.IllegalStateException: Only supported on Linux
	at io.grpc.netty.shaded.io.netty.channel.epoll.Native.loadNativeLibrary(Native.java:225)
	at io.grpc.netty.shaded.io.netty.channel.epoll.Native.<clinit>(Native.java:58)
	... 9 common frames omitted
2020-07-27 09:43:17.603 [main] DEBUG
        i.g.i.AbstractManagedChannelImplBuilder - Unable to apply census stats 
java.lang.ClassNotFoundException: io.grpc.census.InternalCensusStatsAccessor
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:264)
	at io.grpc.internal.AbstractManagedChannelImplBuilder.getEffectiveInterceptors(AbstractManagedChannelImplBuilder.java:534)
	at io.grpc.internal.AbstractManagedChannelImplBuilder.build(AbstractManagedChannelImplBuilder.java:517)
	at io.temporal.internal.grpc.WorkflowServiceStubsImpl.<init>(WorkflowServiceStubsImpl.java:119)
	at io.temporal.serviceclient.WorkflowServiceStubs.newInstance(WorkflowServiceStubs.java:34)
	at my.App.main(App.java:84)
2020-07-27 09:43:17.603 [main] DEBUG
        i.g.i.AbstractManagedChannelImplBuilder - Unable to apply census stats 
java.lang.ClassNotFoundException: io.grpc.census.InternalCensusTracingAccessor
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:264)
	at io.grpc.internal.AbstractManagedChannelImplBuilder.getEffectiveInterceptors(AbstractManagedChannelImplBuilder.java:569)
	at io.grpc.internal.AbstractManagedChannelImplBuilder.build(AbstractManagedChannelImplBuilder.java:517)
	at io.temporal.internal.grpc.WorkflowServiceStubsImpl.<init>(WorkflowServiceStubsImpl.java:119)
	at io.temporal.serviceclient.WorkflowServiceStubs.newInstance(WorkflowServiceStubs.java:34)
	at my.App.main(App.java:84)

And this one is thrown after client.newWorkflowStub is called.

2020-07-27 09:43:17.992 [Activity Poller taskQueue="Queue1", namespace="default": 3] DEBUG
        io.perfmark.PerfMark - Error during PerfMark.<clinit> 
java.lang.ClassNotFoundException: io.perfmark.impl.SecretPerfMarkImpl$PerfMarkImpl
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:264)
	at io.perfmark.PerfMark.<clinit>(PerfMark.java:36)
	at io.grpc.internal.ClientCallImpl.<init>(ClientCallImpl.java:109)
	at io.grpc.internal.ManagedChannelImpl$RealChannel.newCall(ManagedChannelImpl.java:890)
	at io.grpc.internal.ServiceConfigInterceptor.interceptCall(ServiceConfigInterceptor.java:137)
	at io.grpc.ClientInterceptors$InterceptorChannel.newCall(ClientInterceptors.java:156)
	at io.grpc.internal.ManagedChannelImpl.newCall(ManagedChannelImpl.java:855)
	at io.grpc.internal.ForwardingManagedChannel.newCall(ForwardingManagedChannel.java:63)
	at io.temporal.internal.grpc.GrpcMetricsInterceptor$MetricsClientCall.<init>(GrpcMetricsInterceptor.java:88)
	at io.temporal.internal.grpc.GrpcMetricsInterceptor.interceptCall(GrpcMetricsInterceptor.java:74)
	at io.grpc.ClientInterceptors$InterceptorChannel.newCall(ClientInterceptors.java:156)
	at io.temporal.internal.grpc.GrpcDeadlineInterceptor.interceptCall(GrpcDeadlineInterceptor.java:69)
	at io.grpc.ClientInterceptors$InterceptorChannel.newCall(ClientInterceptors.java:156)
	at io.grpc.stub.MetadataUtils$HeaderAttachingClientInterceptor.interceptCall(MetadataUtils.java:74)
	at io.grpc.ClientInterceptors$InterceptorChannel.newCall(ClientInterceptors.java:156)
	at io.grpc.stub.ClientCalls.blockingUnaryCall(ClientCalls.java:128)
	at io.temporal.api.workflowservice.v1.WorkflowServiceGrpc$WorkflowServiceBlockingStub.pollActivityTaskQueue(WorkflowServiceGrpc.java:2682)
	at io.temporal.internal.worker.ActivityPollTask.poll(ActivityPollTask.java:95)
	at io.temporal.internal.worker.ActivityPollTask.poll(ActivityPollTask.java:38)
	at io.temporal.internal.worker.Poller$PollExecutionTask.run(Poller.java:273)
	at io.temporal.internal.worker.Poller$PollLoopTask.run(Poller.java:242)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
2020-07-27 09:43:18.009 [Activity Poller taskQueue="Queue1", namespace="default": 3] DEBUG
        io.grpc.Context - Storage override doesn't exist. Using default 
java.lang.ClassNotFoundException: io.grpc.override.ContextStorageOverride
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:264)
	at io.grpc.Context$LazyStorage.createStorage(Context.java:142)
	at io.grpc.Context$LazyStorage.<clinit>(Context.java:131)
	at io.grpc.Context.storage(Context.java:119)
	at io.grpc.Context.current(Context.java:185)
	at io.grpc.internal.ClientCallImpl.<init>(ClientCallImpl.java:118)
	at io.grpc.internal.ManagedChannelImpl$RealChannel.newCall(ManagedChannelImpl.java:890)
	at io.grpc.internal.ServiceConfigInterceptor.interceptCall(ServiceConfigInterceptor.java:137)
	at io.grpc.ClientInterceptors$InterceptorChannel.newCall(ClientInterceptors.java:156)
	at io.grpc.internal.ManagedChannelImpl.newCall(ManagedChannelImpl.java:855)
	at io.grpc.internal.ForwardingManagedChannel.newCall(ForwardingManagedChannel.java:63)
	at io.temporal.internal.grpc.GrpcMetricsInterceptor$MetricsClientCall.<init>(GrpcMetricsInterceptor.java:88)
	at io.temporal.internal.grpc.GrpcMetricsInterceptor.interceptCall(GrpcMetricsInterceptor.java:74)
	at io.grpc.ClientInterceptors$InterceptorChannel.newCall(ClientInterceptors.java:156)
	at io.temporal.internal.grpc.GrpcDeadlineInterceptor.interceptCall(GrpcDeadlineInterceptor.java:69)
	at io.grpc.ClientInterceptors$InterceptorChannel.newCall(ClientInterceptors.java:156)
	at io.grpc.stub.MetadataUtils$HeaderAttachingClientInterceptor.interceptCall(MetadataUtils.java:74)
	at io.grpc.ClientInterceptors$InterceptorChannel.newCall(ClientInterceptors.java:156)
	at io.grpc.stub.ClientCalls.blockingUnaryCall(ClientCalls.java:128)
	at io.temporal.api.workflowservice.v1.WorkflowServiceGrpc$WorkflowServiceBlockingStub.pollActivityTaskQueue(WorkflowServiceGrpc.java:2682)
	at io.temporal.internal.worker.ActivityPollTask.poll(ActivityPollTask.java:95)
	at io.temporal.internal.worker.ActivityPollTask.poll(ActivityPollTask.java:38)
	at io.temporal.internal.worker.Poller$PollExecutionTask.run(Poller.java:273)
	at io.temporal.internal.worker.Poller$PollLoopTask.run(Poller.java:242)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

Expected Behavior

Exceptions should not be thrown.

Actual Behavior

Exceptions are thrown for no apparent reason, since the same code in a standalone Temporal application doesn't throw any exceptions.

Steps to Reproduce the Problem

  1. Use Temporal with a Spring app.
  2. Register a workflow.
  3. Execute it.

Specifications

  • Version: v0.2.7
  • Platform: Windows 10

Need ability to modify ObjectMapper created by JacksonJsonPayloadConverter

Is your feature request related to a problem? Please describe.

When building a Temporal app using Scala, we need to add jackson-scala-module so it knows how to handle Scala types. However, we need access to the ObjectMapper to do this.

This isn't useful for just Scala; for example, we also have a few other org-specific Jackson modules that we use to make using Twilio-internal JVM types easier with Jackson.

Describe the solution you'd like

Two options come to mind:

  1. A new constructor for JacksonJsonPayloadConverter that allows us to pass our own ObjectMapper.
  2. A getObjectMapper() method that returns the ObjectMapper that's already been created, which allows us to augment its configuration.

Describe alternatives you've considered

Right now I'm using fragile bits of reflection to fetch the private ObjectMapper field to make modifications to it. Obviously, that's not sustainable.

Additional context

n/a

Add support for MaxConcurrentActivityExecutionSize for async activities

Customer reported:

We are noticing that .setMaxConcurrentWorkflowExecutionSize(2) and .setMaxConcurrentActivityExecutionSize(2) on WorkerOptions don’t seem to work if we do asynchronous activity completion. Have you guys experienced this/have a good way around this? We use the async activity completion to be able to use Kotlin coroutines in parts of our code. I am trying to get some logs from the team that reported it, but wanted to see if you guys had seen something similar

The proposed solution is to have a special API for async activity completion from the same process, which is different from the current API which was intended for completion from any process. This new API would perform counting correctly.

Health check between service + temporal servers

Is your feature request related to a problem? Please describe.
With cadence we have seen issues where clients lose their connection and are unable to reconnect to the cadence servers

Describe the solution you'd like
A health checking mechanism that allows me to check whether the workers/factory have a healthy connection. This would allow us to check something like factory.isHealthy() in a k8s health check endpoint and restart the worker services if necessary

Describe alternatives you've considered
None.

Additional context
We built this into the cadence java client by forking it and using the TChannel meta health check that cadence looks to already implement.

Do not retry SimulatedTimeoutException

Expected Behavior

SimulatedTimeoutException of ScheduleToStart type should not be retried.

Actual Behavior

FileProcessingTest.testHostFailover test demonstrates that it is retried according to the retry policy.

Simplify async activity implementation

To not tie up threads users requested support for activity implementations that return CompletableFuture. As we don't want to have Future in the activity API the strawman proposal is to add adapter methods that can be called from activity (similar to Async class from workflow package)

class ActivityAsync {
  public static <A1, R> R function(Functions.Func1<A1, CompletableFuture<R>> function, A1 arg1) { ... }
  // similar methods for procedures and argument counts
}

Then an activity implementation could look like:

String myActivity(String foo) {
   return ActivityAsync.function(myActivityReturningFuture, foo);
}

CompletableFuture<String> myActivityReturningFuture(String foo) {
   ...
}

where only myActivity method is part of the interface. Then a dynamic proxy adapter can be written to convert from a class that has methods that return ComletableFutures to the activity interface that returns values:

interface MyActivities {
   String foo();
   void bar();
}

@ActivitiesImplementation(implements=MyActivities.class)
class MyActivitiesImpl {
   CompletableFuture<String> foo();
   ComletableFuture<Void> bar();
}

Then the Worker would just accept MyActivitiesImpl instance and internally create the dynamic proxy.

Regression with a workflow implementing an empty marker interface

Expected Behavior

In temporal 0.26.0 java-sdk the following structure worked just fine:

interface WorkflowMarker {
}

@WorkflowInterface
interface MyWorkflow {
    @WorkflowMethod
    fun execute(req: Req): Resp
}

MyWorkflowImpl : MyWorkflow, WorkflowMarker

Actual Behavior

In temporal 0.28.0 it doesn't work and calling a stub causing:

Caused by: io.temporal.failure.ApplicationFailure: message='Interface doesn't contain any methods: WorkflowMarker', type='java.lang.IllegalArgumentException', nonRetryable=false
	at io.temporal.internal.sync.POJOWorkflowInterfaceMetadata.newInstance(POJOWorkflowInterfaceMetadata.java:125)
	at io.temporal.internal.sync.POJOWorkflowInterfaceMetadata.newInstanceSkipWorkflowAnnotationCheck(POJOWorkflowInterfaceMetadata.java:107)
	at io.temporal.internal.sync.POJOWorkflowImplMetadata.<init>(POJOWorkflowImplMetadata.java:93)
	at io.temporal.internal.sync.POJOWorkflowImplMetadata.newListenerInstance(POJOWorkflowImplMetadata.java:75)
	at io.temporal.internal.sync.WorkflowInternal.registerListener(WorkflowInternal.java:111)
	at io.temporal.internal.sync.POJOWorkflowImplementationFactory$POJOWorkflowImplementation$RootWorkflowInboundCallsInterceptor.init(POJOWorkflowImplementationFactory.java:318)
	at io.temporal.internal.sync.POJOWorkflowImplementationFactory$POJOWorkflowImplementation.initialize(POJOWorkflowImplementationFactory.java:237)
	at io.temporal.internal.sync.SyncWorkflow.lambda$start$1(SyncWorkflow.java:117)
	at io.temporal.internal.sync.CancellationScopeImpl.run(CancellationScopeImpl.java:104)
	at io.temporal.internal.sync.WorkflowThreadImpl$RunnableWrapper.run(WorkflowThreadImpl.java:107)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:834)

Rootcause analysis

In 0.26.0 POJOWorkflowImplMetadata had a permissive implementation:

POJOWorkflowInterfaceMetadata interfaceMetadata =
    POJOWorkflowInterfaceMetadata.newImplementationInterface(anInterface);
List<POJOWorkflowMethodMetadata> methods = interfaceMetadata.getMethodsMetadata();
for (POJOWorkflowMethodMetadata methodMetadata : methods) {
...
}

If there is no methods in the interface - we do nothing and go to the next interface.

POJOWorkflowInterfaceMetadata.newImplementationInterface(anInterface) doesn’t throw an exception if there is no methods.

Now in 0.28.0 for each interface that the workflow implements we have

POJOWorkflowInterfaceMetadata.newInstanceSkipWorkflowAnnotationCheck(anInterface);
which throws an exception if the interface is empty even if the interface has no Temporal annotations at all.

Specifications

  • Version: 0.26.0 vs 0.28.0 regression

Add Logger that works in both activity and workflow context

Some utility code can be invoked from workflow context as well as outside of it. Workflow.getLogger fails with an exception when invoked outside of a workflow. The proposal is to change getLogger to not fail due to called outside of the workflow context and just return standard loger instead of ReplayAwareLogger.

Scala module

I'll go with a top-to-bottom approach with the things I encountered, and how I tried to solve them in my project.

  1. WorkflowClient
  • WorkflowClient.start with io.temporal.workflow.Functions.Func arguments
  • WorkflowClient.start with io.temporal.workflow.Functions.Proc arguments
  • WorkflowClient.execute with io.temporal.workflow.Functions.Func arguments
  • WorkflowClient.execute with io.temporal.workflow.Functions.Proc arguments

Solved this by writing some small wrappers. I don't think this is the way, but the code inside might be useful. These are the only type of functions I needed, didn't convert them all (e.g the ones that take more arguments)

def startAsync(f: T => Unit, arg: T): WorkflowExecution = {
   WorkflowClient.start(new Proc1[T]() {
     override def apply(j: T): Unit = f(j)
   }, t)
 }

 def executeAsyncVoid(f: () => Unit): Future[Unit] = {
   import scala.compat.java8.FutureConverters._

   WorkflowClient.execute(new Proc {
     override def apply(): Unit = f()
   }).toScala.map[Unit](_ => ())
 }

 def executeAsync[T](f: () => T): Future[T] = {
   import scala.compat.java8.FutureConverters._

   WorkflowClient.execute(new Func[T] {
     override def apply(): T = f()
   }).toScala
 }
  1. Scala specific object types serialization/deserialization
  • Option
  • List (basically all Scala collections)

We can probably solve this when Jackson will be integrated in java-sdk

  1. Workflow.await requiring a Supplier, instead of passing a function reference

Again, solved in a simple wrapper

 def await(duration: Duration, f: () => Boolean) {
    Workflow.await(duration, new Supplier[lang.Boolean] {
      override def get(): lang.Boolean = f()
    })
  }
  1. Java way of referencing some method (not sure how this syntax it's called in Java)
    (Mostly the same as 1. but I put it in a different issue as it's a little bit a different problem)

WorkflowClient.execute(workflow::getGreeting, "World") -> notice the double colon

  1. Accepting Scala Duration type

Add support for unit testing workflow versioning

Is your feature request related to a problem? Please describe.
Currently there is no way to test version upgrades of code through unit testing framework.

Describe the solution you'd like
It should be possible to execute different upgrade scenarios through unit testing framework.

Describe alternatives you've considered
Implement upgrade scenarios through integration testing only.

Add metric to indicate that poll rate is limited by client

Currently there is no metric which can indicate that poll rate is limited by client (for example maximum number of parallel tasks is reached), not by the service or connection latency.

There are multiple reasons for polls being slowed down. I'm not sure if we need a single metric with appropriate slowdown tag or multiple metrics. Here is the polling loop code.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.