February 2011

Volume 26 Number 02

予測: クラウド - SQL Azure によるブランチ ノードの同期、第 2 部: サービスベースの同期

Joseph Fultz | February 2011

このコラムでは、Sync Framework 4.0 のプレリリース版を取り上げています。すべての情報は変更される可能性があります。

先月は、SQL Azure およびその接続先のさまざまなノードと、企業データベースを同期するための一般的なアーキテクチャと設計について説明しました。フィルター処理による最適化と地理的分散を利用した最適化、またはこの 2 つの方法を組み合わせてデータの配布と収集を行うネットワーク全体を最適化する方法を紹介しました。

今月は、同期サービスをホストする Windows Azure を追加し、クラウドのサービス インターフェイスを介して同期を行う方法について説明します。これにより、同期メカニズムを拡張して、データベースとの直接同期によって処理できるノード数よりも、はるかに多くのエンド ノードを処理することができます。今月のコラムでは、2010 年 10 月に公開された Microsoft Sync Framework 4.0 の Community Technology Preview (CTP) リリース (bit.ly/dpyMP8、英語) を使用します。このリリースは、1 月号で使用した Microsoft Sync Framework 2.1 を基に作成されています。

同期サービスはバージョン 2.1 を基盤として作成できます。参考になるサンプルとチュートリアルについては、bit.ly/bibIdl (英語) および bit.ly/epyImQ (英語) を参照してください。ただし、4.0 CTP がリリースされ、そのインターネットを重視した要素を利用できるため、このコラムの Windows Azure 同期サービスには 4.0 CTP を利用する方が理に適っています。運用できるソリューションを作成するには、依然としてかなりの量のコードを記述する必要がありますが、最終的には OData を使用するデバイスであればどのデバイスからも利用できる同期サービスが完成します。

インターネット規模で同期する

先月のコラムでは、データベースとの直接同期を拡張する方法について、いくつか考え方を紹介しました。ただし、それほど多くないにしても、拡張の問題を簡単には解決できない理由がいくつかあります。先月説明した方法では対応できない状況をざっと考えただけでも、すぐにいくつか思い付くことができます。

  1. データ間の関係により、データを容易に分割できない。
  2. 論理的な分割方法がなく、分割する場合は自由裁量で分割することになり、ソリューションのさまざまな領域で、予期しないホット スポットが生じる可能性が高い。
  3. 複製が必要なデータが多いため、複数の場所にデータを置かなければならない場合、コストが増大し、採算が合わない。
  4. 小売店で一日の終わりに数百または数千件の処理が発生する場合など、膨大な件数の同期処理が行われるときに、分割をしていたとしても競合が発生する。

上記は、SQL Azure との直接同期以外の設計が必要になる理由のすべてではないことは明らかですが、話の口火を切り、問題の解決策を考えるには十分でしょう。コンピューター サイエンスの常道として、ここでも、間接指定の層を挿入することで上記の問題の解決を試みます。今回の場合、この層は、SQL Azure インスタンスと直接同期するのではなく、同期ポイントとして使用される Windows Azure Web ロールにホストされるサービス層になります。先月の最終的なアーキテクチャの図に、Windows Azure にホストされる同期サービスのプレースホルダーを追加して、図 1 のような論理設計にしました。

般的な企業のアーキテクチャ

図 1 一般的な企業のアーキテクチャ

はじめに

