3

いくつかのタスクを一緒に実行する最善の方法は何ですか?1 つのタスクが失敗した場合、次のタスクは完了できませんか? それがデータベース操作であった場合、トランザクションを使用する必要があったことはわかっていますが、次のようなさまざまな種類の操作について話しています。

すべてのタスクに合格する必要があります。

SendEmail ArchiveReportsInDatabase CreateAFile

上記のシナリオでは、すべてのタスクがパスするか、バッチ操作全体がロールバックされる必要があります。

4

10 に答える 10

4

ロールバックは大変です - 私の知る限り、実際には 2 つの方法しかありません。2 フェーズ コミット プロトコル、または補正トランザクションのいずれか。これらの方法のいずれかでタスクを構成する方法を見つける必要があります。

通常、より良いアイデアは、他の人々の努力を利用し、既に 2PC または補償が組み込まれているテクノロジを使用することです。これが、RDBMS が非常に人気がある理由の 1 つです。

したがって、詳細はタスクに依存します...しかし、パターンはかなり簡単です:

class Compensator {
   Action Action { get; set; }
   Action Compensate { get; set; }
}

Queue<Compensator> actions = new Queue<Compensator>(new Compensator[] { 
   new Compensator(SendEmail, UndoSendEmail),
   new Compensator(ArchiveReportsInDatabase, UndoArchiveReportsInDatabase),
   new Compensator(CreateAFile, UndoCreateAFile)
});

Queue<Compensator> doneActions = new Queue<Compensator>();
while (var c = actions.Dequeue() != null) {
   try {
      c.Action();
      doneActions.Add(c);
   } catch {
      try {
        doneActions.Each(d => d.Compensate());
      } catch (EXception ex) {
        throw new OhCrapException("Couldn't rollback", doneActions, ex);
      }
      throw;
   }
}

もちろん、特定のタスクについては、運がいいかもしれません。

  • 明らかに、RDBMS の作業は既にトランザクションにラップできます。
  • Vista または Server 2008 を使用している場合は、トランザクション NTFSで CreateFile シナリオをカバーできます。
  • 電子メールは少しトリッキーです - 私は2PCや Compensator については知りません (ただし、誰かが Exchange にあると指摘しても少し驚くだけです) 。サブスクライバーがそれを受け取り、最終的にメールで送信します。その時点で、トランザクションはメッセージをキューに送信するだけで済みますが、おそらくそれで十分です。

これらはすべてSystem.Transactionsトランザクションに参加できるので、かなり良い状態になっているはずです。

于 2008-10-03T20:53:36.817 に答える
2

C#で

return SendEmail() && ArchiveResportsInDatabase() && CreateAFile();

于 2008-10-03T19:43:37.423 に答える
1

別のアイデア:

try {
    task1();
    task2();
    task3();
    ...
    taskN();
}
catch (TaskFailureException e) {
    dealWith(e);
}
于 2008-10-03T19:52:17.767 に答える
1

いくつかの提案:

分散シナリオでは、ある種の 2 フェーズ コミット プロトコルが必要になる場合があります。基本的に、すべての参加者に「X を行う準備をしてください」というメッセージを送信します。次に、各参加者は、「OK、X を実行できることを保証します」または「いいえ、実行できません」という応答を送信する必要があります。すべての参加者が完了することを保証する場合は、完了するようにメッセージを送信します。「保証」は、必要に応じて厳密にすることができます。

もう 1 つの方法は、操作ごとに何らかの元に戻すメカニズムを提供し、次のようなロジックを用意することです。

try:
    SendEmail()
    try:
        ArchiveReportsInDatabase()
        try:
             CreateAFile()
        except:
            UndoArchiveReportsInDatabase()
            raise
    except:
        UndoSendEmail()
        raise
except:
    // handle failure

(コードをそのようにしたくないでしょう。これは、ロジックがどのように流れるかを示す単なる例です。)

于 2008-10-03T19:53:56.903 に答える
0

それを実際に正しく行うには、非同期メッセージングパターンを使用する必要があります。nServiceBusとMSMQを使用してこれを行ったプロジェクトを終了しました。

基本的に、各ステップはキューにメッセージを送信することによって行われます。nServiceBusは、キューで待機しているメッセージを見つけると、そのメッセージタイプに対応するHandleメソッドを呼び出します。このようにして、個々のステップは個別に失敗および再試行可能です。1つのステップが失敗すると、メッセージはエラーキューに入れられるため、後で簡単に再試行できます。

提案されているこれらの純粋なコードソリューションは、ステップが失敗した場合、将来その1つのステップだけを再試行する方法がなく、場合によっては不可能なロールバックコードを実装する必要があるため、それほど堅牢ではありません。

于 2008-10-03T20:14:20.467 に答える
0

お使いの言語で許可されている場合、これは非常に整然としています。

  1. タスクをコード ブロックまたは関数ポインターの配列に配置します。
  2. 配列を反復処理します。
  3. いずれかのブロックが失敗を返すと中断します。
于 2008-10-03T19:46:31.427 に答える
0

使用しているプログラミング言語/環境については言及していません。.NET Framework の場合は、この記事を参照してください。Microsoft の Robotics Studio の Concurrency and Control Runtime について説明しています。これにより、一連の (非同期) イベントにあらゆる種類のルールを適用できます。など。複数のスレッドで実行することもできるため、非常に強力な方法で実行できます。

于 2008-10-03T19:52:26.563 に答える
0

環境を指定しません。Unix シェル スクリプトでは、&& 演算子がまさにこれを行います。

SendEmail () {
  # ...
}
ArchiveReportsInDatabase () {
  # ...
}
CreateAFile () {
  # ...
}

SendEmail && ArchiveReportsInDatabase && CreateAFile
于 2008-10-03T19:52:43.817 に答える
0

ソート回路評価を使用する言語を使用している場合(Java と C# はそうです)、次のように簡単に実行できます。

return SendEmail() && ArchiveResportsInDatabase() && CreateAFile();

これは、すべての関数が true を返す場合に true を返し、最初の関数が false を返すとすぐに停止します。

于 2008-10-03T19:57:49.580 に答える
0

例外は一般的に、この種のことには適しています。疑似 Java/JavaScript/C++ コード:

try {
    if (!SendEmail()) {
        throw "Could not send e-mail";
    }

    if (!ArchiveReportsInDatabase()) {
        throw "Could not archive reports in database";
    }

    if (!CreateAFile()) {
        throw "Could not create file";
    }

    ...

} catch (Exception) {
    LogError(Exception);
    ...
}

メソッド自体が例外をスローする場合はなおさらです。

try {
    SendEmail();
    ArchiveReportsInDatabase();
    CreateAFile();
    ...

} catch (Exception) {
    LogError(Exception);
    ...
}

このスタイルの非常に優れた結果は、タスク チェーンを下に移動するときにコードがますますインデントされないことです。すべてのメソッド呼び出しは同じインデント レベルのままです。インデントが多すぎると、コードが読みにくくなります。

さらに、エラー処理、ロギング、ロールバックなどのためのコードに単一のポイントがあります。

于 2008-10-03T20:00:31.180 に答える