[AWS Step Functions] 1日1回までしか処理をしないようにステートマシンを制御してみた

Lambda関数を使って、1日1回までしか処理をしないようにステートマシンを制御してみました。
2021.12.14

1日1回までしか処理をしないようにステートマシンを制御したいときもある

こんにちは、のんピ(@non____97)です。

皆さんはジョブ管理システムから抜け出したいと思ったことはありますか? 私は常に思っています。

集計処理のジョブネットなど、1日1回までしかジョブネットを実行させたくない場面もありますよね? 加えてそのようなジョブネットは、運用日付の切替時刻が00:00でないパターンが多いと思います。(ソーシャルゲームのログインボーナス配布時間がAM0:00ではなく、AM3:00やAM4:00が設定されている例がイメージしやすいかもしれません。)

今回はそのようなジョブネットをAWS Step Functionsに移行したいニッチな要望に応えて、1日1回までしか処理をしないようにステートマシンを制御してみます。

いきなりまとめ

  • 同日に既にステートマシンを実行したかどうかはLambda関数で判定する
  • Lambda関数でタイムゾーンの環境変数(TZ)は予約済みであり、変更するのは非推奨
    • UTCとの時差を計算して時間の計算を行う

検証の構成

残念ながらAWS Step Functions単体の機能で過去の実行履歴を確認して、同日に既にステートマシンが実行済みかどうかを判断するかは難しいです。そこで、ステートマシンの同日の実行履歴を確認して、実行された履歴がある場合は1を、ない場合は0を返却するLambda関数を用意して対応します。

Lambda関数からのレスポンスが1の場合は後続の処理をスキップし、0の場合は本来実行すべき処理を行うようにします。

処理の流れ

今回は上述のワークフローをAWS Step Functionsを使って表現し、1日に複数回ステートマシンを実行したときには後続の処理を実行しないことを確認します。

同日実行履歴確認用のLambda関数の説明

同日実行履歴確認用のLambda関数では、引数で渡されたステートマシンが運用日付切替基準時刻以降に実行されたかを確認します。運用日付切替基準時刻以降に実行された履歴がある場合は1を、ない場合は0を返却します。

運用日付切替基準時刻は環境変数BASE_LOCAL_TIMEで渡します。例えば毎日日本時間のAM7:30に運用日付が切り替わるようにしたいのであれば、Lambda関数の環境変数BASE_LOCAL_TIME07:30を指定します。

注意すべき点はLambda関数内でDate.now()で取得できる時刻及び、AWS Step Functionsの実行一覧を表示するAPIであるListExecutionsで取得できる実行開始時刻はUTCである点です。そのため、ローカルタイムゾーンの運用日付切替基準時刻を計算時する際には時差を意識する必要があります。

「時差を修正する = タイムゾーンを修正する」場合、環境変数で修正すれば良いかと考えられる方も多いかもしれません。しかし、AWS公式ドキュメントで紹介されている通り、タイムゾーンの環境変数(TZ)は予約済みであり、関数設定では設定できないとなっています。

定義されたランタイム環境変数

Lambda ランタイムは、初期化中にいくつかの環境変数を設定します。ほとんどの環境変数は、関数またはランタイムに関する情報を提供します。これらの環境変数のキーは予約済みであるため、関数設定では設定できません。