Sync Framework 4.0 は、このような問題の解決するうえで特に有効です。ただし、データベース間の直接同期という単純なモデルに比べれば、手間がかかります。4.0 CTP には、ヘルプ ファイルに「Creating a Sync Service in Windows Azure」(Windows Azure で同期サービスを作成する、英語)というタイトルの、役に立つサンプルとチュートリアルが付属しています。ここでは、これを同期サービスの実装を説明する基礎として使用します。OData と連携するプラットフォームとの同期を実現するために抽象化を行う必要があるため、クライアント コードはやや手間がかかります。これは、4.0 には、利用できるクライアント側のランタイム ライブラリがないためです。ただし、4.0 のサンプルには、SQL Server CE を使用した Windows Mobile 6.5 用のサンプルがあります。ここでは、このサンプルから必要なコードを抜き出し、標準の SQL Server で機能するように変更しました。2010 年 10 月の 4.0 CTP では、オブジェクトの特定のセットを使用して同期アクティビティを実行しますが、まず、これらのオブジェクトを理解しておくと役立ちます。クライアント アプリケーションでは、OData を使用して同期サービスとの通信を処理する CacheController を使用します。ローカルでは、CacheController は、アプリケーションとデータ間のインターフェイスとして OfflineSyncProvider を使用します (図 2 参照)。OfflineSyncProvider は、各データ ストア固有で、おそらくターゲット プラットフォーム別になるインターフェイスです。サンプルに基づくこのコラムの実装には、ローカル データ アクセスの処理に使用する StorageHandler オブジェクトがあります。OfflineSyncProvider は CacheController によって使用される既知の型ですが、StorageHandler はバックエンド ストアとのすべての相互作用を処理するために作成したカスタム コードです。OfflineSyncProvider はデータ アクセス ライブラリ上のインテリジェンスであり、StorageHandler はデータ アクセス ライブラリであると考えてください。注意が必要なのは、4.0 CTP には Silverlight クライアント分離ストレージ用の組み込み CacheController しか付属していないことです。このため、標準の SQL Server を使用するには、そのための処理が必要です。オブジェクトと相互作用の境界の概要を図 2 に示します。

Sync Framework 4.0 クライアントの同期オブジェクト

図 2 Sync Framework 4.0 クライアントの同期オブジェクト

クラウド同期サービスを開発する

悪い知らせを先に話し、良い知らせは最後に話すようにいつも言われてきました。そうすることで、会話 (と願わくば会話の参加者の気持ち) を明るい雰囲気で締めくくることができます。しかし、ここではこの順序を逆にして、まず、簡単な方のメリットを示してこのソリューションを認めていただこうと思います。クライアント側では多くの作業が必要ですが、サーバー側には多くの便利な機能が Sync Framework に用意されています。私が進行役を務めたある設計についてのセッションで、葬儀サービス (葬儀、墓地、棺など) を販売されている方に、"商品自体" に焦点を当てたら何ひとつ売ることができないので、"商品の機能" に焦点を当てる必要があると言われたことがあります。葬儀サービスの場合は、棺桶でも墓穴でもなく、心の平安こそが求められている真の商品です。そのような場合に、Sync Framework を使用します。Sync Framework 2.1 では、さまざまな作業が開発者に代わって処理されていましたが、サービスベースの同期となると、目標には今一歩届きませんでした。2.1 では、さまざまなデバイスやプラットフォームから、インターネットに接続している同期サービスを介して提供されるデータと同期する必要があっても、そのようなデバイスやプラットフォームにまったく対応していませんでした。現在では一般用語となった IT の "コンシューマライゼーション" により、企業は社内のあらゆるレベルで使用されているさまざまなデバイスに対応しなければならなくなっています。Sync Framework 4.0 CTP は、このような課題、特にこのような各種デバイスとのデータ同期への対応を支援することを目的にしています。

このソリューションのサーバー側コンポーネントをセットアップするのは、非常に簡単です。基本的には、以下の作業を行います。

  1. データベースを定義する
  2. そのデータベースの構成ファイルを作成する
  3. SyncServiceUtil を使用し、その構成ファイルを使用してデータベースをプロビジョニングする
  4. SyncServiceUtil を使用して、同期サービスに必要なクラスを生成する
  5. 同期サービスをホストする Windows Azure ベースの Web ロールを作成する
  6. 展開する

私と同じように反応するなら、この手順の概要を読んで、「どの構成ファイルのことか」と疑問に思われるでしょう。このファイルのスキーマは、MSDN ライブラリ (bit.ly/h2FJod、英語) で公開されています。このスキーマを使用し、4.0 に付属の ListDB データベースとこのデータベース用の関連構成ファイルを参照することで、データベースを表すカスタムの構成ファイルを最も簡単に作成できます。このファイルを用意できたら、Windows Azure ベースのサービスを作成するのは簡単です。まず、ターゲット データベース (この場合は 4.0 SDK の ListDB サンプル) を Windows Azure に作成する必要があります。データベースを作成したら、新しい SyncServiceUtil を使用して、次のようなコマンドにより、データベースをプロビジョニングします。

