Amazon Web Services ブログ

AWS AppSyncキャッシングとAmazon DynamoDBトランザクションのサポートによりGraphQL APIのパフォーマンスと一貫性が更に向上します

AWS AppSyncはGraphQLのマネージドサービスで、単一または複数のデータソースのデータに安全にアクセスしたり、操作したり、結合するための柔軟なAPIを作成でき、アプリケーション開発をシンプルにすることができます。多くの場合、異なったデータソースは異なったユースケースに合わせて最適化され、データが配信されるスピードも異なっていることでしょう。その基になるGraphQLスキーマで定義されているデータフィールドもかなり多様です。

たとえば、eコマースアプリケーションでは、在庫量を表すデータフィールドは頻繁に更新されますが、顧客プロフィールの更新は時々です。トランザクションIDに関しては不変で、更新されることはありません。すべてのデータ取得操作でデータソースに直接アクセスすると、システム全体の遅延が増大し、ユーザー体験とアプリケーションのパフォーマンスに影響があります。あまり変更されないデータを返すキャッシュ層は、以前リクエストされたデータを後続のリクエストのためにキャッシュから直接配信できるため、パフォーマンスの向上、レイテンシーの短縮およびデータソースの使用率の最適化に役立ちます。

あるいはその他のシチュエーション、例えば金融取引処理、注文の実行と管理、分散コンポーネントとサービス間をまたがる協調動作など、デベロッパーはAmazon DynamoDBをAWS AppSyncデータソースとして使用する際に、単一の論理的なビジネスオペレーションの一部として複数のアイテムを協調してINSERT、DELETE、またはUPDATEするといった複数の不可分な一連の処理のビジネスロジックを実装しなければならない場合もあるでしょう。

2019/11/21、わたしたちはAWS AppSync GraphQL APIに、デベロッパーがこれらのさまざまな要件に対処するのに役立つ2つの重要な機能であるサーバーサイドキャッシュとAmazon DynamoDBトランザクションのビルトインサポートをリリースします。

キャッシング

AppSyncはサポートされるすべてのデータソースにビルトインサーバーサイドキャッシュ機能を提供するので、低レイテンシーで高スループットが必要なアプリケーションのパフォーマンスを向上し、デベロッパーは高速なインメモリのマネージドキャッシュからデータを取得して低レイテンシーにデータを届けることができるようになります。

新しいキャッシュ機能を利用することで、あなたのGraphQL APIに次のようなさまざまなキャッシュ動作を定義できます。

  • なし(None):サーバーサイドキャッシュはありません。これはAppSync APIのデフォルトの動作です。
  • フルリクエストキャッシング(Full request caching):データがキャッシュにない場合はデータソースから取得され、TTLの有効期限までキャッシュに投入されます。その後、APIへのすべてのリクエストはキャッシュから返されます。つまり、データソースに直接アクセスすることはありません。
  • リゾルバごとのキャッシュ(Per-resolver caching):リゾルバで定義された特定の操作またはフィールドからデータを取得するAPI呼び出しのみがキャッシュから応答を返します。

適切なキャッシュ動作を定義した後、さまざまなメモリおよびネットワークパフォーマンスの要件に従ってキャッシュインスタンスタイプを選択し、キャッシュされたエントリがメモリに保存される時間(存続時間またはTTL)を定義できます。キャッシュはAppSyncによって完全に管理されます。他のサービスと同様に、インフラの保守や運用といったサービスそれ自体の差別化要因とならない重労働を気にする必要がありません。また、転送中および保存中のキャッシュを暗号化することもできます(スワップ操作中にメモリからディスクに保存されたデータの場合)。

リゾルバごとのキャッシング(per-resolver caching)を使用すると、どのようにGraphQL呼び出しをキャッシュして要件に合うようにするかをカスタマイズできます。もしかしたら、お使いのGraphQLスキーマには、ある特定のタイプやタイプのフィールド、あるいは特定のクエリ操作はキャッシュが必要だが、それら以外は(キャッシュなしで)データソースに直接到達しなければならないといった要件があるかもしれません。