予約済み環境変数

  • _HANDLER - 関数に設定されているハンドラの場所。
  • _X_AMZN_TRACE_ID - X-Ray トレースヘッダー。
  • AWS_REGION - Lambda 関数が実行される AWS リージョン。
  • AWS_EXECUTION_ENVAWS_Lambda_ のプレフィックスが付いたランタイム識別子 (例: AWS_Lambda_java8)。
  • AWS_LAMBDA_FUNCTION_NAME - 関数の名前。
  • AWS_LAMBDA_FUNCTION_MEMORY_SIZE - 関数で使用できるメモリの量 (MB 単位)。
  • AWS_LAMBDA_FUNCTION_VERSION - 実行される関数のバージョン。
  • AWS_LAMBDA_INITIALIZATION_TYPEon-demand または provisioned-concurrency のいずれかがで指定される、関数の初期化処理のタイプ。詳細については、「プロビジョニングされた同時実行数の設定」を参照してください。
  • AWS_LAMBDA_LOG_GROUP_NAME、AWS_LAMBDA_LOG_STREAM_NAME - Amazon CloudWatch Logs グループの名前と関数のストリーム。
  • AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_SESSION_TOKEN - 関数の実行ロールから取得したアクセスキー。
  • AWS_LAMBDA_RUNTIME_API - (カスタムランタイム) ランタイム API のホストおよびポート。
  • LAMBDA_TASK_ROOT - Lambda 関数コードへのパス。
  • LAMBDA_RUNTIME_DIR - ランタイムライブラリへのパス。
  • TZ - 環境のタイムゾーン (UTC)。実行環境は、システムクロックを同期するために NTP を使用します。

定義されたランタイム環境変数

そのため、今回は以下のようにローカルタイムゾーンとUTCとの時差の環境変数(UTC_OFFSET)を受け取り、時差を修正しました。

./src/lambda/functions/same-day-execution-history-check.ts

  const stateMachineArn: string = event.stateMachineArn;
  const utcOffset: number = Number(process.env["UTC_OFFSET"]);
  const baseLocalTime: string[] = process.env["BASE_LOCAL_TIME"].split(":");
  const regionName: string = process.env["REGION_NAME"];

  // Set State Machine Client
  const sfnClient = new SFNClient({
    region: regionName,
  });

  // Time difference from UTC (millisecond)
  const utcOffsetMillisecond =
    (new Date().getTimezoneOffset() + utcOffset * 60) * 60 * 1000;

  // Set the current local date.
  const currentLocalDate = new Date(Date.now() + utcOffsetMillisecond);

現在のローカルタイムゾーンの日時を求められたら、運用日付切替日時を計算します。

運用日付切替日時は、ステートマシンを実行した際、この運用日付切替日時よりも後に対象ステートマシンが実行されたかどうかを判断するための基準となる日時です。

運用日付切替基準時刻がAM7:30の場合の、ステートマシン実行日時と運用日付切替日時の関係性を分かりやすくするために、3パターンほど以下に記載します。

  • ステートマシン実行日時 : 12/12 22:00
    • 運用日付切替日時 : 12/12 7:30
  • ステートマシン実行日時 : 12/13 5:00
    • 運用日付切替日時 : 12/12 7:30
  • ステートマシン実行日時 : 12/13 8:00
    • 運用日付切替日時 : 12/13 7:30

ポイントは「ステートマシン実行日時 : 12/13 5:00」のパターンです。

他のパターンはステートマシン実行日時と、運用日付切替日時の日付が同じです。しかし、「ステートマシン実行日時 : 12/13 5:00」のパターンは、00:00を超えていますが、運用日付切替基準時刻であるAM7:30を過ぎていないため、運用日付切替の日付は前日の12/12となります。

このような処理を行うために、運用日付切替日時の日付を仮でステートマシン実行日時と同じ日付にし、ステートマシン実行日時が仮の運用日付切替日時よりも前である場合は、「運用日付切替日時 = 仮の運用日付切替日時 - 1日」にするようにしました。

該当処理のコードは以下の通りです。

./src/lambda/functions/same-day-execution-history-check.ts

  // Convert base date to local date
  const tempBaseLocalDateMillisecond = new Date(
    currentLocalDate.getFullYear(),
    currentLocalDate.getMonth(),
    currentLocalDate.getDate(),
    Number(baseLocalTime[0]),
    Number(baseLocalTime[1])
  ).getTime();

  // Calculating the base time for a date change (UTC)
  const baseUtcDate =
    currentLocalDate.getTime() < tempBaseLocalDateMillisecond
      ? new Date(
          tempBaseLocalDateMillisecond -
            utcOffsetMillisecond -
            24 * 60 * 60 * 1000
        )
      : new Date(tempBaseLocalDateMillisecond - utcOffsetMillisecond);

