Backport(バックポート) #2927
完了プラグイン無効時にデータ削除を伴う操作でエラーやデータ不整合が発生する
100%
説明
Overview (現象)¶
プラグイン無効時に退会などのデータ削除を伴う処理がエラーとなり操作できない場合がある。
例:
- opCommunityTopicPlugin 無効時、退会処理を行うと500エラーとなり退会できない
- opCommunityTopicPlugin 無効時、コミュニティ削除処理を行うと500エラーとなり削除できない
- opCommunityTopicPlugin 無効時、画像削除を行うと500エラーとなり削除できない(プロフィール画像や日記画像などコミュニティトピックと関係のない画像であってもエラーになる)
バンドルプラグインでは opCommunityTopicPlugin だけであるが、schema.yml で actAs を使ってプラグイン内のクラスを指定している場合、同様の問題が発生する。
また、プラグイン無効時にはエラーとはならないがデータ不整合が発生し、再有効化後に問題が発生する場合がある。
例:
- opIntroFriendPlugin 無効化中に紹介文の存在するフレンド関係を削除し、再度 opIntroFriendPlugin を有効化すると、フレンドではないメンバーの紹介文が削除されておらず表示されてしまう
Causes (原因)¶
Doctrine により自動生成されるモデルはプラグインの有効・無効を考慮しないため、無効なプラグインのモデルを利用するコードが生成される。例として relation 関係のあるモデルの削除処理の過程で利用される(削除以外の処理で利用されるかどうかの網羅的な調査はできていない)。これにより単純な onDelete: cascade などのデータ整合性を保つための処理がプラグイン無効時にも動作するというメリット(?)はある。
一方、無効なプラグイン内のファイルに含まれるクラスライブラリはオートロードの対象から外れるため、それを呼び出すコードを実行すると Fatal Error となり PHP の実行は停止され、クライアントへは結果として 500 エラーが返される。
opCommunityPlugin のように schema.yml 内で actAs にプラグイン内のクラスが指定されている場合、これに該当し関連するモデル(この例では Member, Community, File)の削除処理において Fatal Error が発生する。
また、プラグイン内でイベントハンドラによりコア側や他プラグインの動作タイミングでデータ更新をしている場合には、プラグイン無効時にイベントハンドラが呼び出されないため、データの不整合が発生する。opIntroFriendPlugin はフレンド削除のタイミングで、op_action.post_execute_friend_unlink イベントを利用し(onDelete: cascade のようなDB上の制約を使わずに)データ削除を行なっているため、このケースに該当する。
Way to fix (修正内容)¶
プラグインの有効・無効の切り替え機能を完全に機能させる前提での修正が難しいため、管理者がそのことを理解した上でこの機能を利用できるように画面上に注意文を追加する対応とする。
機能自体を削除することも検討したが、問題なく有効・無効を切り替えられるプラグインも多いため、安定版での機能削除は不適当と判断した。削除するのであれば別途 Enhancement チケットを作成して開発版のみで対応すべきと考える。
補足として、プラグインの有効・無効の切り替え機能を完全に機能させることが難しい理由を以下に記す。
プラグインを 有効 → 無効 → 有効 と切り替えた際に正常な動作を保証するのが困難で、無効な状態の際にデータの更新処理をどのようにおこなうか(おこなわないか)が課題となる。
データの整合性を保つためには無効なプラグインに関するデータの更新処理をおこなう必要があり、そのためにはプラグイン下のライブラリやDB上のテーブル間の制約を動作させる必要がある。ただし、データの整合性を保つための機能だけを動作させるというのはコア側から一定のルールで切り分けができるものではなく、必要以上に動作してしまう部分が出てくる懸念があるため、各プラグインでもなんらかの実装を合わせてする必要がある可能性が高い。例えば、プラグイン内のイベントハンドラによる処理などは基本的にどんな処理でも書ける仕様であるため、コア側で無効時にどの処理が必要かを判別する術がない。逆にコア側のオートロードの仕組みを変えない限りはプラグイン側だけで解決できる問題でもない。なんとか制御する仕組みが作れたとしてもコア、プラグイン共に変更は大規模となりプラグイン側の後方互換性を損なう恐れもある。
データの整合性を諦めて無効なプラグインのデータの更新処理はおこなわない方針を目指したとしても、少なくとも Doctrine のモデル、DB テーブルの外部キー制約を改変する必要がありこれだけでも簡単ではない。特に外部キーは再有効化時にデータ不整合で戻せない可能性もある。そもそもこの方針が上手く行ったところで再有効化時に正常動作する保証はないため、プラグインの有効・無効切り替えが正常動作しているとは言い難い。
参考情報¶
http://sns.openpne.jp/communityTopic/8139 より転記
(1) OpenPNEのバージョン OpenPNE 3.6 (2) サーバ情報 CentOS 5.7 PHP 5.3.3 MySQL 5.0 (3) 現象の詳細 opCommunityTopicPlugin を無効にすると退会処理でエラー。 ( opCommunityTopicPlugin 1.0.2.2 ) 再現手順: 1.opCommunityTopicPlugin を無効にする。 2.管理画面から強制退会処理 or ユーザが退会処理を行う => 500エラー発生。 発生個所は、 /root/lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/lib/vendor/doctrine/Doctrine/Relation/Parser.php メソッド: getRelations() line: 250 再現しない場合、複合的な要因かもしれません。 とりあえず opCommunityTopicPlugin を有効にしておくことで回避できますが、参考までのご報告です。
Rimpei Ogawa さんがほぼ12年前に更新
- 題名 を opCommunityTopicPlugin無効後、退会処理を行うとエラーになる から プラグイン無効時にデータ削除を伴う操作でエラーやデータ不整合が発生する に変更
- 説明 を更新 (差分)
- ステータス を New(新規) から Accepted(着手) に変更
Rimpei Ogawa さんがほぼ12年前に更新
- ステータス を Accepted(着手) から Pending Review(レビュー待ち) に変更
- 進捗率 を 0 から 50 に変更
更新履歴 626db456d93b6efc7e657a4ee084e4ef8b0dcf20 で適用されました。
Kousuke Ebihara さんがほぼ12年前に更新
- ステータス を Pending Review(レビュー待ち) から Pending Testing(テスト待ち) に変更
- 進捗率 を 50 から 70 に変更