SyncSvcUtil /mode:provision 
/scopeconfig:listdbconfig.xml

構成ファイルに設定する必要がある情報の 1 つは、SQL Azure データベースへの接続です。構成ファイルの最後のほうに、<TargetDatabase /> 要素があります。この要素をクラウド用に適切に構成する必要があります。

<Databases>
  <TargetDatabase Name="listdb" DbServer="[URI for the SQL Azure DB 
   Instance]" DbName="listdb" UserName="[username]" Password="[password]" 
   UseIntegratedAuth="false" /> 
</Databases>

ユーティリティを実行すると、DefaultScopeEntities.cs と DefaultScopeSyncServices.svc の 2 つのファイルが生成されます。ファイル名の "DefaultScope" の部分は構成ファイルから取得される文字列で、<SyncScope /> 要素に設定されています。

<SyncScope Name="DefaultScope" IsTemplateScope="true">

DefaultScopeEntities.cs はほぼ記述されているとおりですが、DefaultScopeSyncServices.svc は、サービスの呼び出しをインターセプトして、カスタム ロジックを追加できるようにする部分クラスを生成するため (4.0 の新機能)、より重要です。基本の同期ロジックは、すべて、ベース オブジェクトの一部として含まれています。図 3 は、DefaultScopeSyncService クラスと、テンプレートの種類が SyncService テンプレート クラスである関連するエンティティ クラスを示しています。

SyncServices により生成されたコードのオブジェクト ブラウザー ビュー

図 3 SyncServices により生成されたコードのオブジェクト ブラウザー ビュー

図 3 の右側には、同期を実行するために公開されるサービス インターフェイスが一部表示されています (これらは、Sync Framework 2.1 を直接使用する際に公開する必要があるインターフェイスに相当します)。同期プロセスにカスタム ロジックを追加する必要がある場合は、単純に DefaultScopeSyncServices.svc ファイルを開き、メソッド インターセプターを選択して、必要なコードを記述します。作成したばかりのサービス インターフェイスを使用して基本同期を実装するには、このファイルが含まれるサービスまたは Web プロジェクトと任意の Web ロールを関連付け、アクティブ化のコンテキストを作成するコード行を WebRole:OnStart メソッドに追加するだけです。

public override bool OnStart()
{
  DiagnosticMonitor.Start("DiagnosticsConnectionString");

  // For information on handling
  // configuration changes, see the MSDN topic at 
  // go.microsoft.com/fwlink/?LinkId=166357
  RoleEnvironment.Changing += RoleEnvironmentChanging;
  Microsoft.Samples.Synchronization.ActivationContext.
    CreateActivationContext();
  return base.OnStart();
}

次に、構成を 2、3 か所変更して、Sync Framework バイナリを CopyAlways に設定します。新しいサービス インターフェイスを活用するため、4.0 の Microsoft.Synchronization.dll を参照し、パッケージと併せて発行されるように設定します。その後、これを Web ロールに発行すれば完了です。「jofultz.cloudapp.net/defaultscopeSyncService.svc/$syncscopes」のような要求をブラウザーに入力して、現在利用可能な同期スコープを要求することで、簡単なテストを実行できます。以下のような応答が返されれば、サービスが機能していることを確認できます。

- <service xml:base="http://rd00155d3a1a55:20000/defaultscopesyncservice.svc/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns="http://www.w3.org/2007/app">
- <workspace>
  <atom:title>SyncScopes</atom:title> 
- <collection href="defaultscope">
  <atom:title>defaultscope</atom:title> 
  </collection>
  </workspace>
  </service>

また、他のデータを要求することもでき、何か変更があった場合は、既定で OData として変更内容が返されます。これは、ブラウザーからでも、ツールを使用しても実行できます。CodePlex の OData Viewer Tool (dataservicestool.codeplex.com/releases/view/52805、英語) を使用して、「jofultz.cloudapp.net/defaultscopeSyncService.svc/DefaultScope/DownloadChanges?userid=BA9152CC-4280-4DAC-B32D-1782E2E8C3D3」という変更のダウンロード要求を発行したところ、図 4 のような結果が返されました。