その後は、AWS Step Functionsの実行一覧を表示するAPIであるListExecutionsで前回のステートマシン実行開始時刻を確認し、運用日付切替日時と比較します。

比較した結果、運用日付切替基準時刻以降に実行されていた場合は1を、されていない場合は0を返却します。

同日実行履歴確認用Lambda関数全体のコードは以下の通りです。

./src/lambda/functions/same-day-execution-history-check.ts

import { SFNClient, ListExecutionsCommand } from "@aws-sdk/client-sfn";

interface Event {
  stateMachineArn: string;
}

export const handler = async (event: Event): Promise<number> => {
  if (!event.stateMachineArn) {
    console.log("The argument does not specify the Arn of the State Machine.");
    return 1;
  }

  if (
    !process.env["UTC_OFFSET"] ||
    isNaN(Number(process.env["UTC_OFFSET"])) ||
    Number(process.env["UTC_OFFSET"]) > 14 ||
    Number(process.env["UTC_OFFSET"]) < -12
  ) {
    console.log(`
      The environment variable for UTC offset (UTC_OFFSET) has not been entered correctly.
      e.g. For Asia/Tokyo, "9". For America/Los_Angeles, "-8".`);
    return 1;
  }

  if (
    !process.env["BASE_LOCAL_TIME"] ||
    !process.env["BASE_LOCAL_TIME"].match(/^([01][0-9]|2[0-3]):[0-5][0-9]$/)
  ) {
    console.log(`
      The environment variable for base time (BASE_LOCAL_TIME) has not been entered correctly.
      e.g. 07:30`);
    return 1;
  }

  if (!process.env["REGION_NAME"]) {
    console.log(
      `The region name environment variable (REGION_NAME) is not specified.
      e.g. us-east-1`
    );
    return 1;
  }

  const stateMachineArn: string = event.stateMachineArn;
  const utcOffset: number = Number(process.env["UTC_OFFSET"]);
  const baseLocalTime: string[] = process.env["BASE_LOCAL_TIME"].split(":");
  const regionName: string = process.env["REGION_NAME"];

  // Set State Machine Client
  const sfnClient = new SFNClient({
    region: regionName,
  });

  // Time difference from UTC (millisecond)
  const utcOffsetMillisecond =
    (new Date().getTimezoneOffset() + utcOffset * 60) * 60 * 1000;

  // Set the current local date.
  const currentLocalDate = new Date(Date.now() + utcOffsetMillisecond);

  // Convert base date to local date
  const tempBaseLocalDateMillisecond = new Date(
    currentLocalDate.getFullYear(),
    currentLocalDate.getMonth(),
    currentLocalDate.getDate(),
    Number(baseLocalTime[0]),
    Number(baseLocalTime[1])
  ).getTime();

  // Calculating the base time for a date change (UTC)
  const baseUtcDate =
    currentLocalDate.getTime() < tempBaseLocalDateMillisecond
      ? new Date(
          tempBaseLocalDateMillisecond -
            utcOffsetMillisecond -
            24 * 60 * 60 * 1000
        )
      : new Date(tempBaseLocalDateMillisecond - utcOffsetMillisecond);

  // Get a list of the execution history for the specified State Machine.
  const response = await sfnClient
    .send(new ListExecutionsCommand({ stateMachineArn: stateMachineArn }))
    .catch((error: any) => {
      console.error("ListExecutionsCommand failed. \n\n", error);
    });

  console.log(response);
  console.log(`baseUtcDate : ${baseUtcDate}`);

  // Return 1 if the specified State Machine has a history of being executed after the base time.
  if (
    !response?.executions ||
    (response?.executions[1]?.startDate &&
      response.executions[1].startDate > baseUtcDate)
  ) {
    return 1;
  }

  return 0;
};

やってみた

各種リソースのデプロイ

