チュートリアル:差分同期 - AWS AppSync

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

チュートリアル:差分同期

注記

現在、主に APPSYNC_JS ランタイムとそのドキュメントをサポートしています。こちらにある APPSYNC_JS ランタイムとそのガイドの使用をご検討ください。

AWS AppSync のクライアントアプリケーションは、モバイル/ウェブアプリケーションへの GraphQL レスポンスをローカルディスクにキャッシュすることで、データを保存します。バージョン管理されたデータソースと Sync オペレーションにより、お客様は単一のリゾルバーを使用して同期プロセスを実行することができます。これにより、クライアントは大量のレコードを含む可能性のある基本クエリでのローカルキャッシュをハイドレートし、最後のクエリ以降に変更されたデータのみを受信できます (差分更新)。クライアントが初期リクエストでキャッシュのベースハイドレートを実行し、別のリクエストでの増分更新を実行できるようにすることで、その計算をクライアントアプリケーションからバックエンドに移動できます。これは、頻繁にオンライン状態とオフラインの状態を切り替えるクライアントアプリケーションにとって効率が大きく向上します。

Delta Sync を実装するため、Sync クエリはバージョン管理されたデータソースで Sync オペレーションを使用します。AWS AppSync ミューテーションによってバージョン管理されたデータソースの項目が変更されると、その変更のレコードも差分テーブルに保存されます。他のバージョン管理されたデータソースに異なる差分テーブル (タイプごとに 1 つ、ドメイン領域ごとに 1 つ) を使用するか、API に 1 つの差分テーブルを使用するかを選択できます。AWSAppSync はプライマリキーの衝突を回避するため、単一の複数の API に単一の差分テーブルを使用しないことを推奨しています。

さらに、Delta Sync クライアントはサブスクリプションを引数として受け取ることもでき、オフラインからオンラインへの移行間でサブスクリプションの再接続と書き込みを調整します。そのために、Delta Sync はサブスクリプション (さまざまなネットワークエラーシナリオでの、エクスポネンシャルバックオフや、ジッターを伴う再試行など) を自動的に再開し、イベントをキューに保存します。その後、該当する差分クエリまたは基本クエリを実行してから、キュー内のイベントをマージし、最後に通常どおりにサブスクリプションを処理します。

Amplify DataStore などのクライアント設定オプションのドキュメントは、Amplify Amplify Frameworkのウェブサイトで入手できます。このドキュメントは、最適なデータアクセスのために Delta Sync クライアントと連携するように、バージョン管理された DynamoDB データソースと Sync オペレーションをセットアップする方法について概説しています。

ワンクリックでのセットアップ

設定したすべてのリゾルバーと必要な AWS リソースを使用して AWS AppSync で GraphQL エンドポイントが自動的に設定されるようにするには、以下の AWS CloudFormation テンプレートを使用します。

このスタックはお客様のアカウントに以下のリソースを作成します。

  • 2 つの DynamoDB テーブル (基本と差分)

  • 1 AWS API キーを持つ AppSync API

  • DynamoDB テーブルのポリシーを割り当てた 1 つの IAM ロール

2 つのテーブルは、クライアントがオフラインのときに取得できなかったイベントのジャーナルとして機能する 2 番目のテーブルに、同期クエリを分割するために使用されます。差分テーブルに対するクエリが以降も効率的になるように、必要に応じてイベントの自動整理に Amazon DynamoDB TTL を使用します。TTL 時間は、データソースのニーズに合わせて設定できます (1 時間、1 日など)。

スキーマ

差分同期 を実証するために、サンプルアプリケーションでは DynamoDB 内のテーブル基本そしてデルタによってバックアップされる POST スキーマを作成します。AWSAppSync はミューテーションを両方のテーブルに自動的に書き込みます。同期クエリは必要に応じて、ベーステーブルまたは差分テーブルからレコードをプルします。また、1 つのサブスクリプションは、クライアントが再接続ロジックでこのテーブルをどのように活用できるかを定義します。

