Powertools for AWS Lambda (TypeScript) を使って DynamoDB テーブルからのデータ取得をキャッシュしてみた

2023.07.10

こんにちは、CX事業本部 Delivery部の若槻です。

以前に Powertools for AWS Lambda (TypeScript) を使って AWS Systems Manager Parameter Store からのデータ取得のキャッシュを試してみました。

この Powertools ですが、DynamoDB にも対応しており、パラメーター保管領域として使用しているテーブルからのデータ取得のキャッシュを容易に実装することもできます。

試してみた

Powertools for AWS Lambda (TypeScript) を使って DynamoDB からのデータ取得のキャッシュを試してみます。

インストール

必要なモジュールをインストールします。

npm install @aws-lambda-powertools/parameters @aws-sdk/client-dynamodb @aws-sdk/util-dynamodb

検証環境の作成

検証環境として、DynamoDB テーブル 3 つおよび Lambda 関数および AWS CDK で作成します。

lib/cdk-sample-stack.ts

import {
  aws_dynamodb,
  aws_lambda_nodejs,
  aws_lambda,
  RemovalPolicy,
  Stack,
  StackProps,
} from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class CdkSampleStack extends Stack {
  public readonly myFileObjectKey: string;

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

    // my-table:PK=id
    const myTable = new aws_dynamodb.Table(this, 'myTable', {
      tableName: 'my-table',
      partitionKey: { name: 'id', type: aws_dynamodb.AttributeType.STRING },
      billingMode: aws_dynamodb.BillingMode.PAY_PER_REQUEST,
      removalPolicy: RemovalPolicy.DESTROY,
    });
    // my-table2:PK=id, SK=sk
    const myTable2 = new aws_dynamodb.Table(this, 'myTable2', {
      tableName: 'my-table2',
      partitionKey: { name: 'id', type: aws_dynamodb.AttributeType.STRING },
      sortKey: { name: 'sk', type: aws_dynamodb.AttributeType.STRING },
      billingMode: aws_dynamodb.BillingMode.PAY_PER_REQUEST,
      removalPolicy: RemovalPolicy.DESTROY,
    });
    // my-table3:PK=customId, SK=customSk
    const myTable3 = new aws_dynamodb.Table(this, 'my-table3', {
      tableName: 'my-table3',
      partitionKey: {
        name: 'customId',
        type: aws_dynamodb.AttributeType.STRING,
      },
      sortKey: { name: 'customSk', type: aws_dynamodb.AttributeType.STRING },
      billingMode: aws_dynamodb.BillingMode.PAY_PER_REQUEST,
      removalPolicy: RemovalPolicy.DESTROY,
    });

    // Lambda 関数
    const myFunction = new aws_lambda_nodejs.NodejsFunction(
      this,
      'myFunction',
      {
        functionName: 'myFunction',
        architecture: aws_lambda.Architecture.ARM_64,
        runtime: aws_lambda.Runtime.NODEJS_18_X,
      }
    );

    // 読み取り権限付与
    myTable.grantReadData(myFunction);
    myTable2.grantReadData(myFunction);
    myTable3.grantReadData(myFunction);
  }
}

パラメーター取得(キャッシュなし)

my-table:PK=id からのパラメーター取得です。既定では PK は id というキー名である必要があります。dynamoDBProvider を構成し、get メソッドを使用することによりパラメーターを取得します。

lib/cdk-sample-stack.myFunction.ts

import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb';

const dynamoDBProvider = new DynamoDBProvider({ tableName: 'my-table' });

export const handler = async (): Promise<any> => {
  const value = await dynamoDBProvider.get('my-parameter');
  return value;
};

my-table に id: my-parameter のアイテムを作成します。ここでパラメーターの値としたい属性名を value とする必要があります。

aws dynamodb put-item \
  --table-name my-table \
  --item '{"id": {"S": "my-parameter"}, "value": {"S": "my-value"}}'

Lambda 関数を実行すると値が取得されました。

$ aws lambda invoke --function-name myFunction response.json
$ cat response.json
"my-value"

ここで、get は既定ではキャッシュ期間は 0 なので、変更後の値はすぐに取得されます。

id: my-parameter のアイテムの value 属性の値を更新します。

$ aws dynamodb put-item \
  --table-name my-table \
  --item '{"id": {"S": "my-parameter"}, "value": {"S": "new-my-value"}}'

Lambda 関数を実行すると更新後の値が取得されました。

$ aws lambda invoke --function-name myFunction response.json
$ cat response.json
"new-my-value"

パラメーター取得(キャッシュあり)

続いて先程と同じテーブルでキャッシュを有効にしてパラメーター取得を行います。get メソッドで maxAge を指定することによりキャッシュ期間を設定します。

