同時実行の競合の検出および解決

同時実行の競合は、後で同期される異なる 2 つのレプリカで同じ項目または変更単位が変更された場合に発生します。Sync Framework には、同時実行の競合の検出と解決を容易にする変更適用元オブジェクトが用意されています。

変更適用元が同時実行の競合を検出および解決する方法

同時実行の競合は、同期先レプリカの変更のバージョンが同期元レプリカのナレッジに存在しない場合に検出されます。

Sync Framework には、同期先プロバイダーが同時実行の競合を検出するために使用できる変更適用元オブジェクトが用意されています。変更適用元は、同期元プロバイダーから送信された変更バッチ内の項目ごとに次の手順を実行して、同時実行の競合を検出します。

  1. 同期先レプリカの項目のバージョンが同期元レプリカのナレッジに存在するかどうかを確認します。

  2. 同期先レプリカの項目のバージョンが同期元レプリカのナレッジに存在しない場合、変更は競合しています。

変更適用元は、同時実行の競合を検出した後、セッションに対して設定されている競合の解決方法か、指定された競合に対してアプリケーションが設定した競合解決アクションに従って競合を解決します。

変更適用元を使用した同時実行の競合の検出

変更適用元を使用して変更を検出するために、同期先プロバイダーはまず、変更適用元オブジェクトを作成します。

マネージ コード NotifyingChangeApplier オブジェクトを作成します。

アンマネージ コード IID_ISynchronousNotifyingChangeApplierIProviderSyncServices::CreateChangeApplier メソッドに渡して、ISynchronousNotifyingChangeApplier オブジェクトを作成します。

次に、同期先プロバイダーは、同期元プロバイダーから送信された変更バッチ内の項目ごとに、そのバージョン情報を提供する必要があります。これには、次の 2 とおりの方法があります。

  • 同期先プロバイダーが、同期元プロバイダーから送信された変更バッチに対応するバージョンの一覧を構築します。変更適用元は、この一覧を使用して、項目の同期先のバージョンが同期元レプリカのナレッジに存在するかどうかを確認します。

    マネージ コード この一覧を作成するには、System.Collections.Generic.IEnumerable<Microsoft.Synchronization.ItemChange> 型のオブジェクトを作成します。同期元プロバイダーの変更バッチに含まれる各項目について、この一覧に項目を追加します。一覧には、同期先レプリカの項目のバージョンが含まれます。ApplyChanges など、適切なメソッドのオーバーロードの destinationVersions パラメーターとして、この一覧を変更適用元に渡します。

    アンマネージ コード この一覧を作成するには、IProviderSyncServices::CreateDestinationChangeVersionsBuilder を呼び出して、IDestinationChangeVersionsBuilder オブジェクトを作成します。IDestinationChangeVersionsBuilder::AddItemMetadata を呼び出して、同期元プロバイダーの変更バッチに含まれる各項目について、この一覧に項目を追加します。一覧には、同期先レプリカの項目のバージョンが含まれます。IDestinationChangeVersionsBuilder::GetChangeEnumerator を呼び出して一覧の列挙子を取得し、それを ApplyChanges メソッドの pDestinationVersions パラメーターとして変更適用元に渡します。

  • また、同期先バージョンの一覧を同期先プロバイダーから変更適用元に渡さない方法もあります。その場合、同期先プロバイダーは、TryGetDestinationVersion メソッド (マネージ コードの場合) または ISynchronousNotifyingChangeApplierTarget::GetDestinationVersion メソッド (アンマネージ コードの場合) を実装します。変更適用元は、このメソッドを、同期元プロバイダーの変更バッチに含まれる各項目につき 1 回呼び出します。同期先プロバイダーは、このメソッドで同期先レプリカの項目のバージョンを参照し、変更適用元に返します。変更適用元は、それを基に変更が競合するかどうかを確認できます。

最後に、同期先プロバイダーは、変更適用元の ApplyChanges メソッド (マネージ コードの場合) または ISynchronousNotifyingChangeApplier::ApplyChanges メソッド (アンマネージ コードの場合) を呼び出します。

変更適用元を使用した同時実行の競合の解決

同期先プロバイダーは、競合を解決するために変更適用元を使用します。その際、変更適用元は、プロバイダーが指定した変更適用元の対象オブジェクトの呼び出しをディスパッチします。競合の解決方法が指定されている場合、変更適用元はそれを基に、発生した各競合を解決するのに必要な競合解決アクションを決定します。カスタムの競合の解決方法が指定されている場合、変更適用元は同期アプリケーションに競合を通知し、アプリケーションが競合解決アクションを指定します。どちらの場合も、変更適用元が適切な変更適用元の対象メソッドを呼び出し、変更適用元の対象オブジェクトが処理 (レプリカへの変更の保存や、後の処理に備えた競合のログ記録など) を行います。

