Workflow
Workflow Versioning
Changing the interface
Class name
As a workflow is internally named through its interface's class name (including package), we need to keep it stable. We can change it, as long as we use the @Name
annotation to ensure that the internal name does not change. For example,
package com.company.sequences;
public interface MySequence {
RunOutput run(RunInput input);
}
package com.company.sequences
interface MySequence {
fun run(input: RunInput): RunOutput
}
can be changed to:
package com.company.workflows;
import io.infinitic.annotations.Name;
@Name(name = "com.company.sequences.MySequence")
public interface MyWorkflow {
RunOutput run(RunInput input);
}
package com.company.workflows
import io.infinitic.annotations.Name
@Name("com.company.sequences.MySequence")
interface MyWorkflow {
fun run(input: RunInput): RunOutput
}
We recommend always using a @Name
annotation - with a simple name - for all workflows, to avoid keeping legacy names like here.
Method name
As a workflow is started through its interface's method name, we need to keep it stable. We can change also it, as long as we use the @Name
annotation to ensure that the internal name does not change. For example,
public interface MyWorkflow {
RunOutput run(RunInput input);
}
interface MyWorkflow {
fun run(input: RunInput): RunOutput
}
can be changed to:
import io.infinitic.annotations.Name;
public interface MyWorkflow {
@Name(name = "run")
RunOutput execute(RunInput input);
}
import io.infinitic.annotations.Name;
interface MyWorkflow {
@Name("run")
fun execute(input: RunInput): RunOutput
}
Method parameters
Parameters of the method used to start a workflow are serialized and stored in the workflow history. During its execution, the workflow will deserialize the parameters and apply them to the current method multiple times - that's why:
The signature of a workflow's method must not change.
And if the only parameter of a workflow's method is object type (which is recommended):
Any properties we add to this object type must have default values.
Method return value
If the return value is used in another workflow (as a result of a sub-workflow) - then the same constraint applies to the return value of a task:
The return type of a workflow can not change.
And if the return type is an object (which is recommended):
Any properties we add to the return type must have default values.
Changing the implementation
New workflows
The easiest way to update a workflow is to have the new instances use the latest version, while the current instances continue to run the version they were started with.
By convention:
- a new implementation will be named with a postfix
_n
, wheren
is an integer - no postfix means version 0 For example:
package com.company.workflows;
import io.infinitic.workflows.Workflow;
public class MyWorkflow_1 extends Workflow implements MyWorkflow {
public RunOutput run(input: RunInput) {
/* modified implementation */
}
}
package com.company.workflows
import io.infinitic.workflows.Workflow
class MyWorkflowImpl_1 : Workflow(), MyWorkflow {
override fun run(input: RunInput): RunOutput { /* */ }
}
Those multiple versions can be registered in workers, through the configuration file:
workflows:
- name: MyWorkflow
classes:
- com.company.workflows.MyWorkflowImpl
- com.company.workflows.MyWorkflowImpl_1
- com.company.workflows.MyWorkflowImpl_2
- com.company.workflows.MyWorkflowImpl_3
concurrency: 10
or through direct registration:
WorkflowExecutorConfig workflowExecutorConfig = WorkflowExecutorConfig.builder()
.setWorkflowName("MyWorkflow")
.addFactory(() -> new MyWorkflowImpl(/* injections here*/))
.addFactory(() -> new MyWorkflowImpl_1(/* injections here*/))
.addFactory(() -> new MyWorkflowImpl_2(/* injections here*/))
.addFactory(() -> new MyWorkflowImpl_3(/* injections here*/))
.setConcurrency(10)
.build();
InfiniticWorker worker = InfiniticWorker.builder()
.setTransport(transportConfig)
.addWorkflowExecutor(workflowExecutorConfig)
.build();
val workflowExecutorConfig = WorkflowExecutorConfig.builder()
.setWorkflowName("MyWorkflow")
.addFactory { MyWorkflowImpl(/* injections here*/) }
.addFactory { MyWorkflowImpl_1(/* injections here*/) }
.addFactory { MyWorkflowImpl_2(/* injections here*/) }
.addFactory { MyWorkflowImpl_3(/* injections here*/) }
.setConcurrency(10)
.build();
val worker = InfiniticWorker.builder()
.setTransport(transportConfig)
.addWorkflowExecutor(workflowExecutorConfig)
.build()
A newly dispatched workflow will automatically use the highest version available in the Workflow worker - and will stick to this version up to its completion.
Workflow workers should know all versions of a workflow for which at least one instance is still running.
Running workflows
Changing the implementation of an already running workflow is not yet supported by Infinitic.