OData Viewer Tool での DownloadChanges の結果

図 4 OData Viewer Tool での DownloadChanges の結果

さいわいなことに、ここでは Sync Framework 4.0 CTP の追加機能によって、シンプルになった同期インターフェイスから OData ATOM 形式でも OData JSON 形式でも結果を取得できるようになりました。その結果、クライアントの面では、他のプラットフォームとの同期が可能になり、専用のデータ形式は過去のものになりました。おかげで、ここではユーティリティを実行し、プロジェクトを構成して、コード行を追加するだけで済みました。

クライアント同期を実装する

では、この実装の中で、ヘッドフォンを付けて集中し、さまざまな処理に取り組まなくてはならない箇所にとりかかりましょう。クラウド サービスはほぼ構成するだけでできましたが、クライアントの実装をゼロから始める場合は、もう少し作業が必要です。Sync Framework 4.0 CTP には分離ストレージ用の CacheController が付属しているため、Silverlight がターゲットのクライアント プラットフォームであれば、クラウド サービスの実装と同じくらい簡単にクライアントを実装できます。しかし、ここでのターゲットは SQL Server Standard または Express を実行する Windows クライアントなので、いくらか作業が必要です。ここでも SyncServiceUtil を必要なエンティティの生成に利用できますが、CacheController と OfflineSyncProvider を独自に作成する必要があります。さらに重要なのは、変更管理を容易にするため、データ ストアを変更する必要があることです。これには、バージョン 2.1 でプロビジョニングされたデータベースか、変更管理用の独自のカスタム スキーマを使用することが考えられます。しかし、このような実装では、データベース実装が複雑になり、コード ベースも複雑になるため、アプリケーション全体でかなりの作業が必要になり、複雑さが増す可能性があります。とは言え、Synch Framework の他の機能を利用するには必要な作業です。この話を人にすると、「なぜ単純にすべて自分でやらないのか」と聞かれます。その答えは簡単です。この方法で作業するのは、作業全体の量を減らすためと、この実装を他の 2.1 および 4.0 の Synch Framework 同期クライアントや同期エージェント (Windows 以外のプラットフォームも含む) でも使用できるようにするためです。

現在説明しているクライアントとサービスの部分にのみ関係する作業の内訳を見て見ましょう (図 5 参照)。ターゲットのクライアント プラットフォームによりますが、Sync Framework を使用することで、作業量を約 60% 以上削減できることがわかります。

図 5 クライアントとサービスの作業内訳

作業内訳 必要な処理
同期のための DB スキーマ (サーバー) 構成
サービスの実装 生成と 1 行のコード
同期内の検証フックのカスタマイズ フックは生成される。値の追加コードの作成のみが必要。
同期のための DB スキーマ (クライアント) 2.1 プロビジョニングまたはカスタムを使用可能
Silverlight 以外用の同期実装 カスタム
Silverlight 用の同期クライアント 構成と生成

Mobile 6.5 と SQL CE のサンプルで作業することで、クライアント同期を実装するための、データベースを使用した実装のサンプルを作成できました。図 6 の IsDirty、IsTombstone、および Metadata フィールドを確認してください。

カスタムの同期実装をサポートする列

図 6 カスタムの同期実装をサポートする列

スキーマを用意できたら、他にも必要なものが多数あります。

  1. 前述のとおり、CacheController 実装
    1. ローカル ストアの相互作用
    2. サービスの相互作用
    3. 同期競合ハンドラー
    4. 同期エラー ハンドラー
  2. OData を生成および使用するためのコード
  3. 同期対象のエンティティのコード定義
  4. ローカルの SQL Server データベースの OfflineSyncProvider

1. と 2. については、Windows Mobile 6.5 のサンプル用に提供されているコード (図 7 参照) を使用し、これをカスタムの CacheController プロジェクトに追加しています。このカスタム プロジェクトのコードは、すべてサンプルから流用したものです。

Windows Mobile 6.5 サンプルから流用したファイル

図 7 Windows Mobile 6.5 サンプルから流用したファイル

