クライアントとサーバー間のマルチステップアクションでエラーを処理するためのベストプラクティスは何ですか?

Aug 18 2020

APIに求人情報をアップロードするウェブサイトがあります。これを行うには複数の手順があります。

  1. ロゴ画像をファイルストレージにアップロードします。

  2. 求人情報に関するデータをデータベースに挿入します。

  3. サードパーティプロバイダーとの支払いを処理します。

  4. サードパーティのプロバイダーを介して電子メールを送信します。

一般に、サードパーティAPIからの情報の取得、ReCAPTCHAの検証、GoogleインデックスAPIの更新、SMSの送信など、さまざまなアプリケーションに他のステップが存在することを想像できます。

これらはすべてサードパーティを使用しており、API呼び出しを処理するサーバーから独立しているため、いずれかの手順を正常に完了したままにしたり、完了しなかったりする可能性があります(たとえば、ロゴは更新されますが、支払いは回収されません)。

私の質問は、クライアントとサーバー間のこの種のマルチステップアクションのエラーは、通常、本番システムでどのように処理されるのかということです。受け入れられている標準やベストプラクティスはありますか?


私は考慮しました:

  1. エラーを処理せず、すべてがエラーなしで完了することを期待しています。

  2. 各ステップのバックエンドで「元に戻す」関数を定義し、いずれかのステップが失敗した場合は、前のステップでそれを呼び出します。多くのステップで構成されるアクションでは、これは非常に迅速にスパゲッティコードに変わる可能性があり、一部のステップはそれほど簡単に元に戻すことができません(電子メールの送信など)。

  3. ステップごとにバックエンドに個別のエンドポイントを作成し、クライアントが各ステップを順番に呼び出せるようにします。これは「元に戻す」APIエンドポイントを使用することもできるため、クライアントがいずれかの手順でエラーを受け取った場合は、前のすべての手順を元に戻すことができます。これには、完了したアクションの進行状況をクライアントが推定できるという利点があります。つまり、「完了した5つのステップのうちの1つ」をユーザーに表示できます。

  4. 各アクションのDB(またはインメモリデータベース?)に行を作成し、各ステップが完了すると、対応する列に完了のマークを付けます。行のすべての列が完了すると、ユーザーに応答を送り返します。

回答

1 RalfKleberhoff Aug 19 2020 at 13:30

このマルチステッププロセスはトランザクションであり、1つの一貫した初期状態(「何も起こらなかった」)からある最終状態(「求人情報の投稿が完了しました」)に移行し、中間ステップによってアプリが(おそらく)一貫性のない状態になります。

トランザクション処理をクライアントに委任しないでください。特に、プロセスを個々の呼び出しに分割しないでください(または、支払いステップをスキップする可能性があります-ビジネスに悪影響を及ぼします)。

1つのステップが失敗した場合、続行するのは確かにナンセンスです。必ずしも初期状態ではなく、許容可能な状態に何らかのロールバックを実行する必要があります。たとえば、アップロードした画像をファイルストレージから削除する絶対的な必要性はないと思います。

まず、中間のステップのほとんどが受け入れられるようにステップを配置しようとするので、ロールバックする必要はありません。

トリッキーなステップは確かに支払いと電子メールです(私があなたのビジネスを正しく理解している場合)。

  • 電子メールを送信せずに顧客に請求するのは悪いことです(詐欺に近い)。
  • 支払いを受け取らずに電子メールを送信することは、お金を失うことを意味します(これが頻繁に起こらない場合、それは許容できるかもしれません)。電子メールが送信された後は、ロールバックすることはできませんが、支払いをロールバックすることはできます(その瞬間にプロバイダーへの接続が失われない限り)。

外部接続に依存しているため、トランザクションの部分的な完了を完全に回避する方法がわかりません。そのため、中間的な障害が発生するようにプロセスを設計します。

  • 可能性は非常に低い
  • または許容できる状態を残します。

だから、私は

  1. すべての実験サービスにpingを実行して、それらが現在稼働中で到達可能であることを確認します
  2. ロゴ画像をファイルストレージにアップロードします。
  3. 求人情報に関するデータをデータベースに挿入します。
  4. サードパーティのプロバイダーを介して電子メールを送信します。
  5. サードパーティプロバイダーとの支払いを処理します。

ロールバック手順は次のようになります

  • ファイルストレージから画像を削除し、
  • データベースから求人情報を削除し、
  • 電子メールが送信されたが支払いが失敗した場合:後で再試行するために支払い要求をスケジュールします。
JacquesB Aug 18 2020 at 21:56

「最良」は明らかに要件に依存します。番号1は明らかに実装が最も簡単ですが、エラーが発生した場合、トランザクションは失われるか不完全になります。たぶん、これはビジネスの観点から許容できるトレードオフですか?

最も堅牢なソリューションは、プロセスを一連のステップに分割することです。各ステップはトランザクションです。トランザクションは完了または失敗し、失敗した場合は安全に再試行できます。(たとえば、メールやSMSの送信はトランザクションになります。)データベースの行は、どのステップが完了したかを追跡します。

クライアントに各ステップを呼び出させるべきではないと思います。これにより、緊密な結合が作成され、複雑さが増します。クライアントに必要なすべてのデータを含む単一のリクエストを呼び出させるだけで、ワークフローが開始されます。その後、進行状況を表示する場合、クライアントはステータスをポーリングするための個別の定期的な要求を送信できます。

元に戻すのサポートはより複雑であり、(ご存知のとおり)常に可能であるとは限りません。プロセス全体が拒否されるような方法で一部のステップが失敗する可能性がある場合(クレジットカードが有効でなかった場合など)、マルチステッププロセスを開始する前に検証ステップで実行する必要があると思います。これにより、クライアントに同期フィードバックを提供し、クライアントにデータを確認して再入力させることができます。

AdrianL Aug 27 2020 at 01:00

他の人が述べたように、理想的には、求人情報の投稿を成功させるために必要なすべての要素をバンドルし、クライアントが1つのリクエストでそれをバックエンドに送信するようにします。私自身、モバイル開発者として、データ使用量とバッテリー寿命を維持するために、可能な限り最小限の作業をクライアントに推奨しています。

バックエンドに関しては、最初に最も重要な情報をデータベースに挿入してから、たとえばロゴ画像などの補足データを挿入してみることをお勧めします。