この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
Step Functionsを使うとき、Lambdaで取得したパラメータに従って制御することがあります。
たとえば、Choice
で分岐させたり、Wait
を使ったりです。
そんなStep Functionsですが、「DynamoDBから取得した数値」をそのまま使うとエラーになっちゃった話です。
エラーになったこと
次のようなステートマシンがあります。最初のInit
で動くLambdaが「DynamoDBから数値を取得」し、その数値を用いてWait
させています。
このとき、DynamoDBから取得した数値をそのまま使うと、Step Functionsがエラーで失敗したのです。
実行イベント履歴のログはこちら。
{
"error": "States.Runtime",
"cause": "An error occurred while executing the state 'Wait' (entered at the event id #7). The SecondsPath parameter cannot be parsed as a long value: $.waitSeconds == 10.0"
}
エラーの原因
上記に書いている通り、StepFunctionのSecondsPath
でlong型が使えないためです。
「long型? どこから?」
「あなたのlong型はDynamoDBから。」
そう、DynamoDBのNumberを取得したとき、PythonではDecimal型として扱われるのです。
Lambdaコード
app.py
import boto3
import json
import os
dynamodb = boto3.resource('dynamodb')
table_name = os.environ['TABLE_NAME']
def lambda_handler(event, context):
table = dynamodb.Table(table_name)
res = table.get_item(Key={
'userId': '1234'
})
wait_seconds = res['Item']['waitSeconds']
print(wait_seconds)
print(type(wait_seconds))
return {
'waitSeconds': wait_seconds
}
AWS SAM定義
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: StepFunctionsSample
Resources:
# StepFunctionsから起動されるLambda
SfnInitFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.7
Timeout: 5
Environment:
Variables:
TABLE_NAME: !Ref ParameterTable
Policies:
- arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess
SfnInitFunctionLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${SfnInitFunction}
# 設定値が格納されているDynamoDBテーブル
ParameterTable:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: userId
AttributeType: S
KeySchema:
- AttributeName: userId
KeyType: HASH
BillingMode: PAY_PER_REQUEST
# StepFunctionsのステートマシン
SampleStateMachine:
Type: AWS::Serverless::StateMachine
Properties:
Name: Sample-State-Machine
Definition:
StartAt: Init
States:
Init:
Type: Task
Resource: !GetAtt SfnInitFunction.Arn
Next: Wait
Wait:
Type: Wait
SecondsPath: $.waitSeconds
End: true
Role: !GetAtt SampleStateMachineRole.Arn
# StepFunction用のIAMロール
SampleStateMachineRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: !Sub states.${AWS::Region}.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaRole
DynamoDBの様子
{
"userId": "1234",
"waitSeconds": 10
}
対応策
簡単です。int型に直しましょう。
app.py
import boto3
import json
import os
dynamodb = boto3.resource('dynamodb')
table_name = os.environ['TABLE_NAME']
def lambda_handler(event, context):
table = dynamodb.Table(table_name)
res = table.get_item(Key={
'userId': '1234'
})
wait_seconds = res['Item']['waitSeconds']
print(wait_seconds)
print(type(wait_seconds))
return {
'waitSeconds': int(wait_seconds)
}
無事に動きました。
さいごに
地味にハマるポイントでした。どなたかの参考になれば幸いです。