Class Workflow


  • public final class Workflow
    extends java.lang.Object
    Workflow encapsulates the orchestration of activities and child workflows. It can also answer to synchronous queries and receive external events (also known as signals).

    Workflow Interface

    A workflow must define an interface class. All of its methods must have one of the following annotations:
    • @WorkflowMethod indicates an entry point to a workflow. It contains parameters such as timeouts and a task list. Required parameters (like executionStartToCloseTimeoutSeconds) that are not specified through the annotation must be provided at runtime.
    • @SignalMethod indicates a method that reacts to external signals. It must have a void return type.
    • @QueryMethod indicates a method that reacts to synchronous query requests. You can have more than one method with the same annotation.
    
     public interface FileProcessingWorkflow {
    
        @WorkflowMethod(executionStartToCloseTimeoutSeconds = 10, taskList = "file-processing")
         String processFile(Arguments args);
    
        @QueryMethod(name="history")
         List getHistory();
    
        @QueryMethod(name="status")
         String getStatus();
    
        @SignalMethod
         void retryNow();
     }
     

    Starting workflow executions

    See WorkflowClient

    Implementing Workflows

    A workflow implementation implements a workflow interface. Each time a new workflow execution is started, a new instance of the workflow implementation object is created. Then, one of the methods (depending on which workflow type has been started) annotated with @WorkflowMethod is invoked. As soon as this method returns the workflow, execution is closed. While workflow execution is open, it can receive calls to signal and query methods. No additional calls to workflow methods are allowed. The workflow object is stateful, so query and signal methods can communicate with the other parts of the workflow through workflow object fields.

    Calling Activities

    newActivityStub(Class) returns a client-side stub that implements an activity interface. It takes activity type and activity options as arguments. Activity options are needed only if some of the required timeouts are not specified through the @ActivityMethod annotation.

    Calling a method on this interface invokes an activity that implements this method. An activity invocation synchronously blocks until the activity completes, fails, or times out. Even if activity execution takes a few months, the workflow code still sees it as a single synchronous invocation. Isn't it great? It doesn't matter what happens to the processes that host the workflow. The business logic code just sees a single method call.

    
     public class FileProcessingWorkflowImpl implements FileProcessingWorkflow {
    
         private final FileProcessingActivities activities;
    
         public FileProcessingWorkflowImpl() {
             this.store = Workflow.newActivityStub(FileProcessingActivities.class);
         }
    
        @Override
         public void processFile(Arguments args) {
             String localName = null;
             String processedName = null;
             try {
                 localName = activities.download(args.getSourceBucketName(), args.getSourceFilename());
                 processedName = activities.processFile(localName);
                 activities.upload(args.getTargetBucketName(), args.getTargetFilename(), processedName);
             } finally {
                 if (localName != null) { // File was downloaded.
                     activities.deleteLocalFile(localName);
                 }
                 if (processedName != null) { // File was processed.
                     activities.deleteLocalFile(processedName);
                 }
             }
         }
         ...
     }
     
    If different activities need different options, like timeouts or a task list, multiple client-side stubs can be created with different options.
    
     public FileProcessingWorkflowImpl() {
         ActivityOptions options1 = new ActivityOptions.Builder()
             .setTaskList("taskList1")
             .build();
         this.store1 = Workflow.newActivityStub(FileProcessingActivities.class, options1);
    
         ActivityOptions options2 = new ActivityOptions.Builder()
             .setTaskList("taskList2")
             .build();
         this.store2 = Workflow.newActivityStub(FileProcessingActivities.class, options2);
     }
     

    Calling Activities Asynchronously

    Sometimes workflows need to perform certain operations in parallel. The Async static methods allow you to invoke any activity asynchronously. The call returns a Promise result immediately. Promise is similar to both Future and CompletionStage. The Promise.get() blocks until a result is available. It also exposes the Promise.thenApply(Functions.Func1) and Promise.handle(Functions.Func2) methods. See the Promise documentation for technical details about differences with Future.

    To convert a synchronous call

    
     String localName = activities.download(sourceBucket, sourceFile);
     
    to asynchronous style, the method reference is passed to Async.function(Functions.Func) or Async.procedure(Functions.Proc) followed by activity arguments:
    
     Promise localNamePromise = Async.function(activities::download, sourceBucket, sourceFile);
     
    Then to wait synchronously for the result:
    
     String localName = localNamePromise.get();
     
    Here is the above example rewritten to call download and upload in parallel on multiple files:
    
     public void processFile(Arguments args) {
         List<Promise<String>> localNamePromises = new ArrayList<>();
         List<String> processedNames = null;
         try {
             // Download all files in parallel.
             for (String sourceFilename : args.getSourceFilenames()) {
                 Promise<String> localName = Async.function(activities::download, args.getSourceBucketName(), sourceFilename);
                 localNamePromises.add(localName);
             }
             // allOf converts a list of promises to a single promise that contains a list of each promise value.
             Promise<List<String>> localNamesPromise = Promise.allOf(localNamePromises);
    
             // All code until the next line wasn't blocking.
             // The promise get is a blocking call.
             List<String> localNames = localNamesPromise.get();
             processedNames = activities.processFiles(localNames);
    
             // Upload all results in parallel.
             List<Promise<Void>> uploadedList = new ArrayList<>();
             for (String processedName : processedNames) {
                 Promise<Void> uploaded = Async.procedure(activities::upload,
                     args.getTargetBucketName(),
                     args.getTargetFilename(),
                     processedName);
                 uploadedList.add(uploaded);
             }
             // Wait for all uploads to complete.
             Promise<?> allUploaded = Promise.allOf(uploadedList);
             allUploaded.get(); // blocks until all promises are ready.
         } finally {
             // Execute deletes even if workflow is cancelled.
             Workflow.newDetachedCancellationScope(
                 () -> {
                     for (Promise<Sting> localNamePromise : localNamePromises) {
                         // Skip files that haven't completed downloading.
                         if (localNamePromise.isCompleted()) {
                             activities.deleteLocalFile(localNamePromise.get());
                         }
                     }
                     if (processedNames != null) {
                         for (String processedName : processedNames) {
                             activities.deleteLocalFile(processedName);
                         }
                     }
                 }
              ).run();
         }
     }
     

    Child Workflows

    Besides activities, a workflow can also orchestrate other workflows.

    newChildWorkflowStub(Class) returns a client-side stub that implements a child workflow interface. It takes a child workflow type and optional child workflow options as arguments. Workflow options may be needed to override the timeouts and task list if they differ from the ones defined in the @WorkflowMethod annotation or parent workflow.

    The first call to the child workflow stub must always be to a method annotated with @WorkflowMethod. Similarly to activities, a call can be synchronous or asynchronous using Async.function(Functions.Func) or Async.procedure(Functions.Proc). The synchronous call blocks until a child workflow completes. The asynchronous call returns a Promise that can be used to wait for the completion. After an async call returns the stub, it can be used to send signals to the child by calling methods annotated with @SignalMethod. Querying a child workflow by calling methods annotated with @QueryMethod from within workflow code is not supported. However, queries can be done from activities using the WorkflowClient provided stub.

    
     public interface GreetingChild {
        @WorkflowMethod
         String composeGreeting(String greeting, String name);
     }
    
     public static class GreetingWorkflowImpl implements GreetingWorkflow {
    
        @Override
         public String getGreeting(String name) {
             GreetingChild child = Workflow.newChildWorkflowStub(GreetingChild.class);
    
             // This is a blocking call that returns only after child has completed.
             return child.composeGreeting("Hello", name );
         }
     }
     
    Running two children in parallel:
    
     public static class GreetingWorkflowImpl implements GreetingWorkflow {
    
        @Override
         public String getGreeting(String name) {
    
             // Workflows are stateful, so a new stub must be created for each new child.
             GreetingChild child1 = Workflow.newChildWorkflowStub(GreetingChild.class);
             Promise greeting1 = Async.function(child1::composeGreeting, "Hello", name);
    
             // Both children will run concurrently.
             GreetingChild child2 = Workflow.newChildWorkflowStub(GreetingChild.class);
             Promise greeting2 = Async.function(child2::composeGreeting, "Bye", name);
    
             // Do something else here.
             ...
             return "First: " + greeting1.get() + ", second=" + greeting2.get();
         }
     }
     
    To start child then return and let child run:
    
     public static class GreetingWorkflowImpl implements GreetingWorkflow {
    
        @Override
         public String getGreeting(String name) {
    
             GreetingChild child1 = Workflow.newChildWorkflowStub(GreetingChild.class);
             Async.function(child1::composeGreeting, "Hello", name);
    
             // block until child started,
             // otherwise child may not start because parent complete first.
             Promise childPromise = Workflow.getWorkflowExecution(child);
             childPromise.get();
    
             return "Parent Greeting";
         }
     }
     
    To send signal to a child, call a method annotated with @SignalMethod:
    
     public interface GreetingChild {
        @WorkflowMethod
         String composeGreeting(String greeting, String name);
    
        @SignalMethod
         void updateName(String name);
     }
    
     public static class GreetingWorkflowImpl implements GreetingWorkflow {
    
        @Override
         public String getGreeting(String name) {
             GreetingChild child = Workflow.newChildWorkflowStub(GreetingChild.class);
             Promise greeting = Async.function(child::composeGreeting, "Hello", name);
             child.updateName("Cadence");
             return greeting.get();
         }
     }
     
    Calling methods annotated with @QueryMethod is not allowed from within a workflow code.

    Workflow Implementation Constraints

    Cadence uses event sourcing to recover the state of a workflow object including its threads and local variable values. In essence, every time a workflow state has to be restored, its code is re-executed from the beginning. When replaying, side effects (such as activity invocations) are ignored because they are already recorded in the workflow event history. When writing workflow logic, the replay is not visible, so the code should be written as it executes only once. This design puts the following constraints on the workflow implementation:
    • Do not use any mutable global variables because multiple instances of workflows are executed in parallel.
    • Do not call any non deterministic functions like non seeded random or UUID.randomUUID() directly form the workflow code. Always do this in activities.
    • Don’t perform any IO or service calls as they are not usually deterministic. Use activities for this.
    • Only use currentTimeMillis() to get the current time inside a workflow.
    • Do not use native Java Thread or any other multi-threaded classes like ThreadPoolExecutor. Use Async.function(Functions.Func) or Async.procedure(Functions.Proc) to execute code asynchronously.
    • Don't use any synchronization, locks, and other standard Java blocking concurrency-related classes besides those provided by the Workflow class. There is no need in explicit synchronization because multi-threaded code inside a workflow is executed one thread at a time and under a global lock.
    • Call sleep(Duration) instead of Thread.sleep(long).
    • Use Promise and CompletablePromise instead of Future and CompletableFuture.
    • Use WorkflowQueue instead of BlockingQueue.
    • Don't change workflow code when there are open workflows. The ability to do updates through visioning is TBD.
    • Don’t access configuration APIs directly from a workflow because changes in the configuration might affect a workflow execution path. Pass it as an argument to a workflow function or use an activity to load it.

    Workflow method arguments and return values are serializable to a byte array using the provided DataConverter. The default implementation uses the JSON serializer, but any alternative serialization mechanism is pluggable.

    The values passed to workflows through invocation parameters or returned through a result value are recorded in the execution history. The entire execution history is transferred from the Cadence service to workflow workers with every event that the workflow logic needs to process. A large execution history can thus adversely impact the performance of your workflow. Therefore, be mindful of the amount of data that you transfer via activity invocation parameters or return values. Other than that, no additional limitations exist on activity implementations.

    • Field Summary

      Fields 
      Modifier and Type Field Description
      static int DEFAULT_VERSION  
    • Method Summary

      All Methods Static Methods Concrete Methods 
      Modifier and Type Method Description
      static boolean await​(java.time.Duration timeout, java.util.function.Supplier<java.lang.Boolean> unblockCondition)
      Block current workflow thread until unblockCondition is evaluated to true or timeoutMillis passes.
      static void await​(java.util.function.Supplier<java.lang.Boolean> unblockCondition)
      Block current thread until unblockCondition is evaluated to true.
      static void continueAsNew​(java.lang.Object... args)
      Continues the current workflow execution as a new run with the same options.
      static void continueAsNew​(java.util.Optional<java.lang.String> workflowType, java.util.Optional<ContinueAsNewOptions> options, java.lang.Object... args)
      Continues the current workflow execution as a new run possibly overriding the workflow type and options.
      static long currentTimeMillis()
      Must be used to get current time instead of System.currentTimeMillis() to guarantee determinism.
      static <R> R getLastCompletionResult​(java.lang.Class<R> resultClass)
      GetLastCompletionResult extract last completion result from previous run for this cron workflow.
      static <R> R getLastCompletionResult​(java.lang.Class<R> resultClass, java.lang.reflect.Type resultType)
      GetLastCompletionResult extract last completion result from previous run for this cron workflow.
      static org.slf4j.Logger getLogger​(java.lang.Class<?> clazz)
      Get logger to use inside workflow.
      static org.slf4j.Logger getLogger​(java.lang.String name)
      Get logger to use inside workflow.
      static com.uber.m3.tally.Scope getMetricsScope()
      Get scope for reporting business metrics in workflow logic.
      static int getVersion​(java.lang.String changeID, int minSupported, int maxSupported)
      getVersion is used to safely perform backwards incompatible changes to workflow definitions.
      static Promise<WorkflowExecution> getWorkflowExecution​(java.lang.Object childWorkflowStub)
      Extracts workflow execution from a stub created through newChildWorkflowStub(Class, ChildWorkflowOptions) or newExternalWorkflowStub(Class, String).
      static WorkflowInfo getWorkflowInfo()  
      static boolean isReplaying()
      True if workflow code is being replayed.
      static <R> R mutableSideEffect​(java.lang.String id, java.lang.Class<R> resultClass, java.lang.reflect.Type resultType, java.util.function.BiPredicate<R,​R> updated, Functions.Func<R> func)
      mutableSideEffect is similar to sideEffect(Class, Functions.Func) in allowing calls of non-deterministic functions from workflow code.
      static <R> R mutableSideEffect​(java.lang.String id, java.lang.Class<R> resultClass, java.util.function.BiPredicate<R,​R> updated, Functions.Func<R> func)
      mutableSideEffect is similar to sideEffect(Class, Functions.Func) in allowing calls of non-deterministic functions from workflow code.
      static <T> T newActivityStub​(java.lang.Class<T> activityInterface)
      Creates client stub to activities that implement given interface.
      static <T> T newActivityStub​(java.lang.Class<T> activityInterface, ActivityOptions options)
      Creates client stub to activities that implement given interface.
      static CancellationScope newCancellationScope​(Functions.Proc1<CancellationScope> proc)
      Wraps a procedure in a CancellationScope.
      static CancellationScope newCancellationScope​(java.lang.Runnable runnable)
      Wraps the Runnable method argument in a CancellationScope.
      static <T> T newChildWorkflowStub​(java.lang.Class<T> workflowInterface)
      Creates client stub that can be used to start a child workflow that implements the given interface using parent options.
      static <T> T newChildWorkflowStub​(java.lang.Class<T> workflowInterface, ChildWorkflowOptions options)
      Creates client stub that can be used to start a child workflow that implements given interface.
      static <T> T newContinueAsNewStub​(java.lang.Class<T> workflowInterface)
      Creates a client stub that can be used to continue this workflow as a new run.
      static <T> T newContinueAsNewStub​(java.lang.Class<T> workflowInterface, ContinueAsNewOptions options)
      Creates a client stub that can be used to continue this workflow as a new run.
      static CancellationScope newDetachedCancellationScope​(java.lang.Runnable runnable)
      Creates a CancellationScope that is not linked to a parent scope.
      static <R> R newExternalWorkflowStub​(java.lang.Class<? extends R> workflowInterface, WorkflowExecution execution)
      Creates client stub that can be used to communicate to an existing workflow execution.
      static <R> R newExternalWorkflowStub​(java.lang.Class<? extends R> workflowInterface, java.lang.String workflowId)
      Creates client stub that can be used to communicate to an existing workflow execution.
      static <E> Promise<E> newFailedPromise​(java.lang.Exception failure)  
      static <T> T newLocalActivityStub​(java.lang.Class<T> activityInterface)
      Creates client stub to local activities that implement given interface.
      static <T> T newLocalActivityStub​(java.lang.Class<T> activityInterface, LocalActivityOptions options)
      Creates client stub to local activities that implement given interface.
      static <E> CompletablePromise<E> newPromise()  
      static <E> Promise<E> newPromise​(E value)  
      static <E> WorkflowQueue<E> newQueue​(int capacity)  
      static java.util.Random newRandom()
      Replay safe random numbers generator.
      static Promise<java.lang.Void> newTimer​(java.time.Duration delay)
      Create new timer.
      static ActivityStub newUntypedActivityStub​(ActivityOptions options)
      Creates non typed client stub to activities.
      static ChildWorkflowStub newUntypedChildWorkflowStub​(java.lang.String workflowType)
      Creates untyped client stub that can be used to start and signal a child workflow.
      static ChildWorkflowStub newUntypedChildWorkflowStub​(java.lang.String workflowType, ChildWorkflowOptions options)
      Creates untyped client stub that can be used to start and signal a child workflow.
      static ExternalWorkflowStub newUntypedExternalWorkflowStub​(WorkflowExecution execution)
      Creates untyped client stub that can be used to signal or cancel a child workflow.
      static ExternalWorkflowStub newUntypedExternalWorkflowStub​(java.lang.String workflowId)
      Creates untyped client stub that can be used to signal or cancel a child workflow.
      static ActivityStub newUntypedLocalActivityStub​(LocalActivityOptions options)
      Creates non typed client stub to local activities.
      static java.util.UUID randomUUID()
      Replay safe way to generate UUID.
      static void registerQuery​(java.lang.Object queryImplementation)
      Register query or queries implementation object.
      static <R> R retry​(RetryOptions options, Functions.Func<R> fn)
      Invokes function retrying in case of failures according to retry options.
      static void retry​(RetryOptions options, Functions.Proc proc)
      Invokes function retrying in case of failures according to retry options.
      static <R> R sideEffect​(java.lang.Class<R> resultClass, Functions.Func<R> func)
      Executes the provided function once, records its result into the workflow history.
      static <R> R sideEffect​(java.lang.Class<R> resultClass, java.lang.reflect.Type resultType, Functions.Func<R> func)
      Executes the provided function once, records its result into the workflow history.
      static void sleep​(long millis)
      Must be called instead of Thread.sleep(long) to guarantee determinism.
      static void sleep​(java.time.Duration duration)
      Must be called instead of Thread.sleep(long) to guarantee determinism.
      static java.lang.Exception unwrap​(java.lang.Exception e)
      Removes CheckedExceptionWrapper from causal exception chain.
      static void upsertSearchAttributes​(java.util.Map<java.lang.String,​java.lang.Object> searchAttributes)
      upsertSearchAttributes is used to add or update workflow search attributes.
      static java.lang.RuntimeException wrap​(java.lang.Exception e)
      If there is a need to return a checked exception from a workflow implementation do not add the exception to a method signature but wrap it using this method before rethrowing.
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • Method Detail

      • newActivityStub

        public static <T> T newActivityStub​(java.lang.Class<T> activityInterface,
                                            ActivityOptions options)
        Creates client stub to activities that implement given interface.
        Parameters:
        activityInterface - interface type implemented by activities.
        options - options that together with the properties of ActivityMethod specify the activity invocation parameters.
      • newActivityStub

        public static <T> T newActivityStub​(java.lang.Class<T> activityInterface)
        Creates client stub to activities that implement given interface. `
        Parameters:
        activityInterface - interface type implemented by activities
      • newUntypedActivityStub

        public static ActivityStub newUntypedActivityStub​(ActivityOptions options)
        Creates non typed client stub to activities. Allows executing activities by their string name.
        Parameters:
        options - specify the activity invocation parameters.
      • newLocalActivityStub

        public static <T> T newLocalActivityStub​(java.lang.Class<T> activityInterface,
                                                 LocalActivityOptions options)
        Creates client stub to local activities that implement given interface. A local activity is similar to a regular activity, but with some key differences: 1. Local activity is scheduled and run by the workflow worker locally. 2. Local activity does not need Cadence server to schedule activity task and does not rely on activity worker. 3. Local activity is for short living activities (usually finishes within seconds). 4. Local activity cannot heartbeat.
        Parameters:
        activityInterface - interface type implemented by activities.
        options - options that together with the properties of ActivityMethod specify the activity invocation parameters.
      • newLocalActivityStub

        public static <T> T newLocalActivityStub​(java.lang.Class<T> activityInterface)
        Creates client stub to local activities that implement given interface.
        Parameters:
        activityInterface - interface type implemented by activities
      • newUntypedLocalActivityStub

        public static ActivityStub newUntypedLocalActivityStub​(LocalActivityOptions options)
        Creates non typed client stub to local activities. Allows executing activities by their string name.
        Parameters:
        options - specify the local activity invocation parameters.
      • newChildWorkflowStub

        public static <T> T newChildWorkflowStub​(java.lang.Class<T> workflowInterface)
        Creates client stub that can be used to start a child workflow that implements the given interface using parent options. Use newExternalWorkflowStub(Class, String) to get a stub to signal a workflow without starting it.
        Parameters:
        workflowInterface - interface type implemented by activities
      • newChildWorkflowStub

        public static <T> T newChildWorkflowStub​(java.lang.Class<T> workflowInterface,
                                                 ChildWorkflowOptions options)
        Creates client stub that can be used to start a child workflow that implements given interface. Use newExternalWorkflowStub(Class, String) to get a stub to signal a workflow without starting it.
        Parameters:
        workflowInterface - interface type implemented by activities
        options - options passed to the child workflow.
      • newExternalWorkflowStub

        public static <R> R newExternalWorkflowStub​(java.lang.Class<? extends R> workflowInterface,
                                                    java.lang.String workflowId)
        Creates client stub that can be used to communicate to an existing workflow execution.
        Parameters:
        workflowInterface - interface type implemented by activities
        workflowId - id of the workflow to communicate with.
      • newExternalWorkflowStub

        public static <R> R newExternalWorkflowStub​(java.lang.Class<? extends R> workflowInterface,
                                                    WorkflowExecution execution)
        Creates client stub that can be used to communicate to an existing workflow execution.
        Parameters:
        workflowInterface - interface type implemented by activities
        execution - execution of the workflow to communicate with.
      • newUntypedChildWorkflowStub

        public static ChildWorkflowStub newUntypedChildWorkflowStub​(java.lang.String workflowType,
                                                                    ChildWorkflowOptions options)
        Creates untyped client stub that can be used to start and signal a child workflow.
        Parameters:
        workflowType - name of the workflow type to start.
        options - options passed to the child workflow.
      • newUntypedChildWorkflowStub

        public static ChildWorkflowStub newUntypedChildWorkflowStub​(java.lang.String workflowType)
        Creates untyped client stub that can be used to start and signal a child workflow. All options are inherited from the parent.
        Parameters:
        workflowType - name of the workflow type to start.
      • newUntypedExternalWorkflowStub

        public static ExternalWorkflowStub newUntypedExternalWorkflowStub​(WorkflowExecution execution)
        Creates untyped client stub that can be used to signal or cancel a child workflow.
        Parameters:
        execution - execution of the workflow to communicate with.
      • newUntypedExternalWorkflowStub

        public static ExternalWorkflowStub newUntypedExternalWorkflowStub​(java.lang.String workflowId)
        Creates untyped client stub that can be used to signal or cancel a child workflow.
        Parameters:
        workflowId - id of the workflow to communicate with.
      • newContinueAsNewStub

        public static <T> T newContinueAsNewStub​(java.lang.Class<T> workflowInterface,
                                                 ContinueAsNewOptions options)
        Creates a client stub that can be used to continue this workflow as a new run.
        Parameters:
        workflowInterface - an interface type implemented by the next run of the workflow
      • newContinueAsNewStub

        public static <T> T newContinueAsNewStub​(java.lang.Class<T> workflowInterface)
        Creates a client stub that can be used to continue this workflow as a new run.
        Parameters:
        workflowInterface - an interface type implemented by the next run of the workflow
      • continueAsNew

        public static void continueAsNew​(java.lang.Object... args)
        Continues the current workflow execution as a new run with the same options.
        Parameters:
        args - arguments of the next run.
        See Also:
        newContinueAsNewStub(Class)
      • continueAsNew

        public static void continueAsNew​(java.util.Optional<java.lang.String> workflowType,
                                         java.util.Optional<ContinueAsNewOptions> options,
                                         java.lang.Object... args)
        Continues the current workflow execution as a new run possibly overriding the workflow type and options.
        Parameters:
        options - option overrides for the next run.
        args - arguments of the next run.
        See Also:
        newContinueAsNewStub(Class)
      • getWorkflowInfo

        public static WorkflowInfo getWorkflowInfo()
      • newCancellationScope

        public static CancellationScope newCancellationScope​(java.lang.Runnable runnable)
        Wraps the Runnable method argument in a CancellationScope. The Runnable.run() calls Runnable.run() on the wrapped Runnable. The returned CancellationScope can be used to cancel the wrapped code. The cancellation semantic depends on the operation the code is blocked on. For example activity or child workflow is first cancelled then throws a CancellationException. The same applies for sleep(long) operation. When an activity or a child workflow is invoked asynchronously then they get cancelled and a Promise that contains their result will throw CancellationException when Promise.get() is called.

        The new cancellation scope is linked to the parent one (available as CancellationScope.current(). If the parent one is cancelled then all the children scopes are cancelled automatically. The main workflow function (annotated with @WorkflowMethod is wrapped within a root cancellation scope which gets cancelled when a workflow is cancelled through the Cadence CancelWorkflowExecution API. To perform cleanup operations that require blocking after the current scope is cancelled use a scope created through newDetachedCancellationScope(Runnable).

        Example of running activities in parallel and cancelling them after a specified timeout.

        
             List> results = new ArrayList<>();
             CancellationScope scope = Workflow.newDetachedCancellationScope(() -> {
                Async.function(activities::a1);
                Async.function(activities::a2);
             });
             scope.run(); // returns immediately as the activities are invoked asynchronously
             Workflow.sleep(Duration.ofHours(1));
             // Cancels any activity in the scope that is still running
             scope.cancel("one hour passed");
        
         
        Parameters:
        runnable - parameter to wrap in a cancellation scope.
        Returns:
        wrapped parameter.
      • newCancellationScope

        public static CancellationScope newCancellationScope​(Functions.Proc1<CancellationScope> proc)
        Wraps a procedure in a CancellationScope. The procedure receives the wrapping CancellationScope as a parameter. Useful when cancellation is requested from within the wrapped code. The following example cancels the sibling activity on any failure.
        
                       Workflow.newCancellationScope(
                           (scope) -> {
                             Promise p1 = Async.proc(activities::a1).exceptionally(ex->
                                {
                                   scope.cancel("a1 failed");
                                   return null;
                                });
        
                             Promise p2 = Async.proc(activities::a2).exceptionally(ex->
                                {
                                   scope.cancel("a2 failed");
                                   return null;
                                });
                             Promise.allOf(p1, p2).get();
                           })
                       .run();
         
        Parameters:
        proc - code to wrap in the cancellation scope
        Returns:
        wrapped proc
      • newDetachedCancellationScope

        public static CancellationScope newDetachedCancellationScope​(java.lang.Runnable runnable)
        Creates a CancellationScope that is not linked to a parent scope. Runnable.run() must be called to execute the code the scope wraps. The detached scope is needed to execute cleanup code after a workflow is cancelled which cancels the root scope that wraps the @WorkflowMethod invocation. Here is an example usage:
        
          try {
             // workflow logic
          } catch (CancellationException e) {
             CancellationScope detached = Workflow.newDetachedCancellationScope(() -> {
                 // cleanup logic
             });
             detached.run();
          }
         
        Parameters:
        runnable - parameter to wrap in a cancellation scope.
        Returns:
        wrapped parameter.
        See Also:
        newCancellationScope(Runnable)
      • newTimer

        public static Promise<java.lang.Void> newTimer​(java.time.Duration delay)
        Create new timer. Note that Cadence service time resolution is in seconds. So all durations are rounded up to the nearest second.
        Returns:
        feature that becomes ready when at least specified number of seconds passes. promise is failed with CancellationException if enclosing scope is cancelled.
      • newQueue

        public static <E> WorkflowQueue<E> newQueue​(int capacity)
      • newPromise

        public static <E> Promise<E> newPromise​(E value)
      • newFailedPromise

        public static <E> Promise<E> newFailedPromise​(java.lang.Exception failure)
      • registerQuery

        public static void registerQuery​(java.lang.Object queryImplementation)
        Register query or queries implementation object. There is no need to register top level workflow implementation object as it is done implicitly. Only methods annotated with @QueryMethod are registered.
      • currentTimeMillis

        public static long currentTimeMillis()
        Must be used to get current time instead of System.currentTimeMillis() to guarantee determinism.
      • sleep

        public static void sleep​(java.time.Duration duration)
        Must be called instead of Thread.sleep(long) to guarantee determinism.
      • sleep

        public static void sleep​(long millis)
        Must be called instead of Thread.sleep(long) to guarantee determinism.
      • await

        public static void await​(java.util.function.Supplier<java.lang.Boolean> unblockCondition)
        Block current thread until unblockCondition is evaluated to true.
        Parameters:
        unblockCondition - condition that should return true to indicate that thread should unblock.
        Throws:
        java.util.concurrent.CancellationException - if thread (or current CancellationScope was cancelled).
      • await

        public static boolean await​(java.time.Duration timeout,
                                    java.util.function.Supplier<java.lang.Boolean> unblockCondition)
        Block current workflow thread until unblockCondition is evaluated to true or timeoutMillis passes.
        Returns:
        false if timed out.
        Throws:
        java.util.concurrent.CancellationException - if thread (or current CancellationScope was cancelled).
      • retry

        public static <R> R retry​(RetryOptions options,
                                  Functions.Func<R> fn)
        Invokes function retrying in case of failures according to retry options. Synchronous variant. Use Async.retry(RetryOptions, Functions.Func) for asynchronous functions.
        Parameters:
        options - retry options that specify retry policy
        fn - function to invoke and retry
        Returns:
        result of the function or the last failure.
      • retry

        public static void retry​(RetryOptions options,
                                 Functions.Proc proc)
        Invokes function retrying in case of failures according to retry options. Synchronous variant. Use Async.retry(RetryOptions, Functions.Func) for asynchronous functions.
        Parameters:
        options - retry options that specify retry policy
        proc - procedure to invoke and retry
      • wrap

        public static java.lang.RuntimeException wrap​(java.lang.Exception e)
        If there is a need to return a checked exception from a workflow implementation do not add the exception to a method signature but wrap it using this method before rethrowing. The library code will unwrap it automatically using unwrap(Exception) when propagating exception to a remote caller. RuntimeException are just returned from this method without modification.

        The reason for such design is that returning originally thrown exception from a remote call (which child workflow and activity invocations are ) would not allow adding context information about a failure, like activity and child workflow id. So stubs always throw a subclass of ActivityException from calls to an activity and subclass of ChildWorkflowException from calls to a child workflow. The original exception is attached as a cause to these wrapper exceptions. So as exceptions are always wrapped adding checked ones to method signature causes more pain than benefit.

         try {
             return someCall();
         } catch (Exception e) {
             throw CheckedExceptionWrapper.wrap(e);
         }
         
        *
        Returns:
        CheckedExceptionWrapper if e is checked or original exception if e extends RuntimeException.
      • unwrap

        public static java.lang.Exception unwrap​(java.lang.Exception e)
        Removes CheckedExceptionWrapper from causal exception chain.
        Parameters:
        e - exception with causality chain that might contain wrapped exceptions.
        Returns:
        exception causality chain with CheckedExceptionWrapper removed.
      • randomUUID

        public static java.util.UUID randomUUID()
        Replay safe way to generate UUID.

        Must be used instead of UUID.randomUUID() which relies on a random generator, thus leads to non deterministic code which is prohibited inside a workflow.

      • newRandom

        public static java.util.Random newRandom()
        Replay safe random numbers generator. Seeded differently for each workflow instance.
      • isReplaying

        public static boolean isReplaying()
        True if workflow code is being replayed. Warning! Never make workflow logic depend on this flag as it is going to break determinism. The only reasonable uses for this flag are deduping external never failing side effects like logging or metric reporting.
      • sideEffect

        public static <R> R sideEffect​(java.lang.Class<R> resultClass,
                                       Functions.Func<R> func)
        Executes the provided function once, records its result into the workflow history. The recorded result on history will be returned without executing the provided function during replay. This guarantees the deterministic requirement for workflow as the exact same result will be returned in replay. Common use case is to run some short non-deterministic code in workflow, like getting random number. The only way to fail SideEffect is to panic which causes decision task failure. The decision task after timeout is rescheduled and re-executed giving SideEffect another chance to succeed.

        Caution: do not use sideEffect function to modify any workflow state. Only use the SideEffect's return value. For example this code is BROKEN:

        
          // Bad example:
          AtomicInteger random = new AtomicInteger();
          Workflow.sideEffect(() -> {
                 random.set(random.nextInt(100));
                 return null;
          });
          // random will always be 0 in replay, thus this code is non-deterministic
          if random.get() < 50 {
                 ....
          } else {
                 ....
          }
         
        On replay the provided function is not executed, the random will always be 0, and the workflow could takes a different path breaking the determinism.

        Here is the correct way to use sideEffect:

        
          // Good example:
          int random = Workflow.sideEffect(Integer.class, () -> random.nextInt(100));
          if random < 50 {
                 ....
          } else {
                 ....
          }
         
        If function throws any exception it is not delivered to the workflow code. It is wrapped in Error causing failure of the current decision.
        Parameters:
        resultClass - type of the side effect
        func - function that returns side effect value
        Returns:
        value of the side effect
        See Also:
        mutableSideEffect(String, Class, BiPredicate, Functions.Func)
      • sideEffect

        public static <R> R sideEffect​(java.lang.Class<R> resultClass,
                                       java.lang.reflect.Type resultType,
                                       Functions.Func<R> func)
        Executes the provided function once, records its result into the workflow history. The recorded result on history will be returned without executing the provided function during replay. This guarantees the deterministic requirement for workflow as the exact same result will be returned in replay. Common use case is to run some short non-deterministic code in workflow, like getting random number. The only way to fail SideEffect is to panic which causes decision task failure. The decision task after timeout is rescheduled and re-executed giving SideEffect another chance to succeed.

        Caution: do not use sideEffect function to modify any workflow state. Only use the SideEffect's return value. For example this code is BROKEN:

        
          // Bad example:
          AtomicInteger random = new AtomicInteger();
          Workflow.sideEffect(() -> {
                 random.set(random.nextInt(100));
                 return null;
          });
          // random will always be 0 in replay, thus this code is non-deterministic
          if random.get() < 50 {
                 ....
          } else {
                 ....
          }
         
        On replay the provided function is not executed, the random will always be 0, and the workflow could takes a different path breaking the determinism.

        Here is the correct way to use sideEffect:

        
          // Good example:
          int random = Workflow.sideEffect(Integer.class, () -> random.nextInt(100));
          if random < 50 {
                 ....
          } else {
                 ....
          }
         
        If function throws any exception it is not delivered to the workflow code. It is wrapped in Error causing failure of the current decision.
        Parameters:
        resultClass - class of the side effect
        resultType - type of the side effect. Differs from resultClass for generic types.
        func - function that returns side effect value
        Returns:
        value of the side effect
        See Also:
        mutableSideEffect(String, Class, BiPredicate, Functions.Func)
      • mutableSideEffect

        public static <R> R mutableSideEffect​(java.lang.String id,
                                              java.lang.Class<R> resultClass,
                                              java.util.function.BiPredicate<R,​R> updated,
                                              Functions.Func<R> func)
        mutableSideEffect is similar to sideEffect(Class, Functions.Func) in allowing calls of non-deterministic functions from workflow code.

        The difference between mutableSideEffect and sideEffect(Class, Functions.Func) is that every new sideEffect call in non-replay mode results in a new marker event recorded into the history. However, mutableSideEffect only records a new marker if a value has changed. During the replay, mutableSideEffect will not execute the function again, but it will return the exact same value as it was returning during the non-replay run.

        One good use case of mutableSideEffect is to access a dynamically changing config without breaking determinism. Even if called very frequently the config value is recorded only when it changes not causing any performance degradation due to a large history size.

        Caution: do not use mutableSideEffect function to modify any workflow sate. Only use the mutableSideEffect's return value.

        If function throws any exception it is not delivered to the workflow code. It is wrapped in Error causing failure of the current decision.

        Parameters:
        id - unique identifier of this side effect
        updated - used to decide if a new value should be recorded. A func result is recorded only if call to updated with stored and a new value as arguments returns true. It is not called for the first value.
        resultClass - class of the side effect
        func - function that produces a value. This function can contain non deterministic code.
        See Also:
        sideEffect(Class, Functions.Func)
      • mutableSideEffect

        public static <R> R mutableSideEffect​(java.lang.String id,
                                              java.lang.Class<R> resultClass,
                                              java.lang.reflect.Type resultType,
                                              java.util.function.BiPredicate<R,​R> updated,
                                              Functions.Func<R> func)
        mutableSideEffect is similar to sideEffect(Class, Functions.Func) in allowing calls of non-deterministic functions from workflow code.

        The difference between mutableSideEffect and sideEffect(Class, Functions.Func) is that every new sideEffect call in non-replay mode results in a new marker event recorded into the history. However, mutableSideEffect only records a new marker if a value has changed. During the replay, mutableSideEffect will not execute the function again, but it will return the exact same value as it was returning during the non-replay run.

        One good use case of mutableSideEffect is to access a dynamically changing config without breaking determinism. Even if called very frequently the config value is recorded only when it changes not causing any performance degradation due to a large history size.

        Caution: do not use mutableSideEffect function to modify any workflow sate. Only use the mutableSideEffect's return value.

        If function throws any exception it is not delivered to the workflow code. It is wrapped in Error causing failure of the current decision.

        Parameters:
        id - unique identifier of this side effect
        updated - used to decide if a new value should be recorded. A func result is recorded only if call to updated with stored and a new value as arguments returns true. It is not called for the first value.
        resultClass - class of the side effect
        resultType - type of the side effect. Differs from resultClass for generic types.
        func - function that produces a value. This function can contain non deterministic code.
        See Also:
        sideEffect(Class, Functions.Func)
      • getVersion

        public static int getVersion​(java.lang.String changeID,
                                     int minSupported,
                                     int maxSupported)
        getVersion is used to safely perform backwards incompatible changes to workflow definitions. It is not allowed to update workflow code while there are workflows running as it is going to break determinism. The solution is to have both old code that is used to replay existing workflows as well as the new one that is used when it is executed for the first time.\

        getVersion returns maxSupported version when is executed for the first time. This version is recorded into the workflow history as a marker event. Even if maxSupported version is changed the version that was recorded is returned on replay. DefaultVersion constant contains version of code that wasn't versioned before.

        For example initially workflow has the following code:

        
         result = testActivities.activity1();
         
        it should be updated to
        
         result = testActivities.activity2();
         
        The backwards compatible way to execute the update is
        
         int version = Workflow.getVersion("fooChange", Workflow.DEFAULT_VERSION, 1);
         String result;
         if (version == Workflow.DEFAULT_VERSION) {
           result = testActivities.activity1();
         } else {
           result = testActivities.activity2();
         }
         
        Then later if we want to have another change:
        
         int version = Workflow.getVersion("fooChange", Workflow.DEFAULT_VERSION, 2);
         String result;
         if (version == Workflow.DEFAULT_VERSION) {
           result = testActivities.activity1();
         } else if (version == 1) {
           result = testActivities.activity2();
         } else {
           result = testActivities.activity3();
         }
         
        Later when there are no workflow executions running DefaultVersion the correspondent branch can be removed:
        
         int version = Workflow.getVersion("fooChange", 1, 2);
         String result;
         if (version == 1) {
           result = testActivities.activity2();
         } else {
           result = testActivities.activity3();
         }
         
        It is recommended to keep the GetVersion() call even if single branch is left:
        
         Workflow.getVersion("fooChange", 2, 2);
         result = testActivities.activity3();
         
        The reason to keep it is: 1) it ensures that if there is older version execution still running, it will fail here and not proceed; 2) if you ever need to make more changes for “fooChange”, for example change activity3 to activity4, you just need to update the maxVersion from 2 to 3.

        Note that, you only need to preserve the first call to GetVersion() for each changeID. All subsequent call to GetVersion() with same changeID are safe to remove. However, if you really want to get rid of the first GetVersion() call as well, you can do so, but you need to make sure: 1) all older version executions are completed; 2) you can no longer use “fooChange” as changeID. If you ever need to make changes to that same part, you would need to use a different changeID like “fooChange-fix2”, and start minVersion from DefaultVersion again.

        Parameters:
        changeID - identifier of a particular change. All calls to getVersion that share a changeID are guaranteed to return the same version number. Use this to perform multiple coordinated changes that should be enabled together.
        minSupported - min version supported for the change
        maxSupported - max version supported for the change
        Returns:
        version
      • getMetricsScope

        public static com.uber.m3.tally.Scope getMetricsScope()
        Get scope for reporting business metrics in workflow logic. This should be used instead of creating new metrics scopes as it is able to dedup metrics during replay.

        The original metrics scope is set through WorkerOptions when a worker starts up.

      • getLogger

        public static org.slf4j.Logger getLogger​(java.lang.Class<?> clazz)
        Get logger to use inside workflow. Logs in replay mode are omitted unless enableLoggingInReplay is set to true in WorkerOptions when a worker starts up.
        Parameters:
        clazz - class name to appear in logging.
        Returns:
        logger to use in workflow logic.
      • getLogger

        public static org.slf4j.Logger getLogger​(java.lang.String name)
        Get logger to use inside workflow. Logs in replay mode are omitted unless enableLoggingInReplay is set to true in WorkerOptions when a worker starts up.
        Parameters:
        name - name to appear in logging.
        Returns:
        logger to use in workflow logic.
      • getLastCompletionResult

        public static <R> R getLastCompletionResult​(java.lang.Class<R> resultClass)
        GetLastCompletionResult extract last completion result from previous run for this cron workflow. This is used in combination with cron schedule. A workflow can be started with an optional cron schedule. If a cron workflow wants to pass some data to next schedule, it can return any data and that data will become available when next run starts.
        Parameters:
        resultClass - class of the return data from last run
        Returns:
        result of last run
      • getLastCompletionResult

        public static <R> R getLastCompletionResult​(java.lang.Class<R> resultClass,
                                                    java.lang.reflect.Type resultType)
        GetLastCompletionResult extract last completion result from previous run for this cron workflow. This is used in combination with cron schedule. A workflow can be started with an optional cron schedule. If a cron workflow wants to pass some data to next schedule, it can return any data and that data will become available when next run starts.
        Parameters:
        resultClass - class of the return data from last run
        resultType - type of the return data from last run. Differs from resultClass for generic types.
        Returns:
        result of last run
      • upsertSearchAttributes

        public static void upsertSearchAttributes​(java.util.Map<java.lang.String,​java.lang.Object> searchAttributes)
        upsertSearchAttributes is used to add or update workflow search attributes. The search attributes can be used in query of List/Scan/Count workflow APIs. The key and value type must be registered on cadence server side; The value has to be Json serializable. UpsertSearchAttributes will merge attributes to existing map in workflow, for example workflow code:
        
             Map attr1 = new HashMap<>();
             attr1.put("CustomIntField", 1);
             attr1.put("CustomBoolField", true);
             Workflow.upsertSearchAttributes(attr1);
        
             Map attr2 = new HashMap<>();
             attr2.put("CustomIntField", 2);
             attr2.put("CustomKeywordField", "Seattle");
             Workflow.upsertSearchAttributes(attr2);
         
        will eventually have search attributes as:
        
             {
               "CustomIntField": 2,
               "CustomBoolField": true,
               "CustomKeywordField": "Seattle",
             }
         
        Parameters:
        searchAttributes - map of String to Object value that can be used to search in list APIs