> ## 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.

# Mission Control Jobs : un dashboard de jobs dans Rails

> Montez l'engine Mission Control Jobs pour parcourir les queues, les workers et les jobs en échec depuis votre application Rails, et restreignez la route aux administrateurs connectés.

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="HfJp_leekA8" title="Inspecter les jobs en arrière-plan avec Mission Control" description="Montez l'engine Mission Control Jobs pour parcourir les queues, les workers et les jobs en échec depuis votre application Rails, et restreignez la route aux administrateurs connectés." presenter="Chris Oliver" duration="PT9M43S" republished="2026-09-19" />

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

Solid Queue conserve son état dans votre base de données, ce qui vous permet de l'interroger depuis la console Rails. Mission Control Jobs place une interface utilisateur devant cet état, montée à l'intérieur de votre propre application. Vous pouvez parcourir les queues, les workers et les échecs sans écrire de requêtes.

Il communique avec Active Job plutôt qu'avec un backend spécifique, ce qui lui permet aujourd'hui de lire Solid Queue et Resque, et il est conçu pour en accueillir d'autres. AppSignal instrumente les mêmes jobs via ses intégrations [Active Job](/ruby/integrations/active-job), [Solid Queue](/ruby/integrations/solidqueue) et [Resque](/ruby/integrations/resque).

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

* Monter l'engine et restreindre qui peut y accéder.
* Lire les queues, les workers et les arguments des jobs.
* Diagnostiquer un job en échec à partir de son erreur et de son backtrace enregistrés.

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

| Quoi               | Valeur                 |
| ------------------ | ---------------------- |
| Gem                | `mission_control-jobs` |
| Backend Active Job | Solid Queue ou Resque  |

<h2 id="install-and-mount-the-engine">
  Installer et monter l'engine
</h2>

```bash theme={null}
bundle add mission_control-jobs
```

Montez-la ensuite dans `config/routes.rb` :

```ruby theme={null}
mount MissionControl::Jobs::Engine, at: "/jobs"
```

<h2 id="restrict-the-route">
  Restreindre la route
</h2>

L'interface des jobs expose les arguments des jobs, qui contiennent régulièrement des ID d'enregistrements, des adresses e-mail et tout ce que vous avez transmis à un job. Ne la montez pas sans authentification.

Avec Devise, enveloppez le montage dans une contrainte au niveau de la route :

```ruby theme={null}
authenticate :user, ->(user) { user.admin? } do
  mount MissionControl::Jobs::Engine, at: "/jobs"
end
```

Une requête provenant de quelqu'un qui échoue à la contrainte reçoit un `404` plutôt qu'une invite de connexion, de sorte que la route ne révèle pas son existence. Si votre bibliothèque d'authentification ne dispose pas de ce type d'assistant de routage, Mission Control accepte également une configuration au niveau du contrôleur.

<h2 id="read-a-job">
  Lire un job
</h2>

La liste des queues affiche chaque queue et les jobs qu'elle contient, et la liste des workers affiche les dispatchers et workers connectés avec leur dernier heartbeat. Ouvrir un job affiche sa classe, sa queue, ses arguments, ainsi que les moments où il a été mis en file d'attente et terminé.

Les arguments méritent d'être compris, car ils sont stockés en JSON plutôt qu'en objets Ruby. Une instance Active Record transmise à un job apparaît comme une référence Global ID. Un hash avec des clés symboles porte un marqueur indiquant que ses clés doivent redevenir des symboles lorsque le job est désérialisé. Ce que vous voyez, c'est la forme sérialisée, pas les objets que reçoit réellement votre job.

<h2 id="diagnose-a-failed-job">
  Diagnostiquer un job en échec
</h2>

Un job en échec conserve sa classe d'erreur, son message et son backtrace. Cela suffit généralement à identifier la ligne à l'origine de l'erreur, à la corriger et à relancer le job depuis le même écran.

<h2 id="where-appsignal-fits">
  Où AppSignal intervient
</h2>

Mission Control répond à la question « qu'y a-t-il actuellement dans mes queues ». C'est une vue en direct des propres tables du backend, limitée à une seule application.

AppSignal répond aux questions qui nécessitent un historique et une agrégation entre les déploiements :

* **Cet échec est-il nouveau, et à quelle fréquence se produit-il ?** Le [suivi des erreurs](/errors) regroupe les échecs de jobs en incidents, avec un nombre d'occurrences et une date de première apparition, plutôt qu'une liste de lignes individuelles en échec.
* **Quels jobs sont devenus plus lents, et quand ?** Les jobs sont signalés dans le [namespace](/guides/namespaces) `background` avec leurs propres [traces de performance](/performance-tracing). Combinez cela avec les [marqueurs de déploiement](/guides/deploy-markers) pour aligner une régression avec le déploiement qui l'a provoquée.
* **Le job s'est-il seulement exécuté ?** Un job qui n'est jamais mis en file d'attente ne laisse aucune trace dans aucun des deux outils. Les [check-ins](/check-ins) rendent compte du respect des plannings et vous informent de l'exécution qui n'a pas eu lieu.

Utilisez les deux. Mission Control est la console de l'opérateur ; AppSignal est le registre de ce qui s'est passé.

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

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

<Accordion title="Lire la transcription">
  **0:02** Salut à tous, je suis super content de cet épisode. Mission Control est enfin disponible publiquement. Il a quelques bugs, mais ils seront corrigés très rapidement, j'en suis sûr, mais Mission Control Jobs est la première version de Mission Control que DHH a annoncée à la Rails World 2023. Ce qui est intéressant, c'est que ça s'appelle Mission Control tiret Jobs, ce qui veut dire qu'on verra probablement d'autres fonctionnalités Mission Control à l'avenir. Pour la mettre en place, vous ajoutez Mission Control Jobs à votre Gemfile, puis vous ajoutez cette ligne à votre fichier de routes pour démarrer.

  **0:41** On va donc faire ça, on va dire bundle add, Mission tiret bas control tiret jobs, et vous l'exécutez, puis je vais lancer le serveur Rails. J'ai ici une application Rails que nous avons créée dans l'épisode Solid Queue, sur laquelle Solid Queue est installé, configuré comme plugin Puma pour s'exécuter automatiquement. Si on ouvre Puma.rb, on voit ici le plugin Solid Queue : il exécute Solid Queue à chaque démarrage et arrêt de Puma, et il arrête aussi automatiquement Solid Queue pour nous. C'est donc tout ce que vous avez à faire pour mettre en place Mission Control Jobs. Si on exécute notre application Rails, on peut ensuite aller dans notre fichier de routes et ajouter cette ligne pour monter l'engine Mission Control. On peut aussi, si vous utilisez Devise, utiliser authenticated user do autour de ça pour vérifier si l'utilisateur est connecté, puis autoriser l'accès à cette route.

  **1:45** Sinon, si un utilisateur n'est pas connecté, il obtiendra simplement une 404. C'est donc un moyen pratique d'ajouter une authentification à la route Mission Control, mais vous pouvez aussi ajouter l'authentification au contrôleur avec un peu de configuration si vous utilisez autre chose qui n'a pas ces assistants de routage. Mais je trouve ça vraiment pratique, car je n'ai pas besoin d'ajouter de configuration supplémentaire, c'est simplement fait ici dans mon fichier de routes. Une fois cela ajouté — nous n'avons pas Devise ici, donc on va laisser ça commenté — on peut maintenant accéder à la route jobs.

  **2:22** Vous voyez que j'ai déjà un peu joué avec ça. On a la queue par défaut, qui est bien sûr créée par défaut si vous n'avez pas spécifié les queues exactes que vous voulez. On a quelques jobs — j'ai joué avec ça il y a un mois avec Solid Queue —, mais on a aussi les jobs avec lesquels j'ai expérimenté un peu plus récemment. On voit ici les workers connectés. J'ai mon unique worker, configuré par Puma et en cours d'exécution ici, il est connecté.

  **2:52** Le dernier heartbeat remonte à une minute. On peut actualiser et voir si ça se met à jour. Mais voilà Mission Control Jobs. Si on devait exécuter un job dans notre console Rails ou autre, on peut dire console Rails, et on peut ouvrir, disons, j'ai quelques exemples ici, mais si on veut, disons, envoyer un mailer avec un post dedans, on peut le faire.

  **3:21** Ça va mettre le job en file d'attente, le mail delivery job. On obtient toutes ces informations là, puis on peut actualiser notre interface. On a un job en cours, cet Action Mailer, mail delivery job. En cours d'exécution par le worker 26 depuis moins de 20 secondes. Si on actualise ça, il devrait être terminé, et il a réussi, on le retrouve ici parmi les jobs terminés, ce qui est génial.

  **3:43** On peut cliquer sur les jobs et voir les arguments, l'ID du job, la queue dans laquelle il était, quand il a été mis en file d'attente, quand il a été terminé, et on peut voir toutes les données brutes le concernant. C'est donc pratique de voir vos arguments pour savoir quelle classe de job c'était, quel était l'ID, et quels étaient vos arguments. On a donc le user mailer, la méthode receipt, on a deliver\_now, on a les params. L'instance post a donc été convertie en Global ID. Ça sait aussi qu'on veut des symboles pour nos clés de hash, et comme on doit convertir ça en JSON pour le stocker dans la base de données, il y a une représentation spéciale pour ça, qui dit : à l'intérieur de params, il y a une clé post, mais on veut la reconvertir en symbole quand on la retransforme en hash Ruby.

  **4:34** Tout ça, ce sont des informations super utiles, et je voulais souligner que si on faisait une erreur dans notre user mailer, par exemple en référençant une variable qui n'existe pas, et qu'on retourne dans notre terminal pour réessayer, il faudra peut-être recharger. Le job sera alors sans doute mis en file d'attente avec la logique erronée. Et voilà, on a un nouveau job en échec. Il y a moins d'une minute, ce mail delivery job a échoué. Et ce qui est bien, c'est qu'il garde une trace des informations d'erreur : le type, le message et le backtrace.

  **5:14** On voit donc ici : user\_mailer, ligne 11, dans la méthode receipt, appel d'une variable locale ou méthode ASDF non définie, ce qui est exact, et on peut ensuite corriger ça puis relancer notre job. Malheureusement, la version actuelle de Mission Control a un bug, donc le retry ne fonctionne pas réellement pour l'instant. Ça sera corrigé très prochainement. Je l'ai déjà signalé, et on fera probablement aussi une pull request pour ça, mais ça sera corrigé très bientôt. Les autres choses à voir ici, il n'y en a pas énormément, mais si vous tapez jobs help dans votre console Rails, vous verrez cette sortie qui indique que vous pouvez vous connecter à un job server avec connect to, l'app ID et le server ID.

  **6:00** Et ça affichera des exemples plus bas. Ce qui manque — j'ai déjà fait une pull request pour ça — ce sont les guillemets doubles autour de ça. Ça m'a un peu troublé. Et il n'est apparemment pas nécessaire d'avoir l'app ID et les deux-points devant, ou le server ID. C'est probablement l'app ID ici, mais vous pouvez écrire connect to avec des guillemets doubles et lui donner une chaîne, en utilisant l'un des job servers disponibles listés ici — ça liste, d'après ce que je peux voir, tous les job servers connectés à votre base de données.

  **6:32** Et ensuite vous serez connecté, vous pourrez alors dire ActiveJob.jobs et voir les jobs chargés depuis la base de données. On n'a pas de jobs en cours d'exécution ou quoi que ce soit en ce moment, donc ça ne renvoie essentiellement rien, mais vous pourrez y accéder, et il y a tout un tas de choses que vous pouvez appeler pour visualiser ces jobs. C'est en quelque sorte l'interface qu'il utilise pour récupérer ça pour l'UI, car ça prend en charge Resque, qui stocke les choses dans Redis, alors que Solid Queue les stocke dans votre base de données. Il a donc un backend flexible pour communiquer avec ces différents services. On peut donc imaginer qu'on aura probablement, à terme, une intégration pour Sidekiq, GoodJob, Que et tous les autres qui existent.

  **7:23** Ils n'auront qu'à s'interfacer avec ça, et ce sera à peu près tout. C'est donc cool, vous pouvez accéder aux jobs en échec, si on veut faire ça et l'entrer ici pour les voir, puis essentiellement interroger les jobs en attente pour une queue spécifique, et ainsi de suite. C'est donc plutôt génial. Il y a beaucoup de choses que vous pourriez vouloir faire avec ça, mais en général, vous n'aurez probablement pas à trop y toucher. L'interface est vraiment le cœur de Mission Control Jobs ici.

  **7:57** Ouais, du coup je me demande, si on ouvrait la console Rails et qu'on appelait simplement ActiveJob.jobs, qu'est-ce qui se passerait ? Je suppose que cette méthode est définie quand on se connecte à l'un des job servers. C'est probablement pour ça, ou si on tape jobs help. Ouais, donc une méthode définie, elle doit être définie dynamiquement quand on se connecte. C'est plutôt cool, ça.

  **8:28** Je ne sais pas s'il y a une méthode disconnect. Je suppose qu'il y en a une, mais peut-être qu'elle n'est simplement pas documentée ou autre, mais c'est plutôt cool. Un petit assistant pratique pour accéder à vos job servers et creuser un peu le sujet. Voilà donc pour Mission Control Jobs. Je suppose que c'est un indice qu'il y aura beaucoup plus de choses intéressantes pour Mission Control à l'avenir, mais voilà ce qui a été annoncé, et ça fonctionne très bien.

  **8:53** J'adore utiliser Solid Queue pour stocker mes jobs côté backend, mais je n'ai même pas à me soucier de gérer un autre processus grâce au plugin Puma. Ça va être une excellente façon d'exécuter des jobs à l'avenir dans vos applications sans service séparé, puisque ça profite simplement de la base de données que vous utilisez déjà. Voilà donc pour cet épisode. Il n'y a vraiment pas grand-chose de plus. C'est tout.

  **9:21** Vous avez accès pour communiquer avec les jobs ActiveJob si vous le souhaitez, mais c'est à peu près tout. C'est une chouette petite interface. Vous pouvez l'ajouter à vos applications Rails dès aujourd'hui. Voilà donc, c'est tout. Si vous avez des questions, dites-le-nous dans les commentaires ci-dessous, et on se retrouve dans la prochaine vidéo.

  **9:37** À plus.
</Accordion>

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

* [Solid Queue](/tutorials/ruby/solid-queue)
* [Regrouper des jobs en arrière-plan](/tutorials/ruby/batching-background-jobs)

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

Ce tutoriel résume un screencast GoRails sur Mission Control Jobs, par Chris Oliver. Le screencast est l'œuvre originale. GoRails le publie, ainsi que le reste de la série, sur [gorails.com](https://gorails.com).