lib/cdk-sample-stack.myFunction.ts

import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb';

const dynamoDBProvider = new DynamoDBProvider({ tableName: 'my-table' });

export const handler = async (): Promise<any> => {
  const value = await dynamoDBProvider.get('my-parameter', { maxAge: 60 });
  return value;
};

my-table に id: my-parameter のアイテムを作成します。

aws dynamodb put-item \
  --table-name my-table \
  --item '{"id": {"S": "my-parameter"}, "value": {"S": "my-value"}}'

Lambda 関数を実行すると値が取得されました。

$ aws lambda invoke --function-name myFunction response.json;cat response.json
"my-value"

ここで値を更新します。

id: my-parameter のアイテムの value 属性の値を更新します。

$ aws dynamodb put-item \
  --table-name my-table \
  --item '{"id": {"S": "my-parameter"}, "value": {"S": "new-my-value"}}'

前回 Lambda 関数実行から 60秒(キャッシュ期間)以内に再度実行すると、更新前の値が取得されました。キャッシュが働いていますね。

$ aws lambda invoke --function-name myFunction response.json;cat response.json
"my-value"

60秒(キャッシュ期間)経過後に再度実行すると、更新後の値が取得されました。キャッシュがクリアされています。

$ aws lambda invoke --function-name myFunction response.json;cat response.json
"new-my-value"

複数のパラメーター取得

my-table2:PK=id, SK=sk からのパラメーター取得です。既定では PK は id 、SK は sk というキー名である必要があります。dynamoDBProvider を構成し、getMultiple メソッドを使用することにより複数のパラメーターをクエリにより取得します。

lib/cdk-sample-stack.myFunction.ts

import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb';

const dynamoDBProvider = new DynamoDBProvider({ tableName: 'my-table2' });

export const handler = async (): Promise<any> => {
  const values = await dynamoDBProvider.getMultiple('my-hash-key');
  return values;
};

ハッシュキー id: my-hash-key に対して複数のアイテムを作成します。get と同様にパラメーターの値としたい属性名を value とする必要があります。

aws dynamodb put-item \
  --table-name my-table2 \
  --item '{"id": {"S": "my-hash-key"}, "sk": {"S": "param-a"}, "value": {"S": "my-value-a"}}'
aws dynamodb put-item \
  --table-name my-table2 \
  --item '{"id": {"S": "my-hash-key"}, "sk": {"S": "param-b"}, "value": {"S": "my-value-b"}}'
aws dynamodb put-item \
  --table-name my-table2 \
  --item '{"id": {"S": "my-hash-key"}, "sk": {"S": "param-c"}, "value": {"S": "my-value-c"}}'

Lambda 関数を実行すると、指定したハッシュキーに対応する複数の値が取得されました。

$ aws lambda invoke --function-name myFunction response.json;cat response.json
{"param-a":"my-value-a","param-b":"my-value-b","param-c":"my-value-c"}

カスタマイズしたパラメーターの取得

続いて、my-table3:PK=customId, SK=customSk からのパラメーター取得です。DynamoDBProvider でオプションを指定することにより、PK、SK、値の属性名をカスタマイズすることができます。

lib/cdk-sample-stack.myFunction.ts

import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb';

const dynamoDBProvider = new DynamoDBProvider({
  tableName: 'my-table3',
  keyAttr: 'customId',
  sortAttr: 'customSk',
  valueAttr: 'count',
});

export const handler = async (): Promise<any> => {
  const values = await dynamoDBProvider.getMultiple('my-hash-key');
  return values;
};

ハッシュキー customId: my-hash-key に対して、DynamoDBProvider で指定した属性名でアイテムを作成します。

aws dynamodb put-item \
  --table-name my-table3 \
  --item '{"customId": {"S": "my-hash-key"}, "customSk": {"S": "param-a"}, "count": {"N": "1"}}'
aws dynamodb put-item \
  --table-name my-table3 \
  --item '{"customId": {"S": "my-hash-key"}, "customSk": {"S": "param-b"}, "count": {"N": "2"}}'
aws dynamodb put-item \
  --table-name my-table3 \
  --item '{"customId": {"S": "my-hash-key"}, "customSk": {"S": "param-c"}, "count": {"N": "3"}}'

Lambda 関数を実行すると、指定したソートキーの値をキー、属性名を値として結果が取得されました。

$ aws lambda invoke --function-name myFunction response.json;cat response.json
{"param-a":1,"param-b":2,"param-c":3}

おわりに

Powertools for AWS Lambda (TypeScript) を使って DynamoDB テーブルからのデータ取得をキャッシュしてみました。

ユースケースは限られそうですが、DynamoDB テーブルを単一のキー/バリューのみのデータ格納領域として使用するのであれば、選択肢としてありではないでしょうか。

以上