input CreatePostInput { author: String! title: String! content: String! url: String ups: Int downs: Int _version: Int } interface Connection { nextToken: String startedAt: AWSTimestamp! } type Mutation { createPost(input: CreatePostInput!): Post updatePost(input: UpdatePostInput!): Post deletePost(input: DeletePostInput!): Post } type Post { id: ID! author: String! title: String! content: String! url: AWSURL ups: Int downs: Int _version: Int _deleted: Boolean _lastChangedAt: AWSTimestamp! } type PostConnection implements Connection { items: [Post!]! nextToken: String startedAt: AWSTimestamp! } type Query { getPost(id: ID!): Post syncPosts(limit: Int, nextToken: String, lastSync: AWSTimestamp): PostConnection! } type Subscription { onCreatePost: Post @aws_subscribe(mutations: ["createPost"]) onUpdatePost: Post @aws_subscribe(mutations: ["updatePost"]) onDeletePost: Post @aws_subscribe(mutations: ["deletePost"]) } input DeletePostInput { id: ID! _version: Int! } input UpdatePostInput { id: ID! author: String title: String content: String url: String ups: Int downs: Int _version: Int! } schema { query: Query mutation: Mutation subscription: Subscription }

GraphQL スキーマは標準的なものですが、先に進む前に注意することがいくつかあります。最初に、すべてのミューテーションは自動的にベーステーブルに書き込み、次に差分テーブルに書き込みます。基本テーブルは、実際の一元的なソースであり、一方、差分テーブルはジャーナルです。lastSync: AWSTimestamp を渡さない場合、 syncPosts クエリは基本テーブルに対して実行され、キャッシュがハイドレートされるだけでなく、グローバルキャッチアッププロセスとしても定期的に実行されます。これは、差分テーブルで設定された TTL 時間より長くクライアントがオフラインになっているエッジケースに当てはまります。lastSync: AWSTimestamp を渡す場合、syncPosts クエリは差分テーブルに対して実行されます。クライアントはこのクエリを使用して、最後のオフライン以降に変更されたイベントを取得します。Amplify クライアントは自動的に lastSync: AWSTimestamp 値を渡し、適切にディスクに保持します。

POST 上の _deleted フィールドは、削除に使用します。クライアントがオフラインになり、レコードが基本テーブルから削除されると、この属性は、同期中のクライアントにローカルキャッシュから項目をエビクションするように通知します。クライアントが長期間オフラインであり、クライアントが Delta Sync クエリでこの値を取得する前に項目が削除された場合は、基本クエリのグローバルキャッチアップイベント (クライアントで設定可能) が実行され、キャッシュからその項目が削除されます。このフィールドがオプションとしてマークされているのは、同期クエリの実行時に、削除された項目がある場合にのみ値を返すためです。

ミューテーション

すべてのミューテーションについて、AWS AppSync は基本テーブルで標準の作成/更新/削除オペレーションを実行するとともに、変更内容を差分テーブルに自動的に記録します。データソースの DeltaSyncTableTTL 値を変更することで、レコードを保持する時間を短縮または延長できます。データの速度が速い組織では、この時間を短縮するのが妥当な場合があります。あるいは、クライアントが長期間オフラインになっている場合は、この時間を延長するのが賢明でしょう。

同期クエリ

基本クエリは、lastSync 値が指定されていない DynamoDB の同期オペレーションです。多くの組織にとって、基本クエリで十分です。基本クエリは起動時に実行され、以降は定期的に実行されるためです。

差分クエリは、lastSync 値が指定された DynamoDB の同期オペレーションです。差分クエリは、クライアントがオフライン状態からオンライン状態に戻るたびに実行されます (基本クエリが定期的にトリガーされていない場合)。クライアントは、データを同期するクエリを最後に正常に実行した時刻を自動的に追跡します。

差分クエリが実行されると、クエリのリゾルバーは ds_pkds_sk を使用して、クライアントが前回同期を実行してから変更されたレコードに対してのみクエリを実行します。クライアントは該当する GraphQL レスポンスを保存します。

同期クエリの実行の詳細については、同期オペレーションのドキュメントを参照してください。

まず、項目を作成するために createPost ミューテーションを呼び出すことから始めましょう。

mutation create { createPost(input: {author: "Nadia", title: "My First Post", content: "Hello World"}) { id author title content _version _lastChangedAt _deleted } }

このミューテーションの戻り値は次のようになります。

{ "data": { "createPost": { "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b", "author": "Nadia", "title": "My First Post", "content": "Hello World", "_version": 1, "_lastChangedAt": 1574469356331, "_deleted": null } } }

ベーステーブルの内容を調べると、次のようなレコードがあります。

{ "_lastChangedAt": { "N": "1574469356331" }, "_version": { "N": "1" }, "author": { "S": "Nadia" }, "content": { "S": "Hello World" }, "id": { "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b" }, "title": { "S": "My First Post" } }

差分テーブルの内容を調べると、次のようなレコードがあります。

{ "_lastChangedAt": { "N": "1574469356331" }, "_ttl": { "N": "1574472956" }, "_version": { "N": "1" }, "author": { "S": "Nadia" }, "content": { "S": "Hello World" }, "ds_pk": { "S": "AppSync-delta-sync-post:2019-11-23" }, "ds_sk": { "S": "00:35:56.331:81d36bbb-1579-4efe-92b8-2e3f679f628b:1" }, "id": { "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b" }, "title": { "S": "My First Post" } }

これで、クライアントが syncPosts クエリなどを実行してローカルデータストアをハイドレートするために実行する基本クエリをシミュレーションすることができます。

query baseQuery { syncPosts(limit: 100, lastSync: null, nextToken: null) { items { id author title content _version _lastChangedAt } startedAt nextToken } }

この基本クエリの戻り値は次のようになります。

{ "data": { "syncPosts": { "items": [ { "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b", "author": "Nadia", "title": "My First Post", "content": "Hello World", "_version": 1, "_lastChangedAt": 1574469356331 } ], "startedAt": 1574469602238, "nextToken": null } } }

差分クエリをシミュレーションするために後で startedAt 値を保存しますが、まずはテーブルを変更する必要があります。updatePost ミューテーションを使って既存の投稿を変更しましょう。

mutation updatePost { updatePost(input: {id: "81d36bbb-1579-4efe-92b8-2e3f679f628b", _version: 1, title: "Actually this is my Second Post"}) { id author title content _version _lastChangedAt _deleted } }

このミューテーションの戻り値は次のようになります。

{ "data": { "updatePost": { "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b", "author": "Nadia", "title": "Actually this is my Second Post", "content": "Hello World", "_version": 2, "_lastChangedAt": 1574469851417, "_deleted": null } } }

ここでベーステーブルの内容を調べると、更新された項目が表示されます。

{ "_lastChangedAt": { "N": "1574469851417" }, "_version": { "N": "2" }, "author": { "S": "Nadia" }, "content": { "S": "Hello World" }, "id": { "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b" }, "title": { "S": "Actually this is my Second Post" } }

ここで差分テーブルの内容を調べると、次の 2 つのレコードがあります。

  1. 項目が作成されたときのレコード

  2. 項目が更新されたときのレコード

新しい項目は次のようになります。

{ "_lastChangedAt": { "N": "1574469851417" }, "_ttl": { "N": "1574473451" }, "_version": { "N": "2" }, "author": { "S": "Nadia" }, "content": { "S": "Hello World" }, "ds_pk": { "S": "AppSync-delta-sync-post:2019-11-23" }, "ds_sk": { "S": "00:44:11.417:81d36bbb-1579-4efe-92b8-2e3f679f628b:2" }, "id": { "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b" }, "title": { "S": "Actually this is my Second Post" } }

差分クエリをシミュレートして、クライアントがオフラインのときに発生した変更を取得できるようになりました。リクエストを行うために、基本クエリから返された値 startedAt を使用します。

query delta { syncPosts(limit: 100, lastSync: 1574469602238, nextToken: null) { items { id author title content _version } startedAt nextToken } }

この差分クエリの戻り値は次のようになります。

{ "data": { "syncPosts": { "items": [ { "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b", "author": "Nadia", "title": "Actually this is my Second Post", "content": "Hello World", "_version": 2 } ], "startedAt": 1574470400808, "nextToken": null } } }