Comment corriger MIXED_DML_OPERATION dans les déploiements Salesforce

Apex tente de modifier un objet setup, comme User ou Group, et un objet non-setup dans la même transaction.

Se produit lors de : l'exécution Apex, le plus souvent pendant l'exécution des tests requise pour un déploiement

Ce que cela signifie

MIXED_DML_OPERATION est une exception à l'exécution Apex : Salesforce n'autorise pas le DML sur un objet setup (User, Group, GroupMember et similaires) et un objet non-setup (Account, Contact, objets personnalisés) au sein de la même transaction. Cela apparaît le plus souvent pendant l'exécution des tests Apex qu'un déploiement en production exige, se transformant en un échec de test qui bloque le déploiement.

Cette restriction existe car les objets setup peuvent modifier l'accès au niveau enregistrement et le partage, et Salesforce ne permet pas qu'une même transaction modifie à la fois qui peut voir les données et écrive ces données, ce qui explique pourquoi la correction concerne toujours les limites de transaction, jamais le contenu des enregistrements.

Diagnostic

Causes courantes

La configuration du test crée un utilisateur et des enregistrements business ensemble
Une méthode de test insère un User puis, dans la même transaction, insère un Account ou un autre enregistrement business sans isoler le DML.
Un trigger assigne une queue ou une appartenance à un groupe
Un trigger sur un objet business insère un GroupMember ou met à jour le partage pendant que d'autres enregistrements business sont enregistrés dans le même contexte.
Le DML de l'objet setup s'exécute en ligne au lieu d'être isolé
Le code qui devrait isoler le DML de l'objet setup dans sa propre transaction l'exécute à la place dans le même contexte que le DML de l'objet business.

La solution

  1. Déplacez le DML de l'objet setup dans sa propre transaction
    Enveloppez l'insertion du User ou du Group dans une méthode future, ou isolez-la autrement, afin qu'elle soit validée avant l'exécution du DML de l'objet business.
    @future
    private static void insertUserAsync(String jsonUser) {
        User u = (User) JSON.deserialize(jsonUser, User.class);
        insert u;
    }
  2. Utilisez Test.startTest() et Test.stopTest() pour scinder la transaction
    Dans les tests, placez le DML de l'objet setup avant Test.startTest() afin qu'il s'exécute dans sa propre limite de transaction avant le DML de l'objet business.
  3. Ne combinez jamais les insertions d'objets setup et business dans un même contexte de trigger
    Refactorisez l'automatisation pour que les changements d'objet setup, comme le provisioning d'utilisateurs ou l'appartenance à un groupe, ne partagent jamais une transaction avec le DML d'enregistrements business.
En pratique

Comment Serpent évite cela

Le pipeline CI de Serpent exécute la suite de tests Apex complète sur chaque tâche avant qu'elle ne soit éligible au merge, de sorte qu'un échec de test MIXED_DML_OPERATION apparaisse sur la tâche qui l'a introduit, et non sur une release en production. Voir la bibliothèque des erreurs de déploiement Salesforce.

Constructeur de pipelines CI/CD no-code dans Serpent

Prévention

Insérez toujours les Users de test dans @TestSetup, avant tout DML d'enregistrement business
Structurez les classes de test pour que la création d'objets setup se fasse dans une méthode @TestSetup dédiée, entièrement séparée de la logique métier testée.
Isolez les changements d'appartenance aux groupes et au partage dans leur propre méthode de service
Acheminez tout le DML de GroupMember et des enregistrements de partage via un unique utilitaire appelé de manière asynchrone, afin que la logique métier ne partage jamais accidentellement une transaction avec lui.
Signalez le DML de l'objet setup en revue de code
Traitez toute insertion ou mise à jour sur User, Group, ou GroupMember comme un signal de revue pour confirmer qu'elle est correctement isolée du DML de l'objet business dans la même méthode.
Questions fréquentes

MIXED_DML_OPERATION, les réponses

Pourquoi MIXED_DML_OPERATION échoue-t-il uniquement pendant le déploiement, pas en usage normal ?
Il échoue chaque fois que le chemin de code s'exécute, mais les déploiements en production exigent que tous les tests Apex réussissent, donc un test qui déclenche cette exception bloque toute la release même si le code sous-jacent exécute rarement ce chemin en production.
Quels objets comptent comme objets setup pour cette règle ?
User, Group, GroupMember, UserRole, et une poignée d'objets de partage et de permissions apparentés. La plupart des objets business et personnalisés ne sont pas concernés et peuvent se mélanger librement entre eux.
System.runAs() dans un test évite-t-il cette erreur ?
Non, System.runAs() change le contexte de l'utilisateur en cours pour le test des permissions ; il n'isole pas une limite de transaction comme le fait un appel future ou Test.startTest().

Commencez gratuitement. Sans carte bancaire, sans installation, sans engagement.

Configuration en moins de 15 minutes. Aucune embauche DevOps nécessaire.

Curieux de livrer plus vite avant de vous lancer ? Parlons-en

Sans engagement.