同期アプリケーションは、通常は同期の開始前に同時実行の競合の解決方法を指定します。

マネージ コード アプリケーションは、同期先プロバイダーの ConflictResolutionPolicy プロパティを目的の値に設定することで、解決方法を指定します。

アンマネージ コード アプリケーションは、ISyncSession::Start メソッドの resolutionPolicy パラメーターで解決方法を指定します。同期先プロバイダーは、IKnowledgeSyncProvider::ProcessChangeBatch メソッドの resolutionPolicy パラメーターとして、この解決方法を受け取ります。

変更適用元が変更適用元のターゲットにメソッドを適切にディスパッチできるように、同期先プロバイダーは、競合の解決方法を変更適用元に渡します。変更適用元のターゲットは、INotifyingChangeApplierTarget オブジェクト (マネージ コードの場合) または ISynchronousNotifyingChangeApplierTarget オブジェクト (アンマネージ コードの場合) によって表されます。

Sync Framework では、次の同時実行の競合の解決方法が定義されています。

競合の解決方法 説明

SourceWins (マネージ コードの場合)、CRP_SOURCE_PROVIDER_WINS (アンマネージ コードの場合)

同期元レプリカで行われた変更が常に優先されます。これは、同期先レプリカを信頼しないようにする、読み取り専用の同期ソリューションをサポートします。Sync Framework によって、SourceWins (マネージ コードの場合) または SRA_ACCEPT_SOURCE_PROVIDER (アンマネージ コードの場合) の競合解決アクションが指定されます。

DestinationWins (マネージ コードの場合)、CRP_DESTINATION_PROVIDER_WINS (アンマネージ コードの場合)

同期先レプリカで行われた変更が常に優先されます。リモート クライアントで行われた変更を同期先レプリカに反映しないようにする場合に使用します。Sync Framework によって、DestinationWins (マネージ コードの場合) または SRA_ACCEPT_DESTINATION_PROVIDER (アンマネージ コードの場合) の競合解決アクションが指定されます。

ApplicationDefined (マネージ コードの場合)、CRP_NONE (アンマネージ コードの場合)

変更適用元は、ItemConflicting イベント (マネージ コードの場合) または ISyncCallback::OnConflict メソッド (アンマネージ コードの場合) を使用して、競合が発生したときに各競合について同期アプリケーションに通知します。アプリケーションでは、競合している項目を調査し、マネージ コードの場合は SetResolutionAction、アンマネージ コードの場合は IChangeConflict::SetResolveActionForChange または IChangeConflict::SetResolveActionForChangeUnit を呼び出して、競合解決アクションを指定します。

カスタム競合解決の指定

発生した同時実行の競合ごとに競合解決アクションを動的に指定するために、アプリケーションでは同期の開始前に次の操作を実行します。

マネージ コード

  • 同期先プロバイダーの ItemConflicting イベントのイベント ハンドラーを登録します。

  • 同期先プロバイダーの ConflictResolutionPolicy プロパティを ApplicationDefined に設定します。

アンマネージ コード

同期中に、変更適用元は、検出した同時実行の競合ごとに 1 回だけ ItemConflicting イベント (マネージ コードの場合) を生成するか、ISyncCallback::OnConflict メソッド (アンマネージ コードの場合) を呼び出します。アプリケーションでは、競合する 2 つの変更を調査して、メタデータまたは項目データに変更を加えることができます。さらに、マネージ コードの場合は SetResolutionAction メソッド、アンマネージ コードの場合は IChangeConflict::SetResolveActionForChange メソッドまたは IChangeConflict::SetResolveActionForChangeUnit メソッドを使用して、競合の解決アクションを設定できます。その後、変更適用元が競合を処理し、変更適用元の対象オブジェクトに適切な呼び出しをディスパッチします。

注意

項目内のすべての競合変更単位に対して同じ競合解決アクションが指定されている必要があります。そうしないと予期しない結果になるおそれがあります。このような競合解決が必要である場合は、競合をマージによって解決し、同期先プロバイダーで解決を処理するように指定します。

マネージ コードで使用される同時実行の競合解決アクション

Sync Framework には同時実行の競合に対する一連の解決アクションが用意されており、変更適用元はそれらを利用することで、解決処理の大半を実行できます。

競合解決アクション 説明

SourceWins

同期元レプリカで行われた変更が優先されます。変更適用元は、変更を SaveItemChange メソッドまたは SaveChangeWithChangeUnits メソッドに渡し、UpdateVersionAndData の保存操作を指定します。この変更は、競合しない変更とまったく同じように同期先レプリカに適用されます。

DestinationWins

