AWS Step Functions — Are Active Executions Affected When Your State Machine Gets Updated?
Table of Contents
Intro #
AWS Step Functions is a Serverless orchestration service that helps to build a workflow with various steps that connect different AWS services. These steps are defined in State Machine, which will be implemented in Amazon States Language (ASL).
There are two types of workflows in Step Functions:
- Express flow
- Standard flow
Among many differences, Express flow can only run up to five minutes and Standard flow can run up to one year.
Since even five minutes is a long time, there is a possibility of having an active execution when the state machine is updated. In this article, I am going to discuss how such active executions are affected when the State Machine is updated and how to handle those scenarios.
Let’s assume we have a simple state machine with two Lambda functions as follows. There is a “Wait” state with a three-minute wait time in between Lambda calls.
Scenario 1 #
Here, we will run the above state machine, and while there is an active execution, we will update the state machine by adding a third Lambda function after the second Lambda function.
The result is that the active execution is not affected.
The reason is when a new execution initialised, the Step Function keeps a ‘snapshot’ of the State Machine and any updates to the State Machine is not affect this ‘snapshot.’
Scenario 2 #
Here, we will run the state machine in Fig 1, and this time we will keep the State Machine as it is, but, we will update the second Lambda function code.
The result is that the active execution is affected.
The reason is although Step Function keeps a ‘snapshot’ of the state machine at the start of the execution, the Lambda functions in the state machine are referenced with their ARNs. This is the latest version of Lambda. So, if the Lambda functions are updated, it affects the running execution since it always refers to the latest version of Lambda.
How To Prevent Modifications To Active Executions When Lambda Functions Update? #
There is a simple solution for this: use Lambda versions.
On deployment, create a Lambda version and in the state machine, refer to the Lambda function with the version in the ARN.
Example: arn:aws:lambda:[region]:[accountId]:function:[functionName]:[Version]
An example of this implementation is this CDK code snippet.
Previously (without Lambda versions):
const lambdaFunctionOne = new nodejs_lambda.NodejsFunction(this, "LambdaFunctionOne", {
runtime: lambda.Runtime.NODEJS_14_X,
entry: path.join(__dirname, `/../lambda/FunctionOne/index.ts`),
handler: "handler",
timeout: Duration.minutes(10),
});
const waitForXMinutes = new sfn.Wait(this, 'Wait', {
time: sfn.WaitTime.duration(Duration.minutes(3)),
});
const lambdaFunctionTwo = new nodejs_lambda.NodejsFunction(this, "LambdaFunctionTwo", {
runtime: lambda.Runtime.NODEJS_14_X,
entry: path.join(__dirname, `/../lambda/FunctionTwo/index.ts`),
handler: "handler",
timeout: Duration.minutes(10),
});
const stateMachine = new sfn.StateMachine(this, 'SfTestStateMachine', {
definition: new tasks.LambdaInvoke(this, 'TriggerLambdaFunctionOne', {
lambdaFunction: lambdaFunctionOne
})
.next(waitForXMinutes)
.next(
new tasks.LambdaInvoke(this, 'TriggerLambdaFunctionTwo', {
lambdaFunction: lambdaFunctionTwo
})
)
});
Now (with Lambda versions):
const lambdaFunctionOne = new nodejs_lambda.NodejsFunction(this, "LambdaFunctionOne", {
runtime: lambda.Runtime.NODEJS_14_X,
entry: path.join(__dirname, `/../lambda/FunctionOne/index.ts`),
handler: "handler",
timeout: Duration.minutes(10),
});
const waitForXMinutes = new sfn.Wait(this, 'Wait', {
time: sfn.WaitTime.duration(Duration.minutes(3)),
});
const lambdaFunctionTwo = new nodejs_lambda.NodejsFunction(this, "LambdaFunctionTwo", {
runtime: lambda.Runtime.NODEJS_14_X,
entry: path.join(__dirname, `/../lambda/FunctionTwo/index.ts`),
handler: "handler",
timeout: Duration.minutes(10),
});
const stateMachine = new sfn.StateMachine(this, 'SfTestStateMachine', {
definition: new tasks.LambdaInvoke(this, 'TriggerLambdaFunctionOne', {
lambdaFunction: new lambda.Version(this, 'LambdaFunctionOneVersion', {
lambda: lambdaFunctionOne,
})
})
.next(waitForXMinutes)
.next(
new tasks.LambdaInvoke(this, 'TriggerLambdaFunctionTwo', {
lambdaFunction: new lambda.Version(this, 'LambdaFunctionTwoVersion', {
lambda: lambdaFunctionTwo,
})
})
)
});
Conclusion #
When using Lambda functions within Step Functions, use the Lambda function versions to make sure any active executions are not affected by Lambda updates.
In contrast, if you need any active executions to be updated with the latest Lambda code, do not use Lambda function versions.
Resources #
-
AWS Step Functions Express vs Standard workflows: https://docs.aws.amazon.com/step-functions/latest/dg/concepts-standard-vs-express.html
-
Documentation — Amazon State Language: https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html
-
Documentation — State Machine: https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-state-machine-structure.html
Please feel free to try this and share your experience with me.
Keep building! Keep sharing!
[Header image by davide ragusa on Unsplash]