Salesforceのフローで取引先を更新したタイミングに、ユーザーへの権限セット割り当ても自動化しようとすると、MIXED_DML_OPERATIONが発生することがあります。
エラー文には「setup object」と「non-setup object」が表示されますが、フローを分ければよいのか、非同期パスへ移す必要があるのかが少し分かりにくいところです。
今回は、取引先とPermissionSetAssignmentを同じレコードトリガーフローで処理し、即時実行パスでの失敗と、非同期パスへ移した後の結果を比較します。さらに、非同期側だけを意図的に失敗させ、元の取引先更新が残るかも確認します。
MIXED_DML_OPERATIONは、設定オブジェクトと通常オブジェクトのDMLを同じトランザクションで実行したときに発生します。レコードトリガーフローでは、設定オブジェクト側の処理を非同期パスへ移し、元の保存処理とトランザクションを分けるのが回避方法の一つです。
今回の検証では、即時実行パスでMIXED_DML_OPERATIONが発生し、取引先の更新もロールバックされました。一方、PermissionSetAssignmentの作成を非同期パスへ移すと、取引先は正常に保存され、数秒後に権限セットが割り当てられました。
MIXED_DML_OPERATIONが発生する理由
Salesforceでは、ユーザーのアクセス権に関わる一部のオブジェクトを「設定オブジェクト」として扱います。設定オブジェクトと、取引先などの通常オブジェクトに対するDMLは、原則として同じトランザクション内で混在できません。
たとえば、取引先の保存処理と同じトランザクションで権限セットを割り当てると、アクセス権が変わる途中の状態で通常データへ変更を加えることになります。Salesforceはセキュリティ上の整合性を保つため、この組み合わせを制限しています。
| 分類 | 今回使うオブジェクト | ほかの例 |
|---|---|---|
| 設定オブジェクト | PermissionSetAssignment | User、GroupMember、UserRoleなど |
| 通常オブジェクト | Account(取引先) | Contact、Opportunity、Caseなど |
Userは操作内容や更新項目によって例外があるため、今回はMixed DMLを再現しやすいPermissionSetAssignmentの作成を使います。対象となる設定オブジェクトと例外条件は、Salesforce開発者ガイドの「混在DML操作」で確認できます。
ボンボンフローを別ファイルにしただけでは、同じトランザクションのままかもしれないんだね。
FlowとApexが同じレコードを更新したときの実行順序や再実行については、次の記事でデバッグログを使って確認しています。


検証環境と今回作るフロー
検証では、取引先のチェックボックスをオンにしたとき、指定したユーザーへ検証用の権限セットを割り当てます。ユーザーIDや権限セットIDをフローへ直接書かず、取引先のユーザー参照項目と権限セットのAPI参照名から取得する構成にします。
| 検証日 | 2026年7月26日 |
|---|---|
| 組織種別 | Developer Edition |
| Salesforceリリース | Summer ’26 |
| APIバージョン | 67.0 |
| 通常オブジェクト | 取引先(Account) |
| 設定オブジェクト | 権限セット割り当て(PermissionSetAssignment) |
検証用の項目・ユーザー・権限セットを準備する
取引先に次のカスタム項目を用意します。
- 「Mixed DML検証」:チェックボックス
- 「検証対象ユーザー」:ユーザーを参照するルックアップ項目


続いて、検証専用の権限セットを作成します。権限を追加しない空の権限セットでも、割り当てレコードの作成確認には使えます。


レコードトリガーフローの開始条件を設定する
取引先を対象に、更新後に動くレコードトリガーフローを作成します。開始条件は次のとおりです。
- オブジェクト:取引先
- フローをトリガーする条件:レコードが更新された
- 条件1:「Mixed DML検証」が
True - 条件2:「検証対象ユーザー」が
Null = False - 更新されたレコードでフローを実行するタイミング:条件の要件を満たすように更新されたときのみ
- フローを最適化:アクションと関連レコード
非同期パスを追加するには、開始条件の設定がポイントです。非同期パスの選択肢が表示されない場合は、「条件の要件を満たすように更新されたときのみ」になっているか、または開始条件でIsChangedを使っているかを確認します。


即時実行パスでMIXED_DML_OPERATIONを再現する
1.権限セットを取得する
「レコードを取得」要素でPermissionSetを検索します。条件には検証用権限セットのAPI参照名を指定し、最初の1件だけを保存します。
2.権限セット割り当てを作成する
即時実行パスに「レコードを作成」要素を置き、PermissionSetAssignmentへ次の値を設定します。
| 項目 | 設定する値 |
|---|---|
AssigneeId | トリガー元の取引先に設定した「検証対象ユーザー」 |
PermissionSetId | 「レコードを取得」で取得した検証用権限セットのID |


3.取引先を更新してエラーとロールバックを確認する
検証対象ユーザーを選択し、「Mixed DML検証」をオンにして取引先を保存します。即時実行パスは取引先の保存処理と同じトランザクションで動くため、ここでMIXED_DML_OPERATIONが以下の通り発生します。


エラー画面だけでなく、次の2点も確認します。
- 取引先の「Mixed DML検証」がオフのままで、更新がロールバックされたこと


- 検証対象ユーザーへ権限セットが割り当てられていないこと