以前と同じ構成ファイルを使用して、SyncServiceUtil に "/mode:codegen" および "/target:client" フラグを付けてエンティティを生成します。これにより、クライアント側のオブジェクトを含む DefaultScopeEntities.cs ファイルが生成されます。Mobile 6.5 のサンプルを流用しているため、settings.cs、utility.cs、SqlCeOfflineSyncProvider.cs、DataStoreHelper.cs、SqlCeStorageHandler.cs を Windows フォーム プロジェクトにコピーします。できるだけコーディングの手間を省くため、図 8 のような変更を行いました。

図 8 コーディングの手間を最小限にするためのサンプル コードに対する変更

ファイルまたはプロジェクト 変更
DefaultScopeEntities.cs 流用したファイルで必要な型名に一致するように、クラス名を SqlCeOfflineEntity に変更する
 

CacheController 実装内で使用するため、

[Microsoft.Samples.Synchronization.ClientServices.KeyAttribute]

[System.ComponentModel.DataAnnotations.KeyAttribute()]

がある場所に追加する

カスタムの新しい CacheController プロジェクト

すべての名前空間を次に置き換える

namespace Microsoft.Samples.Synchronization.ClientServices.Custom

SqlCeOfflineSyncProvider.cs

カスタムの CacheController 実装を参照するために、

using Microsoft.Samples.Synchronization.ClientServices;

using Microsoft.Samples.Synchronization.ClientServices.Custom;

に置き換える

SqlCeStorageHandler.cs ファイル内の [connection].[transaction commands] をすべてコメント アウト。SQL Server を使用する場合は、SQL CE の場合とは少し異なる実装が必要です。このコードは、実際の実装では正しく追加し直す必要があります。
DataStoreHelper.cs ローカルの SQL Server インスタンスを参照するように、接続文字列を変更する
Settings.cs SyncServiceUrl に Windows Azure Sync Service の URI (ここでは、http://jofultz.cloudapp.net/DefaultScopeSyncService.svc/) を割り当てる
Utility.cs

カスタムの CacheController 実装を参照するために、

using Microsoft.Samples.Synchronization.ClientServices;

using Microsoft.Samples.Synchronization.ClientServices.Custom;

に置き換える

サンプル コードを利用して、上記の変更を行うことで、Utility.Sync 関数を呼び出す小さなコンソール アプリケーションを作成できます。Utility.Sync 関数が呼び出されると、OfflineSyncProvider と CacheController のインスタンスが作成されて、同期が実行されます。

var localProvider = new   
  SqlCeOfflineSyncProvider();
var controller = new CacheController(new 
  Uri(Settings.SyncServiceUrl), Settings.  
  SyncScope, localProvider);

では、変更済みのレコードをローカル ストアからフェッチするなどの処理を行うコードはどこにあるのかと疑問に思われる方もいるでしょう。このようなコードはすべて、StorageHandler 実装に含まれています。図 9 でその一部をご覧ください。

図 9 ローカル ストアに対するデータ コマンド

internal class SqlCeStorageHandler : IDisposable
  {
    #region SQL CE Commands

    private const string GET_ALL_PRIORITY = "SELECT [ID], [Name], [_
      MetadataID] FROM [Priority] WHERE [IsTombstone] = 0";

    private const string GET_ALL_STATUS = "SELECT [ID], [Name], [_
      MetadataID] FROM [Status] WHERE [IsTombstone] = 0";

    private const string GET_ALL_TAGS = "SELECT [ID], [Name], [_
      MetadataID] FROM [Tag] WHERE [IsTombstone] = 0";

    private const string GET_ALL_LISTS =
      "SELECT [ID], [Name], [Description], [UserID], [CreatedDate], 
      [IsTombstone], [_MetadataID] FROM [List] WHERE [IsTombstone] = 0";

    private const string GET_ALL_ITEMS =
      "SELECT ID, ListID, UserID, Name, Description, Priority, Status, 
      StartDate, EndDate, IsTombstone, [_MetadataID] FROM [Item] WHERE 
      [IsTombstone]=0 AND [ListID]=@ListID";

    private const string SELECT_ITEM_CHANGES =
      "SELECT ID, ListID, UserID, Name, Description, Priority, Status, 
      StartDate, EndDate, IsTombstone, [_MetadataID] FROM [Item] WHERE 
      IsDirty = 1";

    private const string SELECT_LIST_CHANGES =
      "SELECT ID, Name, Description, UserID, CreatedDate, IsTombstone, 
      [_MetadataID] FROM [List] WHERE IsDirty = 1";

    private const string SELECT_TAGITEMMAPPING_CHANGES =
      "SELECT TagID, ItemID, UserID, IsTombstone, [_MetadataID] FROM 
      [TagItemMapping] WHERE IsDirty = 1";

