xConnectにおける楽観的並行性
このページの翻訳はAIによって自動的に行われました。可能な限り正確な翻訳を心掛けていますが、原文と異なる表現や解釈が含まれる場合があります。正確で公式な情報については、必ず英語の原文をご参照ください。
xConnectは楽観的並行性モデルを実装しています。これは、連絡先やファセットが読み取ってもロックされず、クライアントが変更を提出する際に並行性の競合を予期しなければならないことを意味します。計算されたファセットはxConnectサービスレイヤーによって自動的に再試されます。
以下の手順で、並行性競合時に何が起こるかを説明します。
- コンタクトやファセットが作成されると、ストレージプロバイダーによってそのレコードに対して並行トークンが生成されます。
- コンタクトやファセットがリクエストされると、コンカレンシートークンは応答に含まれます(すべてのコンタクトとファセットは ConcurrencyToken プロパティを持ちます)。
- コンタクトやファセットが修正されて保存されると、受信レコードがストレージ内のレコードと比較されます。同時実行トークンが異なる場合、そのレコードは最初に取得された後に別のソースによって更新されていることを意味します。この場合、xConnectは例外を投げて、現在ストレージにあるファセットを返します。
- この時点で、再挑戦するかどうかを選べます。
!重要楽観的並行性モデルとは、アップデートが必ず成功することを保証できないことを意味します。潜在的な並行性競合に対処しなければなりません。
紛争のリスク低減
競合のリスクを減らすために、連絡先やファセットを取得した後はできるだけ早く更新を送信することをお勧めします。例えば、Sitecoreのウェブトラッカーではセッション中にファセットの更新はできません。xConnect Client APIを使って即時にxConnectに更新を送信する必要があります。競合リスクを評価する際には以下の要素を考慮してください:
- 特定の側面や連絡先を更新できるシステムはどれだけあるか
- 特定のファセットや識別子がどのくらいの頻度で更新される可能性が高いか
競合する作戦の再挑戦
xConnectは、何らかの理由で1つ以上の操作が失敗した場合、例外をスローします。競合により操作が失敗した場合、以下のことができます:
- エンドユーザーに促さずに失敗した操作を自動的に再試行できます
- エンドユーザーがxConnectに提出する前に競合解決の動作を選択できるようにし、その後(例えば一括インポート時など)失敗した操作を自動的に再試行できるようにします。
- エンドユーザーに各競合を手動で解決するよう促す
- 何もしない
!注バッチ内で成功した操作は、1つ以上の操作が失敗してもロールバックされません。
以下の例は、競合によって失敗したすべての操作を取得し、再試行する方法を示しています:
using Sitecore.XConnect.Collection.Model; using Sitecore.XConnect; using System; using System.Threading.Tasks; using System.Linq; using Sitecore.XConnect.Operations; using Sitecore.XConnect.Client;
namespace Documentation { public class RetryOperation { public async void ExampleAsync() { using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient()) { try { // Get contact var reference = new Sitecore.XConnect.ContactReference(Guid.Parse("B9814105-1F45-E611-82E6-34E6D7117DCB"));
Task<Sitecore.XConnect.Contact> contactTask = client.GetAsync<Sitecore.XConnect.Contact>(reference, new Sitecore.XConnect.ContactExpandOptions(PhoneNumberList.DefaultFacetKey) { });
Sitecore.XConnect.Contact contact = await contactTask;
// Update some properties on an existing facet PhoneNumberList phoneNumberList = contact.PhoneNumbers();
phoneNumberList.PreferredKey = "Work"; phoneNumberList.PreferredPhoneNumber = new PhoneNumber("44", "5555555");
// Set updated facet client.SetPhoneNumbers(contact, phoneNumberList);
// Submit changes
await client.SubmitAsync();
}
catch (XdbExecutionException ex)
{
// Phone numbers changed since the facet was retrieved - a conflict error is thrown
// Get all operations where a conflict occurred - you must do this per operation type
var setPhoneOperations = ex.GetOperations(client).OfType<SetFacetOperation
// Cycle through each operation that set a PhoneNumberList facet foreach (var q in setPhoneOperations) { PhoneNumberList yourFacetValue = q.Facet; // Facet values that you tried to save - sync token is OUT OF DATE PhoneNumberList currentFacetValue = q.Result.CurrentVersion; // Current value returned by xConnect - sync token is CORRECT
// At this point you can either resolve conflicts automatically OR present the end user // with a message. In this scenario, we have decided to overwrite the facet values that were returend // by xConnect with our own values. It is entirely up to you what the logic should be.
currentFacetValue.PreferredKey = yourFacetValue.PreferredKey; currentFacetValue.PreferredPhoneNumber = yourFacetValue.PreferredPhoneNumber;
// Set the facet values again - note that if we passed in yourFacetValue, which has an out of date sync token // the operation would fail. If the facet has changed AGAIN since it was retrieved, the operation will fail and you must // choose whether to retry again.
client.SetFacet
await client.SubmitAsync(); } } } }
public void ExampleSync() { using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient()) { var preferredKey = "Work"; var preferredPhoneNumber = new PhoneNumber("44", "5555555");
try { // Get contact var reference = new Sitecore.XConnect.ContactReference(Guid.Parse("B9814105-1F45-E611-82E6-34E6D7117DCB"));
Sitecore.XConnect.Contact contact = client.Get<Sitecore.XConnect.Contact>( reference, new ContactExpandOptions(PhoneNumberList.DefaultFacetKey) { });
// Update some properties on an existing facet PhoneNumberList phoneNumberList = contact.PhoneNumbers();
phoneNumberList.PreferredKey = preferredKey; phoneNumberList.PreferredPhoneNumber = preferredPhoneNumber;
// Set updated facet client.SetPhoneNumbers(contact, phoneNumberList);
// Submit changes client.Submit(); } catch (AggregateException ex) { // Phone numbers changed since the facet was retrieved - a conflict error is thrown // Get all operations where a conflict occurred - you must do this per operation type foreach (var e in ex.Flatten().InnerExceptions) { if (e is FacetOperationException exception && exception.Result == SaveResultStatus.Conflict) { // At this point you can either resolve conflicts automatically OR present the end user // with a message. In this scenario, we have decided to overwrite the facet values that were returned // by xConnect with our own values. It is entirely up to you what the logic should be.
// Set the facet values again. // If the facet has changed AGAIN since it was retrieved, the operation will fail and you must // choose whether to retry again. var entityId = exception.EntityId; var facetKey = exception.FacetKey;
Sitecore.XConnect.Contact contact = client.Get<Sitecore.XConnect.Contact>( new ContactReference((Guid)entityId), new ContactExpandOptions(facetKey));
PhoneNumberList phoneNumberList = contact.PhoneNumbers();
phoneNumberList.PreferredKey = preferredKey; phoneNumberList.PreferredPhoneNumber = preferredPhoneNumber;
client.SetFacet
client.Submit(); } } } } } } }
!注競合を解決する一般的な方法はなく、競合を引き起こす可能性のある各操作タイプやファセットタイプを考慮しなければなりません。
.ファセット対 。結果。現在のバージョン
各SetFacetOperationオブジェクトの.Facetプロパティは、提出しようとしたファセットオブジェクトを表しています。.Result.CurrentVersionプロパティはxConnectから返された最新のファセットを表しています。返されたファセットを使って、エンドユーザーに正しい値を選ぶよう促したり、変更を.Result.CurrentVersionにマッピングして再挑戦したりできます。
!重要再送信はしないでください.Facet同期トークンは有効です。プロパティを.Result.CurrentVersionオブジェクトにマッピングして、そちらを提出してください。