[{"data":1,"prerenderedAt":711},["ShallowReactive",2],{"\u002Fnext\u002Fdodock\u002Fdeveloppeurs\u002Fapi\u002Fmapreduce":3,"section:\u002Fnext\u002Fdodock\u002Fdeveloppeurs\u002Fapi\u002Fmapreduce":710},{"id":4,"title":5,"body":6,"description":689,"extension":690,"meta":691,"navigation":109,"path":706,"seo":707,"stem":708,"__hash__":709},"docs\u002F2.next\u002F3.dodock\u002F10.developpeurs\u002F1.api\u002Fmapreduce.md","Traitement parallèle MapReduce",{"type":7,"value":8,"toc":680},"minimark",[9,14,18,21,50,62,66,83,87,165,265,271,277,281,312,316,462,475,479,488,560,563,567,573,598,603,615,619,676],[10,11,13],"h2",{"id":12},"principe","Principe",[15,16,17],"p",{},"MapReduce découpe un traitement long en petites tâches indépendantes. Dodock exécute ces tâches en arrière-plan, plusieurs à la fois, puis combine leurs résultats en un résultat final. Cette fonction existe dans la version next de Dodock.",[15,19,20],{},"Un traitement MapReduce a trois éléments :",[22,23,24,32,42],"ol",{},[25,26,27,31],"li",{},[28,29,30],"strong",{},"Les données"," : une liste d'éléments indépendants, par exemple des noms de documents.",[25,33,34,41],{},[28,35,36,37],{},"La fonction de ",[38,39,40],"em",{},"map"," : traite un élément et renvoie un résultat partiel.",[25,43,44,49],{},[28,45,36,46],{},[38,47,48],{},"reduce"," : ajoute un résultat partiel au résultat final.",[15,51,52,53,57,58,61],{},"Dodock crée un document ",[54,55,56],"code",{},"MapReduce Job"," pour le traitement et un document ",[54,59,60],{},"MapReduce Task"," par élément de la liste.",[63,64,65],"mermaid",{},"\nflowchart LR\n  D[\"Liste de données\"] --> T1[\"Tâche 1 : map\"]\n  D --> T2[\"Tâche 2 : map\"]\n  D --> T3[\"Tâche 3 : map\"]\n  T1 --> R[\"reduce\"]\n  T2 --> R\n  T3 --> R\n  R --> F[\"Résultat final\"]\n  F --> C[\"callback\"]\n",[67,68,71],"callout",{"color":69,"icon":70},"amber","i-heroicons-exclamation-triangle",[15,72,73,74,76,77,82],{},"Les tâches s'exécutent dans un ordre quelconque. Le résultat final ne doit pas dépendre de cet ordre : la fonction de ",[38,75,48],{}," doit être associative et commutative. Pour des étapes qui dépendent les unes des autres, utilisez une ",[78,79,81],"a",{"href":80},"\u002Fnext\u002Fdodock\u002Fdeveloppeurs\u002Ftaches-en-arriere-plan","tâche en arrière-plan"," classique.",[10,84,86],{"id":85},"lancer-un-traitement","Lancer un traitement",[88,89,94],"pre",{"className":90,"code":91,"language":92,"meta":93,"style":93},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","from frappe.utils.background_jobs import mapreduce\n\nmapreduce(\n    map_method,\n    reduce_method,\n    callback_method,\n    data,\n    document_type,\n    document_name,\n    job_name,\n)\n","python","",[54,95,96,104,111,117,123,129,135,141,147,153,159],{"__ignoreMap":93},[97,98,101],"span",{"class":99,"line":100},"line",1,[97,102,103],{},"from frappe.utils.background_jobs import mapreduce\n",[97,105,107],{"class":99,"line":106},2,[97,108,110],{"emptyLinePlaceholder":109},true,"\n",[97,112,114],{"class":99,"line":113},3,[97,115,116],{},"mapreduce(\n",[97,118,120],{"class":99,"line":119},4,[97,121,122],{},"    map_method,\n",[97,124,126],{"class":99,"line":125},5,[97,127,128],{},"    reduce_method,\n",[97,130,132],{"class":99,"line":131},6,[97,133,134],{},"    callback_method,\n",[97,136,138],{"class":99,"line":137},7,[97,139,140],{},"    data,\n",[97,142,144],{"class":99,"line":143},8,[97,145,146],{},"    document_type,\n",[97,148,150],{"class":99,"line":149},9,[97,151,152],{},"    document_name,\n",[97,154,156],{"class":99,"line":155},10,[97,157,158],{},"    job_name,\n",[97,160,162],{"class":99,"line":161},11,[97,163,164],{},")\n",[166,167,168,181],"table",{},[169,170,171],"thead",{},[172,173,174,178],"tr",{},[175,176,177],"th",{},"Paramètre",[175,179,180],{},"Rôle",[182,183,184,201,212,225,235,255],"tbody",{},[172,185,186,192],{},[187,188,189],"td",{},[54,190,191],{},"map_method",[187,193,194,195,197,198],{},"Chemin pointé de la fonction de ",[38,196,40],{},", par exemple ",[54,199,200],{},"mon_app.calculs.map_ligne",[172,202,203,208],{},[187,204,205],{},[54,206,207],{},"reduce_method",[187,209,194,210],{},[38,211,48],{},[172,213,214,219],{},[187,215,216],{},[54,217,218],{},"callback_method",[187,220,221,222],{},"Chemin pointé de la fonction appelée à la fin, ou ",[54,223,224],{},"None",[172,226,227,232],{},[187,228,229],{},[54,230,231],{},"data",[187,233,234],{},"Liste Python des éléments à traiter. Elle ne peut pas être vide",[172,236,237,246],{},[187,238,239,242,243],{},[54,240,241],{},"document_type",", ",[54,244,245],{},"document_name",[187,247,248,249,251,252],{},"Document lié au traitement, ou ",[54,250,224],{},". Dodock les passe au ",[38,253,254],{},"callback",[172,256,257,262],{},[187,258,259],{},[54,260,261],{},"job_name",[187,263,264],{},"Nom libre du traitement",[15,266,267,268,270],{},"Tous les paramètres sont obligatoires. Passez ",[54,269,224],{}," pour ceux que vous n'utilisez pas.",[15,272,273,274,276],{},"La fonction crée et valide le document ",[54,275,56],{},", puis le renvoie. Les tâches partent en arrière-plan : la fonction ne renvoie pas le résultat.",[10,278,280],{"id":279},"écrire-les-fonctions","Écrire les fonctions",[282,283,284,289,300],"ul",{},[25,285,36,286,288],{},[38,287,40],{}," reçoit un élément de la liste. Elle renvoie un résultat partiel sérialisable en JSON.",[25,290,36,291,293,294],{},[38,292,48],{}," reçoit le résultat final en cours, puis le résultat partiel. Elle renvoie le nouveau résultat final. ",[28,295,296,297,299],{},"Au premier appel, le résultat final vaut ",[54,298,224],{},".",[25,301,302,303,305,306,308,309,311],{},"Le ",[38,304,254],{}," reçoit le résultat final. Si ",[54,307,241],{}," et ",[54,310,245],{}," sont renseignés, il les reçoit aussi.",[10,313,315],{"id":314},"exemple-somme-dune-liste-de-nombres","Exemple : somme d'une liste de nombres",[88,317,319],{"className":90,"code":318,"language":92,"meta":93,"style":93},"# mon_app\u002Fcalculs.py\nimport frappe\nfrom frappe.utils.background_jobs import mapreduce\n\n\ndef map_nombre(item):\n    return item\n\n\ndef reduce_somme(final, partiel):\n    return (final or 0) + partiel\n\n\ndef fin_somme(resultat):\n    frappe.logger().info(f\"Somme calculée : {resultat}\")\n\n\ndef lancer():\n    return mapreduce(\n        \"mon_app.calculs.map_nombre\",\n        \"mon_app.calculs.reduce_somme\",\n        \"mon_app.calculs.fin_somme\",\n        [2, 6, 1, 87],\n        None,\n        None,\n        \"Somme de test\",\n    )\n",[54,320,321,326,331,335,339,343,348,353,357,361,366,371,376,381,387,393,398,403,409,415,421,427,433,439,445,450,456],{"__ignoreMap":93},[97,322,323],{"class":99,"line":100},[97,324,325],{},"# mon_app\u002Fcalculs.py\n",[97,327,328],{"class":99,"line":106},[97,329,330],{},"import frappe\n",[97,332,333],{"class":99,"line":113},[97,334,103],{},[97,336,337],{"class":99,"line":119},[97,338,110],{"emptyLinePlaceholder":109},[97,340,341],{"class":99,"line":125},[97,342,110],{"emptyLinePlaceholder":109},[97,344,345],{"class":99,"line":131},[97,346,347],{},"def map_nombre(item):\n",[97,349,350],{"class":99,"line":137},[97,351,352],{},"    return item\n",[97,354,355],{"class":99,"line":143},[97,356,110],{"emptyLinePlaceholder":109},[97,358,359],{"class":99,"line":149},[97,360,110],{"emptyLinePlaceholder":109},[97,362,363],{"class":99,"line":155},[97,364,365],{},"def reduce_somme(final, partiel):\n",[97,367,368],{"class":99,"line":161},[97,369,370],{},"    return (final or 0) + partiel\n",[97,372,374],{"class":99,"line":373},12,[97,375,110],{"emptyLinePlaceholder":109},[97,377,379],{"class":99,"line":378},13,[97,380,110],{"emptyLinePlaceholder":109},[97,382,384],{"class":99,"line":383},14,[97,385,386],{},"def fin_somme(resultat):\n",[97,388,390],{"class":99,"line":389},15,[97,391,392],{},"    frappe.logger().info(f\"Somme calculée : {resultat}\")\n",[97,394,396],{"class":99,"line":395},16,[97,397,110],{"emptyLinePlaceholder":109},[97,399,401],{"class":99,"line":400},17,[97,402,110],{"emptyLinePlaceholder":109},[97,404,406],{"class":99,"line":405},18,[97,407,408],{},"def lancer():\n",[97,410,412],{"class":99,"line":411},19,[97,413,414],{},"    return mapreduce(\n",[97,416,418],{"class":99,"line":417},20,[97,419,420],{},"        \"mon_app.calculs.map_nombre\",\n",[97,422,424],{"class":99,"line":423},21,[97,425,426],{},"        \"mon_app.calculs.reduce_somme\",\n",[97,428,430],{"class":99,"line":429},22,[97,431,432],{},"        \"mon_app.calculs.fin_somme\",\n",[97,434,436],{"class":99,"line":435},23,[97,437,438],{},"        [2, 6, 1, 87],\n",[97,440,442],{"class":99,"line":441},24,[97,443,444],{},"        None,\n",[97,446,448],{"class":99,"line":447},25,[97,449,444],{},[97,451,453],{"class":99,"line":452},26,[97,454,455],{},"        \"Somme de test\",\n",[97,457,459],{"class":99,"line":458},27,[97,460,461],{},"    )\n",[15,463,464,465,468,469,471,472,299],{},"À la fin, le champ ",[54,466,467],{},"result"," du ",[54,470,56],{}," contient ",[54,473,474],{},"96",[10,476,478],{"id":477},"suivre-lexécution","Suivre l'exécution",[15,480,481,482,484,485,487],{},"Le formulaire du ",[54,483,56],{}," affiche une barre de progression : la part des tâches terminées. Chaque ",[54,486,60],{}," a un statut :",[166,489,490,500],{},[169,491,492],{},[172,493,494,497],{},[175,495,496],{},"Statut",[175,498,499],{},"Signification",[182,501,502,514,525,536,547],{},[172,503,504,511],{},[187,505,506,507,510],{},"En file d'attente (",[54,508,509],{},"Queued",")",[187,512,513],{},"La tâche attend un worker",[172,515,516,522],{},[187,517,518,519,510],{},"En cours (",[54,520,521],{},"Running",[187,523,524],{},"Un worker exécute la tâche",[172,526,527,533],{},[187,528,529,530,510],{},"En pause (",[54,531,532],{},"Paused",[187,534,535],{},"Le traitement est suspendu",[172,537,538,544],{},[187,539,540,541,510],{},"Terminé (",[54,542,543],{},"Completed",[187,545,546],{},"La tâche est finie et son résultat est ajouté",[172,548,549,555],{},[187,550,551,552,510],{},"Annulé (",[54,553,554],{},"Canceled",[187,556,302,557,559],{},[54,558,56],{}," a été annulé",[15,561,562],{},"Dodock lance d'abord 4 tâches, puis une nouvelle tâche à chaque tâche terminée.",[10,564,566],{"id":565},"suspendre-reprendre-relancer","Suspendre, reprendre, relancer",[15,568,569,570,572],{},"Le formulaire d'un ",[54,571,56],{}," validé et pas encore fini propose ces boutons :",[282,574,575,583,589],{},[25,576,577,580,581,299],{},[28,578,579],{},"Pause"," : passe les tâches en file d'attente au statut ",[54,582,532],{},[25,584,585,588],{},[28,586,587],{},"Resume"," : remet les tâches en pause en file d'attente.",[25,590,591,594,595,597],{},[28,592,593],{},"Actions > Forcer la réessayer"," : remet en file d'attente les tâches au statut ",[54,596,521],{},", par exemple après un redémarrage du serveur.",[67,599,600],{"color":69,"icon":70},[15,601,602],{},"Avant de forcer une relance, vérifiez qu'aucun worker n'exécute encore la tâche. Sinon, son résultat partiel peut être ajouté deux fois.",[15,604,605,606,608,609,611,612,614],{},"Annuler le ",[54,607,56],{}," passe les tâches en attente ou en pause au statut ",[54,610,554],{},". Dodock supprime les ",[54,613,56],{}," de plus de 30 jours avec le nettoyage des journaux.",[10,616,618],{"id":617},"exemples-dusage","Exemples d'usage",[166,620,621,634],{},[169,622,623],{},[172,624,625,628,631],{},[175,626,627],{},"Cas d'usage",[175,629,630],{},"Map",[175,632,633],{},"Reduce",[182,635,636,647,658],{},[172,637,638,641,644],{},[187,639,640],{},"Génération de PDF en masse",[187,642,643],{},"Génère le PDF d'une facture et renvoie le nom du fichier",[187,645,646],{},"Ajoute le nom à une liste",[172,648,649,652,655],{},[187,650,651],{},"Nettoyage de données",[187,653,654],{},"Renvoie les doublons d'une période",[187,656,657],{},"Fusionne les listes",[172,659,660,663,673],{},[187,661,662],{},"Contrôle d'un import",[187,664,665,666,669,670],{},"Contrôle une ligne et renvoie ",[54,667,668],{},"{\"ok\": 1}"," ou ",[54,671,672],{},"{\"erreur\": 1}",[187,674,675],{},"Additionne les compteurs",[677,678,679],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":93,"searchDepth":100,"depth":106,"links":681},[682,683,684,685,686,687,688],{"id":12,"depth":106,"text":13},{"id":85,"depth":106,"text":86},{"id":279,"depth":106,"text":280},{"id":314,"depth":106,"text":315},{"id":477,"depth":106,"text":478},{"id":565,"depth":106,"text":566},{"id":617,"depth":106,"text":618},"Parallelisez des traitements longs et intensifs via le modèle MapReduce intégré au framework.","md",{"keywords":692,"reviewed":109,"last_verified":699,"sources":700},[693,694,695,696,697,698],"MapReduce","parallélisation","tâches asynchrones","traitement batch","framework","dodock","2026-11-03",[701,702,703,704,705],"dodock@develop:frappe\u002Futils\u002Fbackground_jobs.py","dodock@develop:frappe\u002Fcore\u002Fdoctype\u002Fmapreduce_job\u002Fmapreduce_job.py","dodock@develop:frappe\u002Fcore\u002Fdoctype\u002Fmapreduce_job\u002Fmapreduce_job.js","dodock@develop:frappe\u002Fcore\u002Fdoctype\u002Fmapreduce_task\u002Fmapreduce_task.json","dodock@develop:frappe\u002Fhooks.py","\u002Fnext\u002Fdodock\u002Fdeveloppeurs\u002Fapi\u002Fmapreduce",{"title":5,"description":689},"2.next\u002F3.dodock\u002F10.developpeurs\u002F1.api\u002Fmapreduce","lV47O517xWnzuYwvDUZQPR7MTuik3QFHUQGowgejByg",{},1793871731663]