したがって、操作は次のような順番で処理されます。

  1. クライアント アプリケーションから任意の同期関数を呼び出す
  2. 同期関数が次の処理を行う
    1. OfflineSyncProvider のインスタンスを作成する
    2. CacheController (カスタム) のインスタンスを作成し、サービス URI と OfflineSyncProvider を渡す
    3. 最後に、CacheController.Refresh() を呼び出す
  3. CacheController が、Windows Azure 内の同期サービスとの通信を処理する CacheRequestHandler を作成する
  4. CachController が OfflineSyncProvider にローカルの変更セットを要求する
  5. OfflineSyncProvider が StorageHandler を使用して、ローカルの SQL Server から変更を取得する
  6. CacheController が変更セットを使用して要求を作成し、その要求を CacheRequestHandler に渡す
  7. CacheRequestHandler が適切なフォーマッタ (ここでは OData ATOM) を使用して、適切な要求を作成し、この要求を Sync Service URI に送る

当然、パッケージを展開してデータをクライアントに戻すには、基本的に、これと同じ処理を逆の順番で行うだけです。図 4 は、サービスから返される OData パッケージを示しています。

最後に

言うまでもないことですが、トランザクションのサポートを削除し、オブジェクトの SqlCe[suffix] などの不適切な情報を保持することは、実際の実装では行うべきことではありませんが、ここでは、まったく新しいコードを記述せずに、運用可能なクライアントを作成するという目的を果たしています。SQL Server CacheController を作成する場合は、まず Windows Mobile 6.5 のサンプルを基にリファクタリングと名前の変更を行い、次にデータ ストア固有の情報が必要な StorageHandler 内のコマンドを設定するだけで簡単に作成できます。

このコラム最大の目標は、サービスベース同期アーキテクチャの具体例を示すことでした。スケール変更に必要になるキャッシュやその他の最適化については、ここでは敢えて触れていませんが、このような最適化については、通常、よく理解されています。また、Sync Framework 4.0 CTP の機能を説明しながら、この CTP ではどのような機能があり、どのような機能がなく、どのようなことが可能かをお伝えしたいと考えていました。これらの目標を達成できていたらさいわいです。

SQL Azure Data Sync CTP 2 は開発中ですが、この CTP 2 では、構成とクライアント側エージェントのダウンロードにより、クライアント コンポーネントも含めて、すべての実装をセットアップできるようになることが約束されています。もちろん、それは Windows ベースのコンピューターの場合になりますが、各種プラットフォームでの利用を目標としている場合は、Sync Framework 4.0 を直接使用する方がお勧めかもしれません。

最新の Sync Framework SDK をダウンロードして、少なくとも SQL Azure データベースを使用して Windows Azure に同期サービスをセットアップするチュートリアルを実行し、Silverlight クライアント向けのサンプルを確認して、Sync Framework の感触を掴むことをお勧めします。もう少し意欲がある方は、ここで説明したように 4.0 CTP に付属の Windows Mobile 6.5 サンプル (2 つのプロジェクトがあります) のファイルを使用して、独自の Windows ベースの同期クライアントを作成してみてください。

Joseph Fultz は、ダラスにある Microsoft Technology Center のアーキテクトで、企業ユーザーおよび ISV と連携して、ビジネスや市場の要求に応じてソフトウェア ソリューションを設計しプロトタイプを作成しています。Tech·Ed などのイベントや、同様の社内トレーニング イベントで講演を行っています。

この記事のレビューに協力してくれた技術スタッフの Ganeshan Iyer に心より感謝いたします。