質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.64%

プログラムの例外処理

解決済

回答 3

投稿

  • 評価
  • クリップ 2
  • VIEW 848

annderber

score 93

お世話になります。

現在php5.6であるアプリケーションを作成していますが、
そこのある処理で、確定ボタンを押したら、入力されたユーザー情報(仮に)をデータベース(DBサーバーはローカル)に登録して、その後4つの外部サーバーにftp接続してcsvファイルをはき出すという処理を同時に行っている箇所があります。

このようないくつかの処理を同時に行っている場合で、どこかのタイミングでエラーが発生した場合、すべてロールバックする例外をthrowすべきでしょうか。
ただ、ftp接続して、ファイルをアップロードした場合、途中でエラーが起きてもロールバックでアップロードしたことを戻すのは難しいと思います。
db処理、2つの外部サーバーにcsv吐き出しまで成功して3つ目のftpサーバーに接続できなかったといった様な場合はできるところまでやって、csv吐き出しが行えなかったことをエラーメッセージを表示すると言ったような処理もアリでしょうか。

またはそもそもこのような処理を設計すること自体間違いということもありますでしょうか。

当然要件によると思いますが、皆様のご経験上このような要件でどのような設計を行うか教えてください。
よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+7

難問ですね。

そこのある処理で、確定ボタンを押したら、入力されたユーザー情報(仮に)をデータベース(DBサーバーはローカル)に登録して、その後4つの外部サーバーにftp接続してcsvファイルをはき出すという処理を同時に行っている箇所があります。

確認すべきなのは、「確定ボタン」を押した時の「入力されたユーザー情報」は正しいものかどうかということです。

「DBへの保存」「CSVファイル作成」
どっちが先でも構いませんが、「入力されたユーザー情報」が正しければ処理を継続出来るよう頑張る方が健全です。
例えばCSVファイル作成がメモリ不足で落ちたり、FTP接続に失敗したからといってDBへ保存したものを削除するのは正しいシステムの動作とも思えません。

私ならこうするという方法は「処理を分けてバッチで動かす」です。
ユーザーからの入力値を受け付けて、情報が正しければ即刻DBへ保存します。
それと同時に、「保存した入力値をCSVファイルに加工してFTPで別の場所へ送信したか? Yes or No」にレコードを挿入します。

Cronで何分かに一度この「Yes or No」テーブルに問い合わせるバッチを作成します。
後はこのようなフローで解決できるでしょう。

  • Yes or Noテーブルに問い合わせる
  • 未送信のユーザー情報を取り出す
  • CSVファイル作成する
  • FTP送信を試みる
  • 上記の手順が全て完了したら「Yes or No」テーブルの完了済みにチェックを入れる

