オフライン データ同期

オフライン データ同期は、Azure Mobile Apps の SDK 機能です。 データはローカル ストアに格納されます。 アプリがオフラインの場合でも、データの作成、変更、および検索を行うことができます。 お使いのデバイスがオンラインになっている場合、データは Azure Mobile Apps サービスと同期されます。 SDK では、クライアントとサービスの両方で同じレコードが変更された場合の競合解決がサポートされます。

オフライン同期には、次のような複数の利点があります。

  • アプリの応答性が向上する
  • ネットワーク接続に問題がある場合のアプリの信頼性が向上する
  • 待機時間の長いネットワークや従量制ネットワークの使用を制限する
  • 切断された状態での使用をサポートする

次のチュートリアルでは、Azure Mobile Apps を使用してモバイル クライアントにオフライン同期を追加する方法を示します。

同期テーブルについて

Azure Mobile Apps SDK には、サービスに直接アクセスする IRemoteTable<T> が用意されています。 デバイスにネットワーク接続がない場合、操作は失敗します。 IOfflineTable<T> によって提供される同期テーブルは、同じ操作をローカル ストアに対して提供します。 ローカル ストアは後でサービスと同期することができます。 操作を実行する前に、ローカル ストアを初期化する必要があります。

ローカル ストアについて

ローカル ストアは、クライアント デバイス上のデータ永続化レイヤーです。 ほとんどのプラットフォームではローカル ストアに SQLite を使用しますが、iOS ではコア データを使用します。 独自のローカル ストアを実装することもできます。 たとえば、あるバージョンの SQLite と SQLCipher を使用して暗号化されたストアを生成します。

オフライン同期のしくみ

クライアント コードによって、ローカルの変更がデータ同期サービスと同期されるタイミングが制御されます。 ローカルの変更をプッシュするまで、サービスには何も送信されません。 同様に、新しいデータや更新されたデータがローカル ストアに入力されるのは、データをプルしたときだけです。

すべてのテーブル、テーブルの一覧、または 1 つのテーブルに対して、保留中の操作をプッシュできます。

// All tables
await client.PushTablesAsync();

// A list of tables
var tablesToPush = new string[] { "table1", "table2" };
await client.PushTablesAsync(tablesToPush);

// A single table
await table.PushItemsAsync();

同期

プッシュ操作では、操作キュー内のすべての保留中の変更をサービスに送信します。 保留中の変更は、HTTP REST 呼び出しを通じてサービスに送信され、その後、データベースが変更されます。

プッシュ操作はすべてのプル操作の前に実行されます。 プル操作では変更されたデータをサービスからプルし、ローカル ストアに格納します。

暗黙的なプッシュ

保留中のローカルの更新があるテーブルに対してプルを実行すると、そのプル操作では、まず対象のテーブルに対してプッシュが実行されます。 このプッシュにより、キュー済みの変更とサーバーの新規データとの競合が最小限に抑えられます。 必要に応じて、PullOptionsPushOtherTables を設定すると、すべてのテーブルのプッシュを構成できます。

var pullOptions = new PullOptions { PushOtherTables = true };
await table.PullItemsAsync(pullOptions);

レコードのサブセットのプル

必要に応じて、オフライン データベースに含めるレコードを決定するために使用するクエリを指定できます。 次に例を示します。

var query = table.CreateQuery().Where(x => x.Color == "Blue");
await table.PullItemsAsync(query);

増分同期

Azure Mobile Apps では増分同期が実装されるため、前回のプル操作以降に変更されたレコードのみがプルされます。 増分同期を使用すると、大きいテーブルを処理するときの時間と帯域幅を節約できます。

一意のクエリごとに、最後に正常に転送されたレコードの UpdatedAt フィールドがオフライン ストアにトークンとして保存されます。 最後の UpdatedAt の値は、デルタ トークン ストアに格納されます。 デルタ トークン ストアは、オフライン ストアのテーブルとして実装されます。

パフォーマンスと整合性

場合によっては、同期が途中で停止することがあります。 次に例を示します。

  • 同期に使用していたネットワークが、同期プロセス中に利用できなくなった場合。
  • 同期中にアプリケーションを強制的に閉じた場合。

オフライン データベース内で整合性の問題が発生するリスクを最小限に抑えるために、各レコードは受信と同時にデータベースに書き込まれます。 必要に応じて、レコードをバッチ単位でデータベースに書き込むこともできます。 バッチ操作により、プル操作中のオフライン データベース書き込みのパフォーマンスが向上します。 ただし、テーブル メタデータとテーブル内のデータの間に不整合が生じるリスクも高くなります。

書き込みの間隔は次のように調整できます。

var pullOptions = new PullOptions { WriteDeltaTokenInterval = 25 };
await table.PullItemsAsync(pullOptions);

このコードは、書き込みを 25 件のレコードのバッチに集約します。 パフォーマンス テストでは、パフォーマンスが向上する最大の値は 25 であると示唆されています。 WriteDeltaTokenInterval の値を 25 より大きくしても、パフォーマンスが大幅に向上することはありません。

消去

ローカル ストアの内容は、IOfflineTable<T>.PurgeItemsAsync を使用してクリアできます。 消去は、クライアント データベースに古いデータがある場合、または保留中の変更をすべて破棄する場合に必要になることがあります。 消去により、ローカル ストアからテーブルがクリアされます。 テーブルを消去する方法を次に示します。

await table.PurgeItemsAsync("", new PurgeOptions());

テーブル内に保留中の変更があると、PurgeItemsAsync() メソッドは InvalidOperationException エラーをスローします。 この場合は、次の方法で強制的に消去を実行できます。

await table.PurgeItemsAsync("", new PurgeOptions { DiscardPendingOperations = true });

消去は、オフライン ストア内のテーブルをクリーンアップするための最後の手段です。これにより、キャッシュからすべてのレコードがワイプされ、レコードの再ダウンロードが必要になります。