Vous savez créer un diagramme de flux ? Vous saurez utiliser le Process Builder.

Le Lightning Process Builder est une des fonctionnalités les plus prometteuses que Salesforce ait mise à la disposition de ses utilisateurs depuis longtemps.

Le concept : créez des processus à partir d’une interface visuelle.

Vous savez créer un diagramme de flux ? Vous saurez utiliser le Process Builder.

Plus besoin de code Apex pour gérer des mises à jours de champs sur des enregistrements enfants, ce qui ne pouvait pas se faire avec un workflow classique. Le Process Builder est même capable de différer ses actions dans le temps. Ok, on pouvait déjà le faire avec un workflow, mais pas avec un trigger !

Le moteur d’exécution est le même que celui de VisualFlow, que le Process Builder complète, et non remplace. Le Process Builder devient le déclencheur de processus, qu’il peut traiter lui-même tant qu’il reste dans ses cordes, sinon, il peut appeler un Flow qui se chargera de traiter les actions plus complexes.

En passant par le Process Builder, vous pouvez :

  • faire une mise à jour de champs,
  • créer ou mettre à jour des enregistrements,
  • appeler un Flow (qui peut prendre en charge des processus plus complexes,
  • soumettre un enregistrement pour approbation,
  • exécuter une Quick Action,
  • envoyer un email,
  • appeler une classe Apex.

Tout ça paraît bien attrayant, si ce n’était un détail qui peut vous causer pas mal de problèmes : le fonctionnement actuel du Process Builder peut générer des erreurs lors d’imports en masse.
Je m’explique : bien que les actions d’enregistrement soient exécutées en mode Bulk (une écriture en base pour un lot d’enregistrements), chaque lookup utilise une requête SOQL ce qui va créer des erreurs « Too many SOQL queries: 101 » lors de l’import de lots trop importants.

Pour illustrer ce comportement, faisons un test simple. Le but de ce test est d’examiner le fonctionnement interne du Process Builder et comment il procède pour traiter un lot d’enregistrements.

Pour cela, nous aurons besoin des éléments suivants :

  • un processus créé par Process Builder,
  • un trigger vide pour illustrer les déclenchements de triggers,
  • une classe de test APEX.

Code de la classe de test :

@isTest
private class ProcessBuilderBulkTest {
    @isTest static void test_bulk() {

        // Nombre de lignes à créer
        Integer n = 5;

        // Création de n comptes de test
        List accounts = new List();
            for(Integer i = 0; i < n; i++){
                accounts.add(new Account(
                     Name = 'test PBBulk account '+i,
                     Phone = '1234567 '+i,
                     Fax = '7654321 '+i
                ));
            }
        
        // Insertion du compte
        insert accounts;

        // Création de n contacts liés aux n comptes des test
        List contacts = new List();
        for(Integer i = 0; i < n; i++){
            contacts.add(new Contact(
                Lastname = 'test PBBulk lastname '+i,
                Firstname = 'test PBBulk firstname '+i,
                AccountId = accounts[i].Id
            ));
        }

        Test.startTest();
        
        // Insertion des contacts
        insert contacts;
        
        Test.stopTest();

        // Récupération des contacts créés en base
        contacts = [
            select Id, Phone, Fax
            from Contact 
            where Id in :contacts
        ];

        // Vérification du nombre de contacts insérés
        System.assertEquals(n, contacts.size());
    } 
}

Code du trigger :

trigger ContactTrigger on Contact (
	before insert,
	before update,
	after insert,
	after update
) {
	system.debug('########### Trigger contacts ###########') ;
}

 

Le processus est lui aussi très simple : il va mettre à jour les contacts avec des données du compte : le numéro de Téléphone.

 

img1

RÉSULTAT

10:58:31.891|TESTING_LIMITS
10:58:31.891|LIMIT_USAGE_FOR_NS|(default)|
Number of SOQL queries: 5 out of 100
Number of DML statements: 1 out of 150

Le code entre les instructions Test.startTest() et Test.stopTest() possède ses propres limites d’exécution, elles sont affichées dans un bloc distinct dans le log.

On voit ici que 5 requêtes SOQL ont été faites alors que notre code mettait à jour les 5 enregistrements d’un coup, ce qui est bien le cas plus bas : Number of DML statements: 1 out of 150.

Déjà, à cette étape, on commence à voir que si on augmente le nombre d’enregistrements à traiter, on va atteindre rapidement la limite des 100 requêtes SOQL possibles à chaque transaction.

Imaginons maintenant que nous ayons aussi besoin de copier le numéro de fax du compte dans le contact : pas de problème, il suffit d’ajouter une action avec le Process Builder !

 

img2

img3

RÉSULTAT

12:21:50.839|TESTING_LIMITS
12:21:50.839|LIMIT_USAGE_FOR_NS|(default)|
Number of SOQL queries: 10 out of 100
Number of DML statements: 2 out of 150

A présent, on a 10 requêtes SOQL, et 2 instructions DML : 1 instruction DML pour chaque action du processus.

11:22:43.233 (1233521852)|FLOW_BULK_ELEMENT_DETAIL|FlowRecordUpdate|myRule_1_A1|1
11:22:43.240 (1240826165)|FLOW_BULK_ELEMENT_DETAIL|FlowRecordUpdate|myRule_1_A1|1
11:22:43.247 (1247509147)|FLOW_BULK_ELEMENT_DETAIL|FlowRecordUpdate|myRule_1_A1|1
11:22:43.254 (1254216955)|FLOW_BULK_ELEMENT_DETAIL|FlowRecordUpdate|myRule_1_A1|1
11:22:43.261 (1261157136)|FLOW_BULK_ELEMENT_DETAIL|FlowRecordUpdate|myRule_1_A1|1
11:22:43.284 (1284233331)|CODE_UNIT_STARTED|[EXTERNAL]|01qi0000000XSEr|ContactTrigger on Contact trigger event BeforeUpdate for [003i0000022zyWu, 003i0000022zyWv, 003i0000022zyWw, 003i0000022zyWx, 003i0000022zyWy]
11:22:43.284 (1284388003)|USER_DEBUG|[7]|DEBUG|########### Trigger contacts ###########

11:22:43.307 (1307855142)|FLOW_BULK_ELEMENT_BEGIN|FlowRecordUpdate|myRule_1_A2
11:22:43.320 (1320421064)|FLOW_BULK_ELEMENT_DETAIL|FlowRecordUpdate|myRule_1_A2|1
11:22:43.329 (1329474033)|FLOW_BULK_ELEMENT_DETAIL|FlowRecordUpdate|myRule_1_A2|1
11:22:43.335 (1335161417)|FLOW_BULK_ELEMENT_DETAIL|FlowRecordUpdate|myRule_1_A2|1
11:22:43.340 (1340552573)|FLOW_BULK_ELEMENT_DETAIL|FlowRecordUpdate|myRule_1_A2|1
11:22:43.345 (1345913304)|FLOW_BULK_ELEMENT_DETAIL|FlowRecordUpdate|myRule_1_A2|1
11:22:43.370 (1370871160)|CODE_UNIT_STARTED|[EXTERNAL]|01qi0000000XSEr|ContactTrigger on Contact trigger event BeforeUpdate for [003i0000022zyWu, 003i0000022zyWv, 003i0000022zyWw, 003i0000022zyWx, 003i0000022zyWy]
11:22:43.371 (1371011977)|USER_DEBUG|[7]|DEBUG|########### Trigger contacts ###########

De plus, on voit dans le log que lors de chaque action de mise à jour de champ, les triggers sont déclenchés, ce qui peut occasionner de nouvelles requêtes SOQL ou DML.

Dans un souci d’optimisation, on veillera donc à limiter le nombre d’actions de mise à jour d’objets dans nos futurs processus.

Il n’empêche que si on doit ajouter d’autres mises à jour de champs, même groupées dans une seule action, on aura autant de requêtes SOQL que d’enregistrements.
La solution d’un trigger Apex n’est peut-être pas si compliquée après tout, et la simplicité d’utilisation du Process Builder est sérieusement occultée par cette limitation.

Comment remédier à ce problème ?

Il existe une solution de contournement : utiliser les nouvelles Apex Invocable Actions, introduites dans Spring15.
En utilisant cette nouvelle fonctionnalité appelée depuis un Flow, il serait possible de récupérer tous les enregistrements en une seule requête. Une fois obtenus, ils sont transmis à un Flow qui sera capable de les traiter en bloc.

La version généralisée de cette méthode est détaillée dans l’excellent post de James Melville (en anglais).

Bien qu’elle offre une solution, cette méthode reste limitée et si Salesforce veut que son nouveau Process Builder soit adopté par ses utilisateurs, il doit impérativement résoudre ce problème.
Pour autant, ne doutons pas qu’ils ont cela sur leur plan de release, et que nous verrons prochainement des évolutions dans ce sens.

En attendant, il ne reste plus qu’à voter pour avoir cette gestion du traitement par blocs du Lightning Process Builder :

https://success.salesforce.com/ideaView?id=08730000000DhBlAAK