同期先レプリカで行われた変更が優先されます。変更適用元は、バージョンのみの変更を SaveItemChange メソッドまたは SaveChangeWithChangeUnits メソッドに渡し、UpdateVersionOnly の保存操作を指定します。同期先レプリカ上のメタデータでは、項目のバージョン情報だけが更新されます。項目データは変更されません。

Merge

同期元項目のデータを同期先項目にマージします。変更適用元は、同期元レプリカの変更データを SaveItemChange メソッドまたは SaveChangeWithChangeUnits メソッドに渡し、UpdateVersionAndMergeData の保存操作を指定します。同期先プロバイダーは、同期元の項目データと同期先の項目データを結合し、その結果を同期先レプリカに適用します。

SaveConflict

競合をログに記録します。変更は適用されません。変更適用元は、競合データを SaveConflict メソッドに渡します。これにより、競合ログに競合が保存されます。競合のログ記録の詳細については、「競合のログ記録と管理」を参照してください。

SkipChange

競合を無視します。変更は適用されません。変更適用元は、競合データを同期先プロバイダーに渡しません。

最後に書き込みを行った方を優先させる

最後に行われた変更が優先されます。アプリケーションは、同期元レプリカに対する変更が実行された時間と、同期先レプリカに対する変更が実行された時間を取得します。そのために、2 つの変更に対して GetItemChangeTime または GetChangeUnitChangeTime を呼び出します。アプリケーションは、この 2 つの時間を比較し、最後に行われた変更を適用する競合解決アクションを指定します。たとえば、同期先の変更の方が後に行われていた場合、アプリケーションは DestinationWins の競合解決アクションを指定します。

アンマネージ コードで使用される同時実行の競合の解決アクション

Sync Framework には同時実行の競合に対する一連の解決アクションが用意されており、変更適用元はそれらを利用することで、解決処理の大半を実行できます。

競合解決アクション 説明

SRA_ACCEPT_SOURCE_PROVIDER

同期元レプリカで行われた変更が優先されます。変更適用元は、変更を ISynchronousNotifyingChangeApplierTarget::SaveChange メソッドまたは ISynchronousNotifyingChangeApplierTarget::SaveChangeWithChangeUnits メソッドに渡し、SSA_UPDATE_VERSION_AND_DATA の保存操作を指定します。この変更は、競合しない変更とまったく同じように同期先レプリカに適用されます。

SRA_ACCEPT_DESTINATION_PROVIDER

同期先レプリカで行われた変更が優先されます。変更適用元は、バージョンのみの変更を SaveChange メソッドまたは SaveChangeWithChangeUnits メソッドに渡し、SSA_UPDATE_VERSION_ONLY の保存操作を指定します。同期先レプリカ上のメタデータでは、項目のバージョン情報だけが更新されます。項目データは変更されません。

SRA_MERGE

同期元項目のデータを同期先項目にマージします。変更適用元は、同期元レプリカの変更データを SaveChange メソッドまたは SaveChangeWithChangeUnits メソッドに渡し、SSA_UPDATE_VERSION_AND_MERGE_DATA の保存操作を指定します。同期先プロバイダーは、同期元の項目データと同期先の項目データを結合し、その結果を同期先レプリカに適用します。

SRA_TRANSFER_AND_DEFER

競合をログに記録します。変更は適用されません。変更適用元は、競合データを ISynchronousNotifyingChangeApplierTarget::SaveConflict メソッドに渡します。これにより、競合ログに競合が保存されます。競合のログ記録の詳細については、「競合のログ記録と管理」を参照してください。

SRA_DEFER

競合を無視します。変更は適用されません。変更適用元は、競合データを同期先プロバイダーに渡しません。

最後に書き込みを行った方を優先させる

最後に行われた変更が優先されます。アプリケーションは、同期元レプリカに対する変更が実行された時間と、同期先レプリカに対する変更が実行された時間を取得します。そのために、2 つの変更に対して ISupportLastWriteTime::GetItemChangeTime または ISupportLastWriteTime::GetChangeUnitChangeTime を呼び出します。アプリケーションは、この 2 つの時間を比較し、最後に行われた変更を適用する競合解決アクションを指定します。たとえば、同期先の変更の方が後に行われていた場合、アプリケーションは SRA_ACCEPT_DESTINATION_PROVIDER の競合解決アクションを指定します。

参照

リファレンス

ISynchronousNotifyingChangeApplier インターフェイス
ISynchronousNotifyingChangeApplierTarget インターフェイス
CONFLICT_RESOLUTION_POLICY 列挙体
SYNC_RESOLVE_ACTION 列挙体
NotifyingChangeApplier
INotifyingChangeApplierTarget
ConflictResolutionAction
ConflictResolutionPolicy

概念

競合の処理
制約の競合の検出および解決