Lambda関数やステートマシンなど環境一式をAWS CDKでデプロイします。なお、AWS CDK関連のコードは長くなったので、以下に折りたたみます。

AWS CDK関連の情報

AWS CDKのディレクトリ構成

> tree
.
├── .gitignore
├── .npmignore
├── README.md
├── bin
│   └── app.ts
├── cdk.json
├── jest.config.js
├── lib
│   ├── lambda-stack.ts
│   └── state-machine-stack.ts
├── package-lock.json
├── package.json
├── src
│   └── lambda
│       └── functions
│           └── same-day-execution-history-check.ts
├── test
│   └── app.test.ts
└── tsconfig.json

6 directories, 13 files

package.json

{
  "name": "app",
  "version": "0.1.0",
  "bin": {
    "app": "bin/app.js"
  },
  "scripts": {
    "build": "tsc",
    "watch": "tsc -w",
    "test": "jest",
    "cdk": "cdk"
  },
  "devDependencies": {
    "@types/jest": "^26.0.10",
    "@types/node": "10.17.27",
    "aws-cdk": "2.1.0",
    "jest": "^26.4.2",
    "ts-jest": "^26.2.0",
    "ts-node": "^9.0.0",
    "typescript": "~3.9.7"
  },
  "dependencies": {
    "@aws-sdk/client-sfn": "^3.43.0",
    "aws-cdk-lib": "2.1.0",
    "constructs": "^10.0.0",
    "source-map-support": "^0.5.16"
  }
}

./bin/app.ts

#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import { LambdaStack } from "../lib/lambda-stack";
import { StateMachineStack } from "../lib/state-machine-stack";

const app = new cdk.App();

const lambdaStack = new LambdaStack(app, "LambdaStack");
new StateMachineStack(app, "StateMachineStack", {
  sameDayExecutionHistoryCheckFunction:
    lambdaStack.sameDayExecutionHistoryCheckFunction,
});

./lib/lambda-stack.ts

import { Stack, StackProps, ScopedAws } from "aws-cdk-lib";
import { Construct } from "constructs";
import * as iam from "aws-cdk-lib/aws-iam";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as nodejs from "aws-cdk-lib/aws-lambda-nodejs";

export class LambdaStack extends Stack {
  public readonly sameDayExecutionHistoryCheckFunction: nodejs.NodejsFunction;

  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    // Declare AWS account ID and region.
    const { accountId, region } = new ScopedAws(this);

    const listExecutionsIamPolicy = new iam.ManagedPolicy(
      this,
      "ListExecutionsIamPolicy",
      {
        statements: [
          new iam.PolicyStatement({
            effect: iam.Effect.ALLOW,
            actions: ["states:ListExecutions"],
            resources: [`arn:aws:states:${region}:${accountId}:stateMachine:*`],
          }),
        ],
      }
    );

    // Create an IAM role for Lambda functions.
    const lambdaIamRole = new iam.Role(this, "LambdaEc2IamRole", {
      assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName(
          "service-role/AWSLambdaBasicExecutionRole"
        ),
        listExecutionsIamPolicy,
      ],
    });

    // Lambda function
    this.sameDayExecutionHistoryCheckFunction = new nodejs.NodejsFunction(
      this,
      "SameDayExecutionHistoryCheckFunction",
      {
        entry: "src/lambda/functions/same-day-execution-history-check.ts",
        runtime: lambda.Runtime.NODEJS_14_X,
        bundling: {
          minify: true,
        },
        environment: {
          UTC_OFFSET: "9",
          BASE_LOCAL_TIME: "07:30",
          REGION_NAME: region,
        },
        role: lambdaIamRole,
      }
    );
  }
}

./lib/state-machine-stack.ts

import { Stack, StackProps, ScopedAws, Fn } from "aws-cdk-lib";
import { Construct } from "constructs";
import * as logs from "aws-cdk-lib/aws-logs";
import * as sfn from "aws-cdk-lib/aws-stepfunctions";
import * as tasks from "aws-cdk-lib/aws-stepfunctions-tasks";
import * as nodejs from "aws-cdk-lib/aws-lambda-nodejs";