細部は色々とアレンジ出来ると思います。
まぁ、AWSのS3に保存すると○○のバッチが走ってSQSに行って、DynamoDBに振り分けて分析して…みたいなのは全てさっき挙げた設計思想で動いています。
S3だったりDynamoDBに保存した瞬間、AWSのシステムが発火して登録しているバッチが動作して値の加工→別の場所に保存を行ってくれます。
その結果、サーバを使わず値をうまく加工して別の場所へ保存を繰り返すシステムが構築出来るのです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/02/27 11:15 編集

    コメントありがとうございます。

    おおーこの設計はかなり良さそうな感じがします。
    お恥ずかしながら普段あまりバッチ処理を使うことがないので、こういった発想が出なかったです。
    実装はするのは簡単そうではないですが

    キャンセル

  • 2018/02/27 11:22

    phpのソースコードは、`php [ファイル名]`コマンドで手動で動かす事が出来ます。
    なので、PHPが既にある程度触れるなら、やってみれば意外と楽勝だと思います。
    $_GETみたいな一部のスーパーグローバル変数は使えませんけどね。

    なお、フレームワークのモデルとかを活用したいケースはあると思います。
    その場合でもCakePHP等のフレームワークはShellモードというコマンドラインから実行してもモデルやヘルパー等の機能を利用出来る手段が搭載されています。
    開発がめっちゃ楽になるので確認してください。

    キャンセル

  • 2018/02/27 11:58

    これって、外部のサーバーとかが絡む処理は全部バッチ処理で切り出した方がいいっていうのは短絡的でしょうか?

    例えば、例に挙げた処理に更にメール送信処理が追加されるとなって、外部のsmtpサーバー経由でメールを送信する場合、何らかの障害で外部のsmtpサーバーに接続できないなどの問題が発生する可能性があると思うので、同じようなバッチ処理で対応すればいい気がします。実際にメールが送れたかどうかまでは判断できないですけど。

    キャンセル

  • 2018/02/27 12:13

    そうですね、やることが増えてもカラム1個増やすだけで対応できたりするので楽ですね。
    SQL叩けば何の処理サボってるか一目瞭然ですしね。

    メール送信はpostfixで詰まったりすることもありますが、GmailのAPIなら申請が通過すればほぼ間違いなく相手に届くはず(相手のメールアドレスが生きていれば)なので信頼性を担保してくれる別の何かを利用しても良いかもしれないですね。

    必要なのはこんな感じですね。
    ・ステートレスに私は何の条件で発火するんだっけと確認出来ること
    ・処理は何なのか記載していること
    ・完了したという情報はどうやって持たせるのか記述しておく
    ・2重実行した時に困らないように対策しておく

    最後の項目は「未処理・完了」の他に「実行中」というステータスを持たせるという方法があります。
    例えばSQLで問い合わせて未処理のモノを見つけたら即座に実行中にアップデートするSQLを発行するとか…
    `update hoge set state = '実行中' where id = 1234 and state = '未処理'`みたいな感じにすれば、1行更新出来た事をトリガーに出来ます。
    ごく僅かな時間差で2重起動しても、勝手に2回処理が走る事はありません。

    キャンセル

+4

DB登録とftp処理はさすがに相性悪そうですね
DB登録時の失敗はロールバックするまでもなくデータ投入されないし
ftpに失敗した場合、DBからデータ削除する処理を入れればよいのでは?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/02/27 11:20

    コメントありがとうございます。

    そもそも同時に行うのがよくないという感じですね

    キャンセル

+4

PHPではなくJavaの案件でしたが、
web画面からのトランザクション内で
いくつかの対向先システムに連携が必要なシステムはありました。

画面からリアルタイムで確定が成功したか失敗したかの確認が必要な案件であれば、
そのような設計になると思います。
(非同期にして数秒後に、結果を画面にホップアップという手もありますが、、)

対向先がロールバック(またはロールバック相当のこと)できるかどうかによります。
ftpだと、ファイルを削除するとか後続の処理が止めれるかどうかといったところでしょうか。

ロールバックできない場合には
DBにどこまで実行できたかの状態を登録しておきます。
PHPの処理中にエラーで落ちるなんてことも考慮が必要なので、
別トランザクションにするか、都度コミットするか検討が必要です。
その後、自動(または手動)で残りの対向先に処理を進め、
全て成功するまで運用対象になります。

どの対向先まではロールバックで、どの対向先に連携したら必ず成功させる必要があるなど
業務的な要件も絡んできます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/02/27 11:19

    コメントありがとうございます。

    やはりどこまで厳密にやるかの要件でも変わってきますよね。
    「ロールバックできない場合にはDBにどこまで実行できたかの状態を登録しておきます。」・・・
    の部分はバッチ処理でやるイメージでしょうか?

    キャンセル

  • 2018/02/27 11:47

    それも要件によりけりなのですが、
    最初の状態のみ確定ボタンから行い、
    最初の状態のレコードを処理するバッチを作れば、
    更新はバッチで行うことになります。

    それとは別に画面からのトランザクションで行う場合は、
    確定ボタン~ftpサーバA接続前に1回目のコミット、
    ftpサーバAへの転送完了後に2回目のコミット、
    ftpサーバBへの転送完了後に3回目のコミット、
    ・・・・
    と続けていけば、画面からのトランザクションで
    状態の更新を行うことになります。

    キャンセル

  • 2018/02/27 11:50

    なるほどです。
    ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.64%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる