

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

# のバージョニングされたデータソースでの Delta Sync オペレーションの使用 AWS AppSync
<a name="tutorial-delta-sync"></a>

**注記**  
現在、主に APPSYNC\$1JS ランタイムとそのドキュメントをサポートしています。[こちら](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html) で APPSYNC\$1JS ランタイムとそのガイドの使用をご検討ください。

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

Delta Sync を実装するため、`Sync` クエリはバージョン管理されたデータソースで `Sync` オペレーションを使用します。 AWS AppSync ミューテーションがバージョニングされたデータソースの項目を変更すると、その変更の記録も *Delta* テーブルに保存されます。バージョン管理された他のデータソースに異なる *Delta* テーブル (タイプごとに 1 つ、ドメイン領域ごとに 1 つなど) を使用するか、API に 1 つの *Delta* テーブルを使用するかを選択できます。 AWS AppSync では、プライマリキーの衝突を避けるためAPIs に 1 つの *Delta* テーブルを使用することをお勧めします。

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

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

## ワンクリックでのセットアップ
<a name="one-click-setup"></a>

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

[https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/deltasync/deltasync-v2-full.yaml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/deltasync/deltasync-v2-full.yaml) 

このスタックはお客様のアカウントに以下のリソースを作成します。
+ 2 つの DynamoDB テーブル (基本と差分)
+ 1API キーを使用した AWS AppSync API
+ DynamoDB テーブルのポリシーを割り当てた 1 つの IAM ロール

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

## Schema
<a name="schema"></a>

Delta Sync を実証するために、サンプルアプリケーションは DynamoDB の *Base* テーブルと *Delta* テーブルにバックアップされた *Posts* スキーマを作成します。 AWS AppSync はミューテーションを両方のテーブルに自動的に書き込みます。同期クエリは必要に応じて、*ベース*テーブルまたは*差分*テーブルからレコードをプルします。また、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* 上の *\$1deleted* フィールドは、**削除**に使用します。クライアントがオフラインになり、レコードが*基本*テーブルから削除されると、この属性は、同期中のクライアントにローカルキャッシュから項目をエビクションするように通知します。クライアントが長期間オフラインであり、クライアントが Delta Sync クエリでこの値を取得する前に項目が削除された場合は、基本クエリのグローバルキャッチアップイベント (クライアントで設定可能) が実行され、キャッシュからその項目が削除されます。このフィールドがオプションとしてマークされているのは、同期クエリの実行時に、削除された項目がある場合にのみ値を返すためです。

## ミューテーション
<a name="mutations"></a>

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

## 同期クエリ
<a name="sync-queries"></a>

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

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

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

同期クエリの実行の詳細については、[同期オペレーションのドキュメント](aws-appsync-conflict-detection-and-sync-sync-operations.md)を参照してください。

## 例
<a name="example"></a>

まず、項目を作成するために `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. 項目が作成されたときのレコード

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

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

```
{
  "_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
    }
  }
}
```