Startup Clix: First Steps with AWS Step Functions

May 11, 2018 0 Comments

Startup Clix: First Steps with AWS Step Functions

 

 


Yesterday I finally got presence channel working with Pusher, which I need to check who's in what game and to distribute game updates to them.

Today was about getting myself educated in the art of state-machines with AWS Step Functions.

Step Functions

AWS Step Functions is a serverless (autoscale, pay per use, etc.) service, that lets you define and execute state-machines in the AWS cloud.

It can control Lambda functions and most importantly it can be controlled by Lambda functions.

I created a test implementation to see how this could work.

First I created a state-machine definition and integrated it into my CloudFormation template.yaml. I also created a IAM role that now will be used by Lambda and Step Functions to do their thing.

The definition of a state-machine is generally straight forward, but kinda ugly to integrate into CloudFormation, because it needs to be done as string and the definition is written in JSON.

Anyway, this is the definition inside the template:

 GameStateMachine: Type: "AWS::StepFunctions::StateMachine" Properties: RoleArn: !GetAtt [ ExecutionRole, Arn ] DefinitionString: Fn::Sub: - |- { "StartAt": "WaitingForPlayers", "States": { "WaitingForPlayers": { "Type": "Task", "Resource": "${joinGameActivityArn}", "TimeoutSeconds": 20, "End": true } } } - joinGameActivityArn: !Ref JoinGameActivity JoinGameActivity: Type: "AWS::StepFunctions::Activity" Properties: Name: JoinGame 

As one can see, some string substitution is taking place to get the ARN the JoinGameActivity into the definition. The activity is also defined in the template.

It goes like this:

1) startGame Lambda function is called via API-Gateway

module.exports = async (event, context) => { const executionParams = { // The GameStateMachine ARN is available via env-var // it's passed here by CloudFormation stateMachineArn: process.env.GAMESTATEMACHINEARN, // some input data that is used as start input for the state-machine input: JSON.stringify({ gameId: GAMEID }) }; // starting a new execution via the AWS-SDK await stepFunctions .startExecution(executionParams) .promise(); 
};

2) GameStateMachine execution goes into the WaitingForPlayers state until some kind of worker sends a success via the AWS-SDK or if the timeout hits.

3) joinGame Lambda function is called via API-Gateway

module.exports = async (event, context) => { let task; { const getTaskParams = { // The JoinGame activity ARN is available via env-var // it's passed here by CloudFormation activityArn: process.env.JOINGAMEACTIVITY_ARN }; // If a task for this activity is available it will be polled here task = await stepFunctions.getActivityTask(getTaskParams).promise(); } // some game logic happening, haha const input = JSON.parse(task.input); { // this token is need to send a success or fail state later const { taskToken } = task; const taskSuccessParams = { taskToken, output: JSON.stringify(input) }; // the success is send to the activity // so the GameStateMachine can transition to the next state await stepFunctions.sendTaskSuccess(taskSuccessParams).promise(); } 
};

4) The GameStateMachine transitions to its end state and succeeds execution.

Next

The problem is, there are multiple executions, one for every game, but is still only one JoinGame activity that can be polled. Now, when players for 2 games poll this activity they should only get the activity tasks for their game, which is not possible at the moment.

Well, maybe tomorrow :)


Tag cloud