たとえば、AppSyncコンソールの「API作成(Create API)」ウィザードで「イベントアプリ(Event App)」サンプルプロジェクトを使用して作成されたGraphQLスキーマから次のタイプとクエリを見てみましょう。

type Event {
    id: ID!
    name: String
    where: String
    when: String
    description: String
    comments(limit: Int, nextToken: String): CommentConnection
}

type Comment {
    eventId: ID!
    commentId: String!
    content: String!
    createdAt: String!
}

type EventConnection {
    items: [Event]
    nextToken: String
}

type CommentConnection {
    items: [Comment]
    nextToken: String
}

type Query {
    getEvent(id: ID!): Event
    listEvents(limit: Int, nextToken: String): EventConnection
}

EventsCommentsを保存する2つのDynamoDBテーブルがあります。アプリケーションがイベントの名前のみをキャッシュする必要があり、他の何もキャッシュする必要がない場合は、nameフィールドにリゾルバをアタッチするだけです。

「新しいリゾルバーの作成 (Create new Resolver)」画面で、nameフィールドが保存されているデータソース(この場合はDynamoDBのAppSyncEventTableテーブル)を選択します。

キャッシュ設定(Cache Settings)セクションまで下にスクロールし、キャッシュを有効にする(Enable Caching)を選択して、キャッシュリゾルバのTTLを定義し、リゾルバの保存(Save Resolver)をクリックして設定を保存します。

これにより、Eventタイプのnameフィールド用に作成したリゾルバのキャッシュが有効になります。単一のフィールドの代わりにクエリ操作をキャッシュしたい場合、たとえばスキーマで定義されたgetEventクエリに関しても手順はまったく同じです。クエリリゾルバの設定でキャッシュを有効化し、カスタムTTLを定義するだけです。

キャッシュキーを作成して、データのキャッシュされた値の一意性を確保することもできます。キャッシュキーは、コンテキストオブジェクト中のユーザー識別子(context.indentity)またはリゾルバに渡される引数(context.arguments)を基に定義することができます。

たとえば、今回例示したEvents APIではOpenID ConnectまたはCognito User Poolsでユーザに認可を与えており、2人の異なるユーザが同じイベントに参加する場合、context.identity.sub(ユーザーの一意な識別子)および context.argument.id(イベントの一意な識別子)をユーザが同一のイベントIDのキャッシュデータを取得するためのキャッシュキーとして使うことができます。このようにキャッシュキーをセットアップするには、次のような内容でcacheConfig.jsonという名前のファイルを作成します。

{
    "ttl": 1000,
    "cachingKeys": [
         "$context.identity.sub",
         "$context.arguments.id"
    ]
}

クエリのキャッシュを有効にした後、cacheConfig.jsonファイルと、クエリに対応する既存のリクエストおよびレスポンスマッピングテンプレートを含むファイルを参照する次のAWS CLIコマンドを使用して、任意のキャッシュキーでgetEventリゾルバを更新できます。

