7

こんにちは
、3 層アプリケーションで同時実行制御を実装する最良の方法を知りたいですか? 5月の最初の考えは次のとおりです。

  1. クライアントは、データセットからレコードを編集したいと考えています。
  2. そのレコードのロックを要求するリクエストをサーバーに送信します
  3. サーバーはロックテーブルに基づいて編集リクエストを承認/拒否します

このシナリオに基づいて、ロックには、ロックされたレコードとそのレコードを使用しているクライアントの両方への参照が必要です。
クライアントは定期的にキープアライブ メッセージをサーバーに送信する必要があります。キープアライブは、編集操作中にクライアントを失った場合に備えて、ロックされたレコードを解放するために使用されます。

Datasnap で Delphi を使用します。初歩的な質問かもしれませんがよろしくお願いします!!

4

3 に答える 3

3

私は、ユーザーがレコードを編集したいときにレコードをロックしたり、同時実行を制御しようとしたりしないことで、楽観的同時実行制御を念頭に置いてアプリケーションを設計します。

重要な計算と更新は、クライアントによって適用された更新の処理中に適切な組み込みのデータベースロック機能が設定された後、サーバー側(アプリケーションまたはデータベース)で実行されます。DataSnapの自動トランザクションロールバックは、障害が発生した場合にこれらのロックが他の同時ユーザーをブロックするのを防ぎます。

DataSnapを使用すると、フィールドにProviderFlagsを適切に使用して、2人のユーザーの編集が衝突したときにデータが失われるのを防ぐための完全な制御が可能になります。自動的にチェックするフィールドのpfInWhereを、レコードが読み取られたときと同じ値になるように編集/削除時に設定します。

さらに、衝突が発生した場合は、アプリケーションサーバー(プロバイダーOnUpdateErrorイベント)、クライアント(TClientDataSet OnReconcileErrorイベント)でプログラム的に対応するか、ユーザーに適切な競合解決を依頼することもできます(新しいアイテムリポジトリのReconcileErrorDialogを参照してください)。 。

それまでの間、IMHOは、ロックリスト、クライアントリスト、クライアントリストごとのロック、キープアライブメッセージ、堅牢なアプリケーションサーバーの障害回復、および考えられるすべての不具合を維持するために必要な複雑さを回避し、よりクリーンで優れたソリューションを実現します。

于 2011-03-10T23:01:56.073 に答える
3

コメントで提起された質問に答えるために、jachguate のOptimistic Concurrency Controlの回答に基づいて構築しています。

実装が簡単なため、できる限り OCC を使用することを好みます。オブジェクト永続化フレームワークを使用した 3 層アプリについて説明します。私の推奨スキームには 3 つのレベルがあります。

  1. 各オブジェクトに一意のバージョン ID が格納される行またはオブジェクト レベルのコントロール。オブジェクトを更新しようとすると、バージョン ID が自動的に変更されます。バージョン ID が既存のものと一致しない場合、更新は失敗します。

  2. フィールドまたは列レベルのロック。元のオブジェクトと更新されたオブジェクトの完全なコピーを送信します。更新の各フィールドには、新しい値が適用される前に比較された実際の値と古い値があります。競合を破棄するのではなく、競合を解決するようにユーザーに依頼することは可能ですが、コミット内のデータ量が増加するにつれて、これは面倒になります。

  3. 悲観的ロック。各オブジェクトには通常 null のロック所有者がいます (オブジェクトはロックされていません)。オブジェクトを編集したいときは、まずオブジェクトをロックします。ここでの問題は、ロックを整理する必要があり、その周りのビジネス ルールが醜い (どのようなタイムアウトが望ましいか) ことです。

この利点は、ほとんどの場合、低コストの OCC パスが使用されることです。頻繁に発生するが競合が少ない場合、メリットは非常に大きいです。倉庫での製品追跡を考えてみてください。製品は常に移動しますが、同一のアイテムが同時に移動することはほとんどなく、移動した場合の解決は簡単です (残りの数量 = オリジナルから私の削除とお客様の削除を差し引いたもの)。(たとえば)製品が移動される複雑なケースでは、輸送中に製品をロックすることはおそらく理にかなっています(それは物理的な状況を反映しているためです)。

ロックにフォールバックする必要がある場合は、両方のユーザーに通知し、通信チャネルを持つことができると便利なことがよくあります。少なくとも、ロックが利用可能になったときにロックを希望するユーザーに通知し、できればロック所有者にメッセージを送信できるようにし、場合によってはロックを強制できるようにすることもできます。次に、ロックの敗者に「Jo Smith がロックを取得したため、変更が失われました」と通知します。オフィスの政治にそれを整理させてください:)

私は通常、バグ レポートではなく、ユーザーの苦情によってフォールバック プロセスを推進します。特定のプロセスで頻繁に編集内容が失われるとユーザーが不満を持っている場合は、それを変更してください。レコードが頻繁にロックされるとユーザーが不満を言う場合は、オブジェクト マッピングをリファクタリングしてロックの粒度を高めるか、ビジネス プロセスを変更する必要があります。

于 2011-03-11T00:27:04.020 に答える
-1

jachgateによって提供されるアプローチは優れており、おそらくより優れていますが、これを実装したい場合TThreadListは、サービスの開始時に作成されるサーバー上に が必要になります。TThreadListスレッドセーフであるため、を使用してください。リストをナビゲートする際のパフォーマンスへの影響を最小限に抑えることができるTThreadListように、テーブルごとに設定できます。ロックされているものを制御するには、作成されてリストに渡されるオブジェクトが必要です

  TLockedItem = class(TObject)
  public
    iPK: Integer;
    iClientID: Integer;
  end;

実際のロックを行うには、次のようなものが必要です。

function LockItem(pPK, pClientID: Integer): Boolean;
var
  oLockedItem: TLockedItem;
  oInternalList: TList;
  iCont: Integer;
  bExists: Boolean;
begin
  bExists := False;
  if (Assigned(oLockedList)) then
  begin
    oInternalList := oLockedList.LockList;
    try
      if (oInternalList.Count > 0) then
      begin
        iCont := 0;
        while ((not bExists) and (iCont < oInternalList.Count)) do
        begin
          oLockedItem := TLockedItem(oInternalList[iCont]);
          if (oLockedItem.iPK = pPk) then
            bExists := True
          else
            Inc(iCont);
        end;
      end;
    finally
      oLockedList.UnlockList;
    end;
    if (not bExists) then
    begin
      oLockedItem := TLockedItem.Create;
      oLockedItem.iPK := pPK;
      oLockedItem.iClientID := pClientID;
      oInternalList := oLockedList.LockList;
      try
        oInternalList.Add(oLockedItem);
      finally
        oLockedList.UnlockList;
      end;
    end;
  end;
  Result := bExists;
end;

それはあなたが必要とするものの単なるアイデアです。同様のロジックでロック解除メソッドを実行する必要があります。接続が失われた場合に備えて、各クライアントが保持する各 TLockItem のポイントを保持するクライアントのリストが必要になるでしょう。これは決定的な答えではなく、このアプローチを実装したい場合に備えて、方向性を押し付けるだけです。
幸運を

于 2011-03-10T23:27:13.997 に答える