非同期パスへ移してMixed DMLを回避する
次に、権限セットの取得とPermissionSetAssignmentの作成を、即時実行パスから非同期パスへ移します。非同期パスは元の取引先更新が正常にコミットされた後にキューへ登録されるため、通常オブジェクトと設定オブジェクトのDMLを別トランザクションに分けられます。
非同期パスの仕組みと開始条件は、Salesforce公式のWinter ’22 Flow機能紹介でも説明されています。
1.開始要素に非同期パスを追加する
開始要素の「非同期に実行するパスを含める」を有効にします。
2.設定オブジェクトの処理を非同期側へ移す
権限セットの取得と権限セット割り当ての作成を、非同期パスへ接続します。即時実行パス側にはPermissionSetAssignmentのDMLを残しません。


3.取引先の保存と権限セット割り当てを確認する
フローを有効化し、再度「Mixed DML検証」をオンにして取引先を保存します。保存が正常終了することが確認できます。


ユーザーの権限セット割り当てを再読み込みします。ほんの数秒で反映されることが確認できます。


非同期側だけを失敗させて別トランザクションか確認する
非同期パスへ移してエラーが消えただけでは、元の取引先更新との違いが見えにくいため、非同期処理だけを意図的に失敗させます。
一度割り当てに成功したユーザーと権限セットの組み合わせを使い、取引先のチェックをオフに戻して保存してから、再びオンにしました。フローが同じPermissionSetAssignmentをもう一度作成しようとすると、重複割り当てとして非同期側だけが失敗しました。
- 取引先の「Mixed DML検証」はオンで保存されているか


- 権限セット割り当てが重複していないか


- 失敗したフローインタビューまたはエラーメールを確認できるか


非同期側が失敗しても、すでにコミットされた取引先の更新は原則として元に戻りません。実運用では、フォールトパス、通知、再実行の方法まで含めて設計します。
即時実行パスと非同期パスの結果を比較する
3つのケースを同じ表で比較します。
| ケース | 取引先の保存 | 権限セット割り当て | 確認結果 |
|---|---|---|---|
| 即時実行 | 失敗 | 失敗 | 両方がロールバックされる |
| 非同期実行 | 成功 | 成功 | 保存後に割り当てられる |
| 非同期側を失敗 | 成功 | 失敗 | 取引先更新だけが残る |


比較結果から、非同期パスによって取引先の更新と権限セットの割り当てが別々に処理されたと判断できます。重複割り当てで非同期側だけを失敗させたケースでも、取引先のチェックはオンのまま残り、権限セットは重複して作成されませんでした。
非同期パスが別トランザクションで動くことを確認する
即時実行パスでは、取引先の保存処理とPermissionSetAssignmentの作成が同じトランザクションで実行され、Mixed DMLによって両方がロールバックされました。非同期パスへ移した後は、取引先の保存が完了した後に権限セットが割り当てられています。
さらに、重複割り当てで非同期側だけを失敗させても、すでに保存された取引先の更新は残りました。この結果からも、非同期パスの処理が元の保存処理とは別のトランザクションで動いていることを確認できます。
ログでSOQLやDMLの積み上がりを追う方法は、次の記事でも詳しくまとめています。


非同期パスを使うときの注意点
保存直後に処理が完了するとは限らない
非同期パスは元のトランザクションがコミットされた後にキューへ登録されます。そのため、取引先を保存した直後に権限セットの割り当て結果が必要な処理には向きません。
非同期側の失敗を元の画面へ返せない
元の保存処理が完了した後に動くため、非同期側のエラーを取引先の保存画面へそのまま表示することはできません。フォールトパスで管理者へ通知する、失敗を記録するオブジェクトを用意するなど、後から気づける仕組みが必要です。
重複割り当てと再実行を考慮する
権限セットを割り当てる前に、同じユーザーと権限セットのPermissionSetAssignmentが存在しないかを確認します。開始条件も「条件を満たすたび」ではなく「条件を満たすように更新されたとき」に絞ると、意図しない再実行を減らせます。
大量更新時は非同期処理の滞留も確認する
大量の取引先を一括更新すると、非同期処理もまとめて発生します。1件の検証で成功しても、大量データ処理で同じ時間内に完了するとは限らないため、実運用の件数に近い条件でも確認します。
非同期処理の実行タイミングを調べるときは、スケジュールトリガーフローのタイムゾーン・実行ユーザー・ログを確認した記事も参考になります。


非同期パス以外の回避方法
非同期パスが使えないフロー種別や、より明確な連携・再処理が必要な場合は、次の方法も候補になります。
- スケジュール済みパスで、元の保存処理から実行を分ける
- Platform Eventを発行し、別のフローで設定オブジェクトを処理する
- ApexのQueueableまたはfutureメソッドを使う
- 画面フローでトランザクション境界を明示的に設ける
Platform Eventを使った構成は、Salesforce Admin公式のMixed DMLの解消例で紹介されています。今回のようにレコードトリガーフロー内で完結する小さな処理なら、まず非同期パスで要件を満たせるか確認すると構成を抑えられます。
まとめ
MIXED_DML_OPERATIONは、フロー固有のエラーではなく、設定オブジェクトと通常オブジェクトを同じトランザクションで更新したことが原因です。処理を別フローやサブフローへ分けるだけでは、トランザクションが同じままなら解消しません。
レコードトリガーフローでは、権限セット割り当てなど設定オブジェクト側の処理を非同期パスへ移すことで、元の保存処理と分離できます。ただし、非同期側の失敗で元のレコード更新は戻らないため、エラー通知と重複防止までセットで設計したいところです。
今回試した範囲では、設定オブジェクト側の処理を非同期パスへ移すことでMixed DMLを回避できました。同じエラーに遭遇したときは、フローを分けることだけを考えるのではなく、通常オブジェクトと設定オブジェクトのDMLが同じトランザクションに含まれていないかを先に確認すると切り分けやすそうです。



コメント