> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appsignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Batches Sidekiq : exécuter des jobs en parallèle, puis agir

> Utilisez les batches Sidekiq pour répartir une unité de travail sur de nombreux jobs, puis exécutez un callback une fois que chaque job du batch a réussi.

export const YouTube = ({id, title, description, presenter, duration, republished}) => {
  const playlistId = "PLFHQSOKTqHXA";
  const playlistUrl = `https://www.youtube.com/playlist?list=${playlistId}`;
  if (!id || id === "YOUTUBE_ID") {
    return <div style={{
      border: "1px dashed currentColor",
      borderRadius: "0.5rem",
      opacity: 0.7,
      padding: "2rem 1.5rem",
      textAlign: "center",
      fontSize: "0.875rem"
    }}>
        <strong>Video not published yet.</strong>
        <br />
        {title ? `"${title}" has no YouTube id.` : "This tutorial has no YouTube id."}{" "}
        Replace <code>YOUTUBE_ID</code> in this page with the id from the
        video's URL, once it is in the{" "}
        <a href={playlistUrl}>GoRails x AppSignal playlist</a>.
      </div>;
  }
  const prettyDate = republished ? new Date(`${republished}T00:00:00Z`).toLocaleDateString("en-US", {
    year: "numeric",
    month: "long",
    day: "numeric",
    timeZone: "UTC"
  }) : null;
  const schema = {
    "@context": "https://schema.org",
    "@type": "VideoObject",
    name: title,
    embedUrl: `https://www.youtube-nocookie.com/embed/${id}`,
    contentUrl: `https://www.youtube.com/watch?v=${id}`,
    thumbnailUrl: [`https://i.ytimg.com/vi/${id}/maxresdefault.jpg`],
    creator: {
      "@type": "Organization",
      name: "GoRails",
      url: "https://gorails.com"
    },
    publisher: {
      "@type": "Organization",
      name: "AppSignal",
      url: "https://appsignal.com"
    }
  };
  if (description) schema.description = description;
  if (duration) schema.duration = duration;
  if (republished) schema.uploadDate = republished;
  if (presenter) schema.author = {
    "@type": "Person",
    name: presenter
  };
  return <div style={{
    marginBottom: "1.5rem"
  }}>
      <iframe width="100%" height="450" src={`https://www.youtube-nocookie.com/embed/${id}?list=${playlistId}`} title={title} style={{
    borderRadius: "0.5rem",
    border: 0,
    display: "block"
  }} allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen />
      <p style={{
    fontSize: "0.875rem",
    marginTop: "0.5rem"
  }}>
        <a href={`https://www.youtube.com/watch?v=${id}&list=${playlistId}`}>{`Watch "${title}" on YouTube`}</a>
      </p>
      <script type="application/ld+json" dangerouslySetInnerHTML={{
    __html: JSON.stringify(schema)
  }} />
    </div>;
};

<YouTube id="gs8M3_JPgMA" title="Traiter des jobs en arrière-plan par batches et agir une fois terminés" description="Utilisez les batches Sidekiq pour répartir une unité de travail sur de nombreux jobs, puis exécutez un callback une fois que chaque job du batch a réussi." presenter="Chris Oliver" duration="PT13M11S" republished="2026-09-19" />

Présenté par Chris Oliver pour [GoRails](https://gorails.com). Republié sur AppSignal le 19 septembre 2026.

Les tâches en arrière-plan ordinaires vous font passer du séquentiel au parallèle, et vous laissent là. Rails met plusieurs jobs en file d'attente, ils s'exécutent, et rien ne sait quand le dernier s'est terminé.

C'est un problème chaque fois que l'étape suivante a besoin de tous les résultats précédents. Le provisionnement d'un cluster en est l'exemple le plus clair. Vous créez trois serveurs web, et le load balancer ne peut pas être reconfiguré tant que chacun d'eux n'existe pas et n'a pas d'adresse IP. Vous ne pouvez pas ajouter des adresses que vous n'avez pas encore, et chaque serveur met un temps imprévisible à être prêt.

Les batches Sidekiq résolvent ce problème. Vous regroupez un ensemble de jobs dans un batch, et vous enregistrez un callback qui s'exécute une fois lorsque le batch se termine. Séquentiel, puis parallèle, puis retour au séquentiel.

<h2 id="what-this-covers">
  Ce que couvre ce tutoriel
</h2>

* Regrouper des jobs dans un batch et enregistrer un callback de succès.
* Pourquoi le callback vit dans sa propre classe plutôt que dans un bloc.
* Transmettre du contexte à un callback qui s'exécute sur une autre machine.

<h2 id="requirements">
  Prérequis
</h2>

| Élément | Valeur                                 |
| ------- | -------------------------------------- |
| Queue   | Sidekiq                                |
| Batches | Sidekiq Pro, ou la gem `sidekiq-batch` |

<Note>
  Les batches sont une fonctionnalité de [Sidekiq Pro](https://sidekiq.org/products/pro). La gem open source `sidekiq-batch` implémente la même API, ce qui est utile pour essayer le pattern. Pour la production, Sidekiq Pro est l'option la mieux prise en charge.
</Note>

<h2 id="write-the-jobs-in-plain-sidekiq">
  Écrire les jobs en Sidekiq pur
</h2>

Les batches sont une fonctionnalité de Sidekiq, pas d'Active Job. Les jobs qui font partie d'un batch doivent utiliser Sidekiq directement, afin que l'encapsulation propre à Active Job ne s'interpose pas entre le batch et ses jobs.

Convertir un job est un changement de deux lignes : inclure `Sidekiq::Job` plutôt que d'hériter de `ApplicationJob`, et mettre en file d'attente avec `perform_async` plutôt qu'avec `perform_later`.

```ruby theme={null}
# app/jobs/create_server_job.rb
class CreateServerJob
  include Sidekiq::Job

  def perform(id)
    # The work you want to run in parallel.
  end
end
```

<Note>
  `Sidekiq::Job` est le nom actuel. Le code plus ancien et les anciens screencasts utilisent `Sidekiq::Worker`, qui fonctionne toujours comme alias.
</Note>

<h2 id="create-the-batch">
  Créer le batch
</h2>

Construisez le batch là où le travail est déclenché, puis mettez les jobs en file d'attente à l'intérieur de `batch.jobs` :

```ruby theme={null}
# app/jobs/create_cluster_job.rb
class CreateClusterJob
  include Sidekiq::Job

  def perform(cluster_id)
    batch = Sidekiq::Batch.new
    batch.description = "Creating cluster"
    batch.on(:success, CreateClusterJob::Created, "cluster_id" => cluster_id)

    batch.jobs do
      5.times { |i| CreateServerJob.perform_async(i) }
    end
  end
end
```

Sidekiq suit les jobs mis en file d'attente à l'intérieur de ce bloc. Lorsque le dernier réussit, il exécute le callback une seule fois.

`on(:complete)` est l'autre callback que vous pouvez enregistrer. Il se déclenche lorsque chaque job est terminé, qu'ils aient tous réussi ou non. Utilisez `:success` lorsque l'étape suivante dépend de la réussite du travail.

<h2 id="write-the-callback-as-a-class">
  Écrire le callback comme une classe
</h2>

Le callback est une classe plutôt qu'un bloc, et la raison mérite d'être comprise plutôt que contournée.

Au moment où un batch se termine, le processus qui l'a créé a disparu. Le callback s'exécute comme son propre job Sidekiq, potentiellement sur une machine différente, des minutes plus tard. Aucune requête, aucune variable locale, ni aucune instance de modèle n'est encore dans la portée. Un bloc capturerait un état qui n'existe plus.

Sidekiq instancie donc votre classe de callback à neuf et lui transmet le statut du batch ainsi que le hash d'options que vous avez enregistré :

```ruby theme={null}
class CreateClusterJob
  class Created
    def on_success(status, options)
      cluster = Cluster.find(options["cluster_id"])
      # Everything in the batch succeeded. Do the serial work.
    end
  end
end
```

Ce hash d'options est le seul canal pour le contexte. Tout ce dont le callback a besoin pour retrouver le chemin vers l'enregistrement sur lequel il travaille doit y être placé. Limitez-vous à des identifiants, car il est sérialisé.

<h2 id="chain-batches-together">
  Enchaîner des batches
</h2>

Un callback peut créer un autre batch. C'est ainsi que vous construisez un workflow de plus de deux étapes. Créez les serveurs. Dans le callback, configurez-les en tant que nouveau batch. Dans ce callback, marquez le cluster comme actif.

<h2 id="monitor-batches-with-appsignal">
  Surveiller les batches avec AppSignal
</h2>

AppSignal instrumente Sidekiq via son [intégration Sidekiq](/ruby/integrations/sidekiq), et signale les jobs dans le [namespace](/guides/namespaces) `background`.

Les batches changent ce qu'il vaut la peine de surveiller :

* **Le callback est le job qui compte.** Un batch n'est utile que si le callback s'exécute. Laissez le [suivi des erreurs](/errors) vous avertir lorsqu'il lève une exception. Un callback qui échoue silencieusement laisse un workflow à moitié terminé, alors que les jobs précédents signalent tous un succès. Il s'exécute comme un job Sidekiq, donc AppSignal l'instrumente automatiquement. Ajoutez des [événements d'instrumentation](/ruby/instrumentation/background-jobs#instrumentation-events) pour voir où passe son temps.
* **Un seul échec arrête le batch.** Avec un callback `on(:success)`, un seul job échoué signifie que le callback ne s'exécutera jamais. L'échec apparaîtra dans vos incidents d'erreur ; l'absence du callback, elle, n'apparaîtra nulle part, à moins que vous ne la recherchiez.
* **Les workflows longs méritent un check-in.** Les batches à plusieurs étapes qui doivent se terminer selon un planning conviennent bien aux [check-ins](/check-ins), qui signalent l'exécution qui n'a pas eu lieu plutôt que celle qui a échoué.
* **Taguez les jobs avec le batch.** Ajouter l'ID du batch ou du workflow comme [tag](/guides/tagging) vous permet de regrouper tous les jobs d'une même exécution lorsque vous cherchez à savoir quelle étape a bloqué. AppSignal stocke les arguments de chaque job en tant que [paramètres](/guides/custom-data/request-parameters).

<h2 id="transcript">
  Transcription
</h2>

Transcrit à partir de la vidéo et légèrement édité : les sous-titres automatiques ont mal compris plusieurs noms de produits et d'API, et ceux-ci ont été corrigés.

<Accordion title="Lire la transcription">
  **0:02** Salut tout le monde ! Dans cet épisode, on va parler de la création de workers en arrière-plan avancés avec les batches Sidekiq. C'est vraiment cool, parce que ça permet de prendre un ensemble de jobs en arrière-plan en cours d'exécution et de les regrouper. Quand ils sont terminés, on peut appeler un callback et passer d'une exécution séquentielle à parallèle, puis de nouveau à séquentielle. Normalement, avec les jobs en arrière-plan, on passe de Rails, qui s'exécute en séquentiel, au parallèle, et c'est terminé.

  **0:33** Mais c'est important pour les workflows complexes, parce que souvent, par exemple quand on construit quelque chose comme Hatchbox, l'une des choses à garder à l'esprit quand on fait des choses complexes, c'est que, par exemple, quand on fait des choses en arrière-plan comme créer et configurer des serveurs, il faut parfois attendre qu'un bloc de travail soit terminé. Par exemple, avec un cluster de serveurs, on pourrait avoir un load balancer qui délègue à plusieurs serveurs web. Et dans ce cas, on veut lui ajouter un serveur web. On aurait donc un load balancer et deux serveurs web, et celui-ci fait aussi office de cron, de worker en arrière-plan et de serveur de base de données. Mais on imagine bien que toute configuration de ce genre aura des problèmes similaires.

  **1:18** Le problème, c'est quand on va faire le travail côté backend. Si on soumet ça, on va savoir qu'un serveur web a été ajouté, donc il faut créer ce nouveau serveur sur DigitalOcean. On peut donc lancer ce job pour le faire. Et on sait qu'il faut mettre à jour le load balancer et le reconfigurer avec l'adresse IP de ce nouveau serveur web. Cependant, si on veut en ajouter plusieurs d'un coup, on ne pourrait pas le faire aussi facilement.

  **1:45** Et la raison, c'est que lorsqu'on va créer ces deux serveurs web sur DigitalOcean, ils peuvent prendre un temps aléatoire pour être créés. Et on ne peut pas configurer le load balancer immédiatement. On ne pourrait pas simplement dire : d'accord, configure toutes les machines, crée les nouvelles machines, parce que ce load balancer doit attendre que les nouveaux serveurs web soient créés et qu'on ait des adresses IP pour eux. On ne peut pas ajouter leurs adresses IP dans le load balancer si on ne les a pas. C'est donc notre problème.

  **2:16** Il faut créer tous ces serveurs dans un batch et attendre qu'ils soient tous terminés, et ensuite on peut configurer toutes les machines. Comme ça, on a toutes les informations nécessaires pour faire la configuration. C'est là que les batches Sidekiq entrent en jeu. Parlons maintenant des batches Sidekiq. C'est une fonctionnalité pro de Sidekiq.

  **2:35** Ça coûte donc de l'argent, mais ça en vaut vraiment la peine, et ça s'accompagne d'autres fonctionnalités géniales qui seraient de toute façon nécessaires pour à peu près n'importe quelle application de production utilisant des workers en arrière-plan avancés comme celui-ci. Vous avez aussi la possibilité d'utiliser une version open source des batches Sidekiq, qui ajoute la même API. Je vais vous la montrer pour que vous puissiez aussi cloner ce dépôt et l'essayer, mais vous voudrez sans doute utiliser Sidekiq Pro. Les batches sont vraiment cool. Vous écrivez essentiellement une petite configuration comme celle-ci, vous créez un nouveau batch, vous définissez une description si vous voulez, mais surtout, vous pouvez créer un callback.

  **3:16** Les callbacks vous paraîtront peut-être un peu étranges, mais la raison pour laquelle ils sont définis dans une autre classe, c'est que tout ça s'exécute dans des workers en arrière-plan qui pourraient tourner sur des machines différentes, et vous n'aurez pas accès à l'endroit où ça a été exécuté à l'origine. Donc, au moment où ça réussit et où le callback est appelé, vous n'aurez plus cette requête Rails ou cette instance de modèle d'origine. Ça pourrait être sur une toute autre machine. C'est pour ça qu'il faut créer une nouvelle instance de classe à l'intérieur de tout ça. Et ensuite, vous utilisez simplement ce bloc `jobs` pour créer tous vos nouveaux workers, et c'est à peu près tout.

  **3:59** Vos jobs vont alors s'exécuter dans ce batch, puis on instanciera cette classe de callback, quelle que soit la façon dont vous l'avez définie. Elle recevra les options supplémentaires que vous voudrez peut-être ajouter. Mais c'est à peu près ça. Regardons un exemple. On va utiliser ce concept comme exemple pour que vous puissiez bien le comprendre.

  **4:20** J'ai ici une toute nouvelle application Rails à laquelle j'ai ajouté quelques gems avec le template Jumpstart. Mais surtout, on va ajouter Sidekiq Batch. Vous pouvez utiliser soit Sidekiq Pro, soit Sidekiq Batch. Celui-ci vous permettra de cloner ce dépôt et de le tester. On va donc utiliser celui-là.

  **4:38** Ensuite, il suffit de créer quelques jobs, ou du moins un endroit qui créera au moins le batch. Je l'ai fait dans un job lui-même. J'ai donc un job qui crée un batch et les exécute. Mais vous pourriez faire ça dans vos modèles, vos contrôleurs, vos simples objets Ruby, où vous voulez créer le batch. Tout ce code va évidemment s'exécuter dans Sidekiq.

  **4:58** Tout le travail des workers va donc s'exécuter là. Créons donc rapidement un job. Disons qu'on édite app/jobs/create\_server\_job.rb. Notre `CreateServerJob` aura donc la méthode `perform`. Et il faut inclure Sidekiq Worker ici.

  **5:19** Et la raison pour laquelle on veut utiliser ça plutôt qu'Active Job, c'est qu'avec les batches Sidekiq, vous voulez vous assurer de rester sur Sidekiq lui-même. Vous ne voulez pas qu'Active Job interfère avec ça, et ça fonctionnera bien mieux de cette façon si vous n'interagissez qu'avec Sidekiq. Vous devrez donc peut-être convertir certains de vos jobs en Sidekiq pur, mais c'est vraiment facile. La seule chose à changer, c'est cette ligne ici : ne pas hériter d'Active Job et utiliser `perform_async` au lieu de `perform_later` avec Active Job. C'est à peu près tout.

  **5:55** Et ici, vous voulez faire un peu de travail. Vous voulez donc faire votre travail en parallèle. C'est à peu près tout. Vous n'avez rien de trop compliqué à faire ici. Et là où vous voulez déclencher ces workers, c'est là où vous voulez créer le code du job ou du batch.

  **6:13** Créons donc un autre job appelé `CreateClusterJob`. `class CreateClusterJob`, `perform`. Et c'est ici qu'on veut créer notre batch. On écrit donc `batch = Sidekiq::Batch.new`, `batch.description = "Creating cluster"`. Et ensuite on écrit aussi `batch.on :success`.

  **6:36** On veut appeler une sorte de callback. Vous pouvez donc donner un nom de classe ici. Vous pouvez aussi, du moins avec Sidekiq Batch, donner une chaîne de caractères ici, et elle sera aussi constantisée. Donc si on avait simplement une classe appelée `Created` ici, `class Created`, on pourrait le faire. Il faudrait bien sûr la namespacer.

  **6:58** `CreateClusterJob::Created`. Comme ça, on y a accès très facilement. On peut aussi le faire simplement comme une classe. Et ensuite, vous pouvez éventuellement passer un hash d'options. Donc si vous aviez besoin que l'ID du cluster soit transmis, vous pourriez le faire aussi.

  **7:18** Vous pourriez donc faire quelque chose comme ça, où vous passez l'ID du cluster. On n'a pas de modèle Cluster. Je vais donc simplement mettre 999. Comme ça, vous avez un exemple à voir. Et ensuite on dit `batch.jobs do`.

  **7:28** Et là, on doit mettre en file d'attente nos autres jobs. Restons simples : on écrit `5.times`. On fait ça ici. On prend le nombre là.

  **7:41** `CreateServerJob.perform_async`, on lui passe `i`. Et voilà. Ça garantira que ceux-là seront ajoutés. Et une fois que vous faites ça, ça met les jobs en file d'attente et commence à les exécuter.

  **8:02** Et ensuite, dans votre classe de callback, il vous suffit d'écrire `on_success`. Et éventuellement, vous pouvez aussi faire un `def on_complete`, si vous le souhaitez. Il y a une nuance ou une différence entre les deux. J'ai toujours utilisé `on_success`. C'est donc ce qu'on va utiliser ici.

  **8:20** Les deux prennent un statut et des options. Vous pouvez donc afficher le statut et les options aussi. Les options devraient être exactement le hash tel que vous l'avez passé. Et le statut sera le statut Sidekiq. Mettons donc un séparateur ici et écrivons « created cluster ».

  **8:40** Point. On devrait donc voir ça chaque fois que ces jobs de serveur sont terminés. C'est donc le travail que vous voulez faire en parallèle. Faisons donc simplement dormir le processus entre 1 et 10 secondes de façon aléatoire. Et ensuite on affiche « creating server... ».

  **8:59** Et on peut lui donner l'ID qu'on a passé ici. Et ensuite, une fois terminé, « created ». Juste pour qu'on puisse voir dans nos logs les lignes exactes de serveur qui correspondent entre elles. On peut ainsi les compter et s'assurer que tout est correct. Parce qu'on ne devrait voir ça qu'une seule fois.

  **9:26** On devrait voir ça s'afficher autant de fois qu'on l'appelle. Donc 5 fois dans ce cas, 10 fois si vous passez à 10, peu importe. Et on a donc maintenant ce job qui déclenche essentiellement une série d'autres jobs. Et ce code va s'exécuter une seule fois, une fois que tout est terminé.

  **9:47** Et il s'exécutera aussi à l'intérieur de Sidekiq. C'est donc une autre chose à garder à l'esprit. C'est pour ça que cette classe est instanciée : parce qu'elle s'exécute sur le serveur, quel qu'il soit, à l'intérieur de Sidekiq. Elle n'aura donc pas accès aux variables d'instance ou à d'autres éléments locaux. Parce que ce sera une toute nouvelle instance de cette classe `Created` quand elle s'exécutera.

  **10:07** C'est pour ça qu'il faut pouvoir passer ces options, pour accéder par exemple à votre ID de cluster une fois que c'est terminé, afin de déterminer quel cluster doit être marqué comme actif. Cela dit, on peut aller dans la console Rails et essayer ça. Démarrons Sidekiq. Il tourne maintenant, et on dit `CreateClusterJob.new.perform`. Si tout s'est bien passé, on devrait voir cinq jobs mis en file d'attente.

  **10:34** C'est l'idée de ces jobs. Sidekiq, et ici on peut les voir s'exécuter. On a donc les serveurs zéro à quatre, donc cinq d'entre eux, et vous pouvez voir qu'ils s'exécutent, dans l'ordre où Sidekiq les traite en premier. Vous pouvez voir que le trois s'est terminé avant certains des autres, et tout ça s'est terminé avec succès.

  **10:56** On voit donc que cinq jobs ont démarré et que cinq jobs se sont terminés. Et ensuite, tout à la fin, un nouveau worker de callback de batch Sidekiq a démarré, ce que la gem a implémenté pour nous, ça déclenche ça comme un job et ça ne s'exécute qu'une seule fois. C'est donc cool, parce qu'on a maintenant la possibilité de passer d'une seule chose en cours d'exécution à plusieurs, puis de revenir à une seule chose. Et c'est vraiment génial, parce que ça nous permet de faire ça de façon distribuée sur notre cluster Sidekiq aussi. C'est très courant de faire ça avec du threading, où on a juste une seule machine, on lance une série de threads, on fait du travail parallèle, puis on les rejoint et on attend que ces threads soient terminés pour refaire des choses en séquentiel.

  **11:43** Mais avec Sidekiq, on peut faire ça de façon distribuée sur nos serveurs workers Sidekiq. C'est donc vraiment cool et ça nous permet de faire beaucoup plus de choses de cette manière, de façon bien plus agréable, parce que ça peut maintenant être mis à l'échelle sur plusieurs machines, ce qui est fantastique. Sidekiq Pro, c'est donc là où vous pouvez l'obtenir, ou vous utilisez la gem. Je recommanderais vraiment Sidekiq Pro. Le support sera meilleur.

  **12:08** Ça va probablement implémenter tout ça plus efficacement ou peu importe. Et ça va certainement s'améliorer avec le temps. Cette fonctionnalité est donc vraiment une sorte de nécessité quand vous commencez à construire des jobs en arrière-plan plus avancés. Vous en aurez probablement besoin dans certaines situations, parce que vous avez un workflow que votre code doit exécuter. Et ça va vous aider à construire ces workflows.

  **12:29** Par exemple, votre callback `Created` pourrait en fait démarrer un autre batch et en déclencher un autre. C'est donc assez intéressant. Vous pouvez faire en sorte que tout ça s'enchaîne. Et c'est ce que je fais dans Hatchbox. C'est donc assez chouette de voir comment tout ça fonctionne et les choses complexes qu'on peut faire.

  **12:49** C'est donc très cool. Et c'est bien sûr une fonctionnalité complexe qui est nécessaire pour beaucoup d'applications. J'espère donc que cet épisode vous a plu. Si vous voulez voir plus de workers en arrière-plan avancés ou de choses sur Sidekiq, dites-le-moi dans les commentaires ci-dessous. On se retrouve dans le prochain épisode.

  **13:05** Salut.
</Accordion>

<h2 id="related-tutorials">
  Tutoriels associés
</h2>

* [Solid Queue](/tutorials/ruby/solid-queue)
* [Mission Control Jobs](/tutorials/ruby/mission-control-jobs)

<h2 id="about-this-tutorial">
  À propos de ce tutoriel
</h2>

Ce tutoriel résume un screencast GoRails sur la création de workers en arrière-plan avancés avec les batches Sidekiq, par Chris Oliver. Le screencast est l'œuvre originale. GoRails le publie, avec le reste de la série, sur [gorails.com](https://gorails.com).