interface StateMachineStackProps extends StackProps {
  sameDayExecutionHistoryCheckFunction: nodejs.NodejsFunction;
}

export class StateMachineStack extends Stack {
  public readonly controlStateMachineToRunOnlyOnceADayFunction: nodejs.NodejsFunction;

  constructor(scope: Construct, id: string, props: StateMachineStackProps) {
    super(scope, id, props);

    // Get the string after the stack name in the stack id to append to the end of the Log Group name to make it unique.
    const stackId = new ScopedAws(this).stackId;
    const stackIdAfterStackName = Fn.select(2, Fn.split("/", stackId));

    // Create CloudWatch Logs for Step Functions
    const testStateMachineLogGroup = new logs.LogGroup(
      this,
      "TestStateMachineLogGroup",
      {
        logGroupName: `/aws/vendedlogs/states/testStateMachineLogGroup-${stackIdAfterStackName}`,
        retention: logs.RetentionDays.ONE_WEEK,
      }
    );

    new sfn.StateMachine(this, "TestStateMachine", {
      definition: new tasks.LambdaInvoke(
        this,
        "ControlStateMachineToRunOnlyOncePerDayState",
        {
          resultPath: "$.Payload",
          lambdaFunction: props.sameDayExecutionHistoryCheckFunction,
          payload: sfn.TaskInput.fromObject({
            "stateMachineArn.$": "$$.StateMachine.Id",
          }),
        }
      ).next(
        new sfn.Choice(this, "ChoiceState")
          .otherwise(new sfn.Fail(this, "FailState"))
          .when(
            sfn.Condition.numberEquals("$.Payload.Payload", 0),
            new sfn.Succeed(this, "SucceedState")
          )
      ),
      logs: {
        destination: testStateMachineLogGroup,
        level: sfn.LogLevel.ALL,
      },
    });
  }
}

npx cdk deploy --allで、AWS CDKで定義したリソースをデプロイすると、Lambda関数やステートマシンなど各種リソースが作成されていることが確認できます。

Lambda関数 同日実行履歴確認用のLambda関数

ステートマシン ステートマシン

ステートマシンのワークフロー ステートマシンのワークフロー

なお、今回は同日実行履歴確認用のLambda関数を実行した結果、0が返って来た場合はSucceedState0以外の値が返って来た場合はFailStateに分岐するようにしています。

運用日付切替基準時刻以降に実行された履歴がない場合

それでは、運用日付切替基準時刻以降に実行された履歴がない場合を試してみます。

ステートマシンを選択して、実行の開始をクリックします。実行の開始をクリックすると、名前やJSONの入力を求められます。今回はデフォルトでも問題ないため、何も変更せずに実行の開始をクリックします。

実行の開始

すると、正常にSucceedStateに遷移しました。

運用日付切替基準時刻以降に実行された履歴がない場合のステートマシンの実行結果

その際の同日実行履歴確認用のLambda関数のログは以下の通りです。運用日付切替基準時刻をAM7:30としたので、運用日付切替日時はSun Dec 12 2021 22:30:00 GMT+0000 = 2021/12/13 7:30 (JST)となっています。

START RequestId: ef5b4baf-bf85-4951-bac1-36d02f25f11d Version: $LATEST
2021-12-13T13:13:59.040Z	ef5b4baf-bf85-4951-bac1-36d02f25f11d	INFO	{
  '$metadata': {
    httpStatusCode: 200,
    requestId: 'e361941b-4761-4e04-9e70-ec008dd398d5',
    extendedRequestId: undefined,
    cfId: undefined,
    attempts: 1,
    totalRetryDelay: 0
  },
  executions: [
    {
      executionArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:execution:TestStateMachine3C216BE3-bw4Yr4uTiEdO:a19318d8-a970-31bc-72bd-4e1c00f570b2',
      name: 'a19318d8-a970-31bc-72bd-4e1c00f570b2',
      startDate: 2021-12-13T13:13:57.856Z,
      stateMachineArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:TestStateMachine3C216BE3-bw4Yr4uTiEdO',
      status: 'RUNNING',
      stopDate: undefined
    }
  ],
  nextToken: undefined
}
2021-12-13T13:13:59.098Z	ef5b4baf-bf85-4951-bac1-36d02f25f11d	INFO	baseUtcDate : Sun Dec 12 2021 22:30:00 GMT+0000 (Coordinated Universal Time)
END RequestId: ef5b4baf-bf85-4951-bac1-36d02f25f11d
REPORT RequestId: ef5b4baf-bf85-4951-bac1-36d02f25f11d	Duration: 652.78 ms	Billed Duration: 653 ms	Memory Size: 128 MB	Max Memory Used: 65 MB	Init Duration: 267.74 ms

運用日付切替基準時刻以降に実行された履歴がある場合

続いて、運用日付切替基準時刻以降に実行された履歴がある場合を試します。

前回のステートマシンの実行日時が2021/12/13 22:13 (JST)でした。

前回のステートマシンの実行日時確認

現在の時刻が2021/12/13 22:15 (JST)で、運用日付切替基準時刻をAM7:30としたので、FailStateへの遷移が意図した挙動です。

実行の開始をクリックして、数秒ほど待つと、意図した通りFailStateに遷移しました。

運用日付切替基準時刻以降に実行された履歴がある場合のステートマシン実行結果

その際の同日実行履歴確認用のLambda関数のログは以下の通りです。運用日付切替基準時刻をAM7:30としたので、運用日付切替日時はSun Dec 12 2021 22:30:00 GMT+0000 = 2021/12/13 7:30 (JST)となっています。

START RequestId: 2af99e61-38b9-4e0a-9fdf-67a8bf4de649 Version: $LATEST
2021-12-13T13:15:38.484Z	2af99e61-38b9-4e0a-9fdf-67a8bf4de649	INFO	{
  '$metadata': {
    httpStatusCode: 200,
    requestId: '61f37b82-5fd8-43fd-ab0e-8c97066d4ff6',
    extendedRequestId: undefined,
    cfId: undefined,
    attempts: 1,
    totalRetryDelay: 0
  },
  executions: [
    {
      executionArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:execution:TestStateMachine3C216BE3-bw4Yr4uTiEdO:87562154-f8d9-bc6e-7ef4-7393c6d8ef92',
      name: '87562154-f8d9-bc6e-7ef4-7393c6d8ef92',
      startDate: 2021-12-13T13:15:37.942Z,
      stateMachineArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:TestStateMachine3C216BE3-bw4Yr4uTiEdO',
      status: 'RUNNING',
      stopDate: undefined
    },
    {
      executionArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:execution:TestStateMachine3C216BE3-bw4Yr4uTiEdO:a19318d8-a970-31bc-72bd-4e1c00f570b2',
      name: 'a19318d8-a970-31bc-72bd-4e1c00f570b2',
      startDate: 2021-12-13T13:13:57.856Z,
      stateMachineArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:TestStateMachine3C216BE3-bw4Yr4uTiEdO',
      status: 'SUCCEEDED',
      stopDate: 2021-12-13T13:13:59.298Z
    }
  ],
  nextToken: undefined
}
2021-12-13T13:15:38.484Z	2af99e61-38b9-4e0a-9fdf-67a8bf4de649	INFO	baseUtcDate : Sun Dec 12 2021 22:30:00 GMT+0000 (Coordinated Universal Time)
END RequestId: 2af99e61-38b9-4e0a-9fdf-67a8bf4de649
REPORT RequestId: 2af99e61-38b9-4e0a-9fdf-67a8bf4de649	Duration: 400.47 ms	Billed Duration: 401 ms	Memory Size: 128 MB	Max Memory Used: 65 MB

日付(00:00)をまたいだタイミングで再度実行してみます。

現在の時刻が2021/12/14 3:03 (JST)ですが、運用日付切替基準時刻をAM7:30であり、運用日付切替基準時刻はまたいでいないので、FailStateへの遷移が意図した挙動です。

実行の開始をクリックして、数秒ほど待つと、意図した通りFailStateに遷移しました。

運用日付切替基準時刻以降に実行された履歴がある場合のステートマシン実行結果 (日付またぎ)

その際の同日実行履歴確認用のLambda関数のログは以下の通りです。運用日付切替基準時刻をAM7:30としたので、運用日付切替日時はSun Dec 12 2021 22:30:00 GMT+0000 = 2021/12/13 7:30 (JST)となっています。

START RequestId: abeef55a-5466-4e73-b700-f534c87884f0 Version: $LATEST
2021-12-13T18:03:39.901Z	abeef55a-5466-4e73-b700-f534c87884f0	INFO	{
  '$metadata': {
    httpStatusCode: 200,
    requestId: '5d5062b7-20b2-483f-a4e1-6335271e70da',
    extendedRequestId: undefined,
    cfId: undefined,
    attempts: 1,
    totalRetryDelay: 0
  },
  executions: [
    {
      executionArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:execution:TestStateMachine3C216BE3-bw4Yr4uTiEdO:2b2c5a8f-d1ee-fa04-a15c-0b3f663acbc5',
      name: '2b2c5a8f-d1ee-fa04-a15c-0b3f663acbc5',
      startDate: 2021-12-13T18:03:38.543Z,
      stateMachineArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:TestStateMachine3C216BE3-bw4Yr4uTiEdO',
      status: 'RUNNING',
      stopDate: undefined
    },
    {
      executionArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:execution:TestStateMachine3C216BE3-bw4Yr4uTiEdO:87562154-f8d9-bc6e-7ef4-7393c6d8ef92',
      name: '87562154-f8d9-bc6e-7ef4-7393c6d8ef92',
      startDate: 2021-12-13T13:15:37.942Z,
      stateMachineArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:TestStateMachine3C216BE3-bw4Yr4uTiEdO',
      status: 'FAILED',
      stopDate: 2021-12-13T13:15:38.610Z
    },
    {
      executionArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:execution:TestStateMachine3C216BE3-bw4Yr4uTiEdO:a19318d8-a970-31bc-72bd-4e1c00f570b2',
      name: 'a19318d8-a970-31bc-72bd-4e1c00f570b2',
      startDate: 2021-12-13T13:13:57.856Z,
      stateMachineArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:TestStateMachine3C216BE3-bw4Yr4uTiEdO',
      status: 'SUCCEEDED',
      stopDate: 2021-12-13T13:13:59.298Z
    }
  ],
  nextToken: undefined
}
2021-12-13T18:03:39.903Z	abeef55a-5466-4e73-b700-f534c87884f0	INFO	baseUtcDate : Sun Dec 12 2021 22:30:00 GMT+0000 (Coordinated Universal Time)
END RequestId: abeef55a-5466-4e73-b700-f534c87884f0
REPORT RequestId: abeef55a-5466-4e73-b700-f534c87884f0	Duration: 755.16 ms	Billed Duration: 756 ms	Memory Size: 128 MB	Max Memory Used: 65 MB	Init Duration: 282.45 ms

運用日付切替基準時刻をまたいで再度実行した場合

最後に運用日付切替基準時刻をまたいで再度実行した場合を試してみます。

前回のステートマシンの実行日時が2021/12/14 3:03 (JST)でした。

前回のステートマシンの実行日時確認 2

現在の時刻が2021/12/14 7:57 (JST)で、運用日付切替基準時刻をAM7:30であり、運用日付切替基準時刻はまたいでいるので、SucceedStateへの遷移が意図した挙動です。

実行の開始をクリックして、数秒ほど待つと、意図した通りSucceedStateに遷移しました。

運用日付切替基準時刻をまたいで再度実行した場合のステートマシン実行結果

