C’est le premier tri, et beaucoup de candidats le sautent. Un OOM sur le driver et un OOM sur un executor n’ont ni les mêmes causes ni les mêmes remèdes.
Sur le driver, les suspects sont peu nombreux :
collect() ou toPandas() sur un DataFrame volumineux — les données remontent dans la mémoire du driver.broadcast d’une table trop grosse : elle est collectée par le driver avant d’être redistribuée.checkpoint() résout.Sur un executor, les causes sont différentes :
groupByKey là où un reduceByKey suffisait : toutes les valeurs d’une clé doivent tenir en mémoire sur une seule machine.groupBy("cle").count().orderBy(desc("count")) sur les vingt premières lignes tranche immédiatement entre une clé pathologique et une distribution naturelle.L’erreur d’instinct est d’augmenter spark.executor.memory. C’est le dernier levier, pas le premier : il masque le problème et double la facture.
Le bon ordre :
spark.sql.shuffle.partitions réduit la taille de chacune. C’est souvent la seule chose à faire, et elle est gratuite.reduceByKey au lieu de groupByKey, agrégation en DataFrame au lieu de RDD.unpersist(), ou passer en MEMORY_AND_DISK.« Pourquoi ne pas simplement mettre 64 Go par executor ? »
Parce qu’au-delà d’environ 32 Go de tas, le ramasse-miettes de la JVM devient une source de latence, et qu’on perd la compression des pointeurs d’objets. La pratique courante est plutôt de multiplier les executors de taille moyenne — 4 à 5 cœurs et 16 à 32 Go — que d’en faire un énorme.
Et il faut mentionner la mémoire hors tas : spark.executor.memoryOverhead couvre les tampons de shuffle et les processus Python. Un OOM tué par YARN ou Kubernetes avec un code de sortie 143 vient souvent de là, pas du tas.