kakakakakku blog

Weekly Tech Blog: Keep on Learning!

Powertools for AWS Lambda (Python) の Feature flags で「時間ベースフィーチャーフラグ」を試す

Powertools for AWS Lambda (Python) の Feature flags を使って「時間ベースフィーチャーフラグ (Time based feature flags)」を試してみた.Powertools for AWS Lambda (Python) の Feature flags はデフォルトだと AWS AppConfig をバックエンドにしていて,時間ベース以外に文字列部分一致・数値範囲など柔軟に条件をしてフラグ制御 (true or false) ができる👌

docs.powertools.aws.dev

検証環境

アプリケーション (AWS Lambda) とフィーチャーフラグ (AWS AppConfig) は運用上ライフサイクルが異なるため別のスタックにした.

├── application
│   ├── src
│   │   └── app.py
│   └── template.yaml
└── features
    └── template.yaml

1. features

まず AWS AppConfig をデプロイする.Powertools ドキュメントを参考に AWS CloudFormation テンプレートを書いた.ちなみに Powertools ドキュメントに載っているサンプルだと AWS AppConfig のデプロイ戦略にビルトインの AppConfig.AllAtOnce が指定されていた📝これは即時デプロイをしたら10分間待つ戦略で (BakeTime 10 minutes) 今回はサクサクと検証をしたく,BakeTime なしのカスタムデプロイ戦略を追加した💡

そして HostedConfigurationVersion に時間ベースフィーチャーフラグ (Time based feature flags) を設定した.今回はサンプルとして開始時間と終了時間が決まっているキャンペーン(campaign-acampaign-b)の制御をする❗️

  • campaign-a: 2024-07-06T12:00:00 ~ 2024-07-06T12:29:59
  • campaign-b: 2024-07-06T12:30:00 ~ 2024-07-06T12:59:59

ちなみに時間ベースだと,他には「毎日◯時〜◯時」「毎週◯曜日」という設定もできる📅

AWSTemplateFormatVersion: 2010-09-09

Resources:
  Application:
    Type: AWS::AppConfig::Application
    Properties:
      Name: sandbox
  Environment:
    Type: AWS::AppConfig::Environment
    Properties:
      Name: prd
      ApplicationId: !Ref Application
  ConfigurationProfile:
    Type: AWS::AppConfig::ConfigurationProfile
    Properties:
      Name: profile
      ApplicationId: !Ref Application
      LocationUri: hosted
  HostedConfigurationVersion:
    Type: AWS::AppConfig::HostedConfigurationVersion
    Properties:
      ApplicationId: !Ref Application
      ConfigurationProfileId: !Ref ConfigurationProfile
      ContentType: application/json
      Content: |
        {
          "campaign-a": {
            "default": false,
            "rules": {
              "Enable Campaign A": {
                "when_match": true,
                "conditions": [
                  {
                    "action": "SCHEDULE_BETWEEN_DATETIME_RANGE",
                    "key": "CURRENT_DATETIME",
                    "value": {
                      "START": "2024-07-06T12:00:00",
                      "END": "2024-07-06T12:29:59",
                      "TIMEZONE": "Asia/Tokyo"
                    }
                  }
                ]
              }
            }
          },
          "campaign-b": {
            "default": false,
            "rules": {
              "Enable Campaign B": {
                "when_match": true,
                "conditions": [
                  {
                    "action": "SCHEDULE_BETWEEN_DATETIME_RANGE",
                    "key": "CURRENT_DATETIME",
                    "value": {
                      "START": "2024-07-06T12:30:00",
                      "END": "2024-07-06T12:59:59",
                      "TIMEZONE": "Asia/Tokyo"
                    }
                  }
                ]
              }
            }
          }
        }
  DeploymentStrategy:
    Type: AWS::AppConfig::DeploymentStrategy
    Properties:
      Name: Custom.AllAtOnce
      DeploymentDurationInMinutes: 0
      GrowthFactor: 100
      FinalBakeTimeInMinutes: 0
      ReplicateTo: NONE
  Deployment:
    Type: AWS::AppConfig::Deployment
    Properties:
      ApplicationId: !Ref Application
      EnvironmentId: !Ref Environment
      ConfigurationProfileId: !Ref ConfigurationProfile
      ConfigurationVersion: !Ref HostedConfigurationVersion
      DeploymentStrategyId: !Ref DeploymentStrategy

ちなみに AWS AppConfig 自体のフィーチャーフラグとは関係なく,AWS AppConfig をバックエンドにした Powertools for AWS Lambda (Python) の仕組みになる.コンソールだと 自由形式の設定プロファイル となる.少しややこしい😇

2. application

次は AWS Lambda をデプロイする.AWS SAM テンプレートで Powertools Layer を設定しておく.あと IAM Role に AWS AppConfig を操作する権限を付けておく.Powertools ドキュメントには appconfig:GetLatestConfigurationappconfig:StartConfigurationSession で十分と書いてあった✅

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Resources:
  Function:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: powertools-feature-flags-time-based
      CodeUri: src/
      Handler: app.lambda_handler
      Runtime: python3.12
      Architectures:
        - x86_64
      Layers:
        - arn:aws:lambda:ap-northeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:75
      Role: arn:aws:iam::000000000000:role/xxxxx

コードでは Powertools for AWS Lambda (Python) の Feature flags で evaluate() を実行して,結果を表示するシンプルな実装にした👌

from aws_lambda_powertools.utilities.feature_flags import AppConfigStore, FeatureFlags

app_config = AppConfigStore(application='sandbox', environment='prd', name='profile')
feature_flags = FeatureFlags(store=app_config)


def lambda_handler(event, context):
    campaign_a = feature_flags.evaluate(name='campaign-a', default=False)
    campaign_b = feature_flags.evaluate(name='campaign-b', default=False)

    features = {
        'campaign-a': campaign_a,
        'campaign-b': campaign_b,
    }

    print(features)

動作確認

計4回実行してみた🕐

期待通りの結果になったー \( 'ω')/

👇 11:50
{'campaign-a': False, 'campaign-b': False}
👇 12:10
{'campaign-a': True, 'campaign-b': False}
👇 12:40
{'campaign-a': False, 'campaign-b': True}
👇 13:10
{'campaign-a': False, 'campaign-b': False}

フィーチャーフラグのキャッシュ機能

Powertools for AWS Lambda (Python) の Feature flags は毎回 AWS AppConfig から取得するのではなく,デフォルトでは「5秒間」キャッシュされる📂AWS Lambda 関数のライフサイクルとして,初期化後にもフィーチャーフラグを更新できる👌

docs.aws.amazon.com

AWS AppConfig の呼び出し回数を減らすこととフィーチャーフラグの鮮度を高めることのトレードオフにはなるけど,もしキャッシュ時間を変更する場合は AppConfigStore で初期化するときに max_age を設定して変更できる.

app_config = AppConfigStore(application='sandbox', environment='prd', name='profile', max_age=30)

単体テスト

Powertools ドキュメントには pytest-mock を使って AWS AppConfig に依存せずテストをするサンプル実装が載っていた.LocalStack も AWS AppConfig をサポートしているけど,LocalStack Pro で使えるため,契約が必要になってしまう💨

docs.localstack.cloud

関連記事

kakakakakku.hatenablog.com