その際の同日実行履歴確認用のLambda関数のログは以下の通りです。運用日付切替基準時刻をAM7:30としたので、運用日付切替日時はMon Dec 13 2021 22:30:00 GMT+0000 (Coordinated Universal Time) = 2021/12/14 7:30 (JST)となっています。

START RequestId: 380d5f5d-f055-466c-a1e7-9dc1307d61e8 Version: $LATEST
2021-12-13T22:57:53.996Z	380d5f5d-f055-466c-a1e7-9dc1307d61e8	INFO	{
  '$metadata': {
    httpStatusCode: 200,
    requestId: '4e557e3f-fc0e-4398-8f9c-f11b69d5d01f',
    extendedRequestId: undefined,
    cfId: undefined,
    attempts: 1,
    totalRetryDelay: 0
  },
  executions: [
    {
      executionArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:execution:TestStateMachine3C216BE3-bw4Yr4uTiEdO:83d91603-7716-7ea6-4624-eecef8a3804e',
      name: '83d91603-7716-7ea6-4624-eecef8a3804e',
      startDate: 2021-12-13T22:57:52.764Z,
      stateMachineArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:TestStateMachine3C216BE3-bw4Yr4uTiEdO',
      status: 'RUNNING',
      stopDate: undefined
    },
    {
      executionArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:execution:TestStateMachine3C216BE3-bw4Yr4uTiEdO:2b2c5a8f-d1ee-fa04-a15c-0b3f663acbc5',
      name: '2b2c5a8f-d1ee-fa04-a15c-0b3f663acbc5',
      startDate: 2021-12-13T18:03:38.543Z,
      stateMachineArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:TestStateMachine3C216BE3-bw4Yr4uTiEdO',
      status: 'FAILED',
      stopDate: 2021-12-13T18:03:40.026Z
    },
    {
      executionArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:execution:TestStateMachine3C216BE3-bw4Yr4uTiEdO:87562154-f8d9-bc6e-7ef4-7393c6d8ef92',
      name: '87562154-f8d9-bc6e-7ef4-7393c6d8ef92',
      startDate: 2021-12-13T13:15:37.942Z,
      stateMachineArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:TestStateMachine3C216BE3-bw4Yr4uTiEdO',
      status: 'FAILED',
      stopDate: 2021-12-13T13:15:38.610Z
    },
    {
      executionArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:execution:TestStateMachine3C216BE3-bw4Yr4uTiEdO:a19318d8-a970-31bc-72bd-4e1c00f570b2',
      name: 'a19318d8-a970-31bc-72bd-4e1c00f570b2',
      startDate: 2021-12-13T13:13:57.856Z,
      stateMachineArn: 'arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:TestStateMachine3C216BE3-bw4Yr4uTiEdO',
      status: 'SUCCEEDED',
      stopDate: 2021-12-13T13:13:59.298Z
    }
  ],
  nextToken: undefined
}
2021-12-13T22:57:54.073Z	380d5f5d-f055-466c-a1e7-9dc1307d61e8	INFO	baseUtcDate : Mon Dec 13 2021 22:30:00 GMT+0000 (Coordinated Universal Time)
END RequestId: 380d5f5d-f055-466c-a1e7-9dc1307d61e8
REPORT RequestId: 380d5f5d-f055-466c-a1e7-9dc1307d61e8	Duration: 751.08 ms	Billed Duration: 752 ms	Memory Size: 128 MB	Max Memory Used: 65 MB	Init Duration: 271.40 ms

確かに、1日1回までしか処理をしないようにステートマシンを制御できました。

複雑な処理はまだまだLambda関数の出番

Lambda関数を使って、1日1回までしか処理をしないようにステートマシンを制御してみました。

個人的2021年一番の神アップデートで、AWS Step Functionsが200以上のAWSサービスと連携できるようになりました。

しかし、時差を加味した計算などの複雑な処理は、まだまだLambda関数の出番です。今後も要所要所ではLambda関数を使ってワークフローを組む場面が多いと思います。

この記事が誰かの助けになれば幸いです。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!