$aws appsync update-resolver --api-id 123456789example --type-name Query --field-name getEvent --caching-config file:///cacheConfig.json --request-mapping-template file:///Query.getEvent.req.vtl --response-mapping-template file:///Query.getEvent.res.vtl --data-source-name AppSyncEventTable 
{
    "resolver": {
        "typeName": "Query",
        "fieldName": "getEvent",
        "dataSourceName": "AppSyncEventTable",
        "resolverArn": "arn:aws:appsync:us-west-2:xxxxxxxxxxxx:apis/123456789example/types/Query/resolvers/getEvent",
        "requestMappingTemplate": ""{\n \"version\": \"2017-02-28\",\n \"operation\": \"GetItem\",\n \"key\": {\n \"id\": { \"S\": \"$context.arguments.id\" }\n }\n}",
        "responseMappingTemplate": "$util.toJson($context.result)",
        "kind": "UNIT",
        "cachingConfig": {
            "ttl": 1000,
            "cachingKeys": [
                "$context.identity.sub",
                "$context.arguments.id"
            ]
        }
    }
}

TTL失効前にキャッシュデータが古くなった場合は、キャッシュのフラッシュ(Flush cache)ボタンをクリックしてキャッシュを簡単にフラッシュして無効にできます。

最後に、キャッシュ戦略がどれほど効果的に機能しており、お使いのGraphQL APIに十分な価値と改善をもたらしているかを簡単に理解できることも非常に重要です。適切な可視性を提供するために、キャッシュのヒットやミス率、追い出し(evictions)、使用バイト数などのAppSync APIのすべてのキャッシュアクティビティを、コンソールのAPIのモニタリングセクションの単一のダッシュボードから便利にいつでも監視できます。

AppSyncでキャッシュを使用するには追加料金がかかります。キャッシュがGraphQL APIから削除されるまで、長期契約義務などは一切なく時間ごとに課金されます。詳しい情報は料金ページをご覧ください。

DynamoDBトランザクション

キャッシュに加えて、AppSyncはAmazon DynamoDBデータソースとリゾルバのトランザクションもサポートするようになりました。DynamoDBトランザクションによって、テーブル内およびテーブルをまたがって複数のアイテムを不可分な一連の処理として協調的に変更処理するデベロッパー体験がシンプルになります。トランザクションは、DynamoDBに原子性、一貫性、分離、耐久性(ACID)を提供し、アプリケーションのデータの正確性を維持するのに役立ちます。

AppSyncは、リゾルバでトランザクションをハンドリングするのに使われる次の2つのDynamoDB操作をサポートするようになりました。

  • TransactWriteItems:1つ以上のPutItem、UpdateItem、およびDeleteItem操作からなる書き込みセットを含んだバッチ処理。
  • TransactGetItems:1つ以上のGetItem操作からなる読み取りセットを含んだバッチ処理。

ミューテーションリゾルバで次のようなVTLテンプレートを使用して、異なるテーブル間でアイテムを作成または更新するトランザクションを発行するGraphQLミューテーションを使用できるようになりました。

{
        "version": "2018-05-29",
        "operation": "TransactWriteItems",
        "transactItems": [
           {
               "table": "posts",
               "operation": "PutItem",
               "key": {
                   "post_id": {
                       "S": "p1"
                   }
               },
               "attributeValues": {
                   "post_title": {
                       "S": "New title"
                   },
                   "post_description": {
                       "S": "New description"
                   }
               },
               "condition": {
                   "expression": "post_title = :post_title",
                   "expressionValues": {
                       ":post_title": {
                           "S": "Expected old title"
                       }
                   }
               }
           },
           {
               "table":"authors",
               "operation": "UpdateItem",
               "key": {
                   "author_id": {
                       "S": "a1"
                   },
               },
               "update": {
                   "expression": "SET author_name = :author_name",
                   "expressionValues": {
                       ":author_name": {
                           "S": "New name"
                       }
                   }
               },
           }
        ]
    }

各トランザクションには最大25個の一意なアイテムを含めることができます。トランザクションが成功した場合は、取得されたアイテムの順序はリクエストアイテムの順序と同じになります。また、トランザクションはすべて成功するかさもなくば失敗という風に実行されることに注意することも重要です。リクエストされた要求されたアイテムのいずれかがエラーになる場合、トランザクション全体は実行されず、エラーの詳細が返されます。

AppSyncを使用したDynamoDBトランザクションのより詳しい情報と便利なチュートリアルはデベロッパーガイドからご覧いただけます。

結論

キャッシングとトランザクションサポートのような強力な機能によって、AppSyncはGraphQL APIにより柔軟性を与えます。低レイテンシーが求められる際にはマネージドキャッシングレイヤー、あるいは異なるDynamoDBテーブル間をまたがった協調的な変更が必要な際にはトランザクションサポートを備えたNoSQLの原子性、一貫性、分離、耐久性(ACID)が利用できます。

AWS AppSyncのキャッシングやDynamoDB統合で他に見たいものはありますか?わたしたちのGitHubリポジトリでお気軽にフィーチャーリクエストを作成してください。わたしたちのチームは常にリポジトリを監視しており、AppSyncでお作りいただいたアプリケーションがうまくいくようにデベロッパーのフィードバックをお聞きするのをいつも楽しみにしています。

(この記事は SA 白山が翻訳しました。原文はこちら