Mission Control: The Web UI

Contents

Rocket Job Mission Control is the web interface for managing and monitoring Rocket Job. It is where operators watch what the cluster is doing, retry or abort work, drain servers for a deploy, inspect and repair failed batch jobs, and manage directory monitoring entries, all without writing or deploying any code.

Running jobs

It ships as a separate gem, rocketjob_mission_control, so the web UI and its Rails dependencies are not loaded everywhere Rocket Job jobs are defined or run. Workers stay lean, and the console runs wherever it is convenient to host a small Rails app.

A quick orientation

If you are new to Rocket Job, a handful of terms show up throughout this interface:

The top navigation bar mirrors these concepts with four sections: Jobs, Servers, Workers, and Directory Monitoring. The rest of this page walks through each one.

Installation

Mission Control is a Rails engine. Mount it into a new or existing Rails application.

Add it to the Gemfile:

gem "rocketjob_mission_control", "~> 6.0"

Install it:

bundle install

Mount the engine in config/routes.rb, pointing it at whatever path you want it served from:

Rails.application.routes.draw do
  mount RocketJobMissionControl::Engine => "rocketjob"
end

The application that hosts Mission Control needs the same Mongoid configuration as the rest of the cluster (the rocketjob and rocketjob_slices clients) so that it reads and writes the same jobs and slices. See Installation for the Mongoid setup.

Because it only needs railties and not a full Rails stack, Mission Control can be mounted into a minimal app dedicated to operations. It exposes destructive actions (aborting jobs, stopping servers, editing slice data), so put it behind your own authentication and authorization before exposing it outside a trusted network. The next section covers the built-in authorization.

Access control

Mission Control authenticates nothing itself; the host app is responsible for identifying the user. What Mission Control does provide is fine-grained authorization: deciding which of its actions a given user is allowed to perform.

Authorization is built on the access-granted gem, a lightweight, policy-based, role-driven authorization library. A few of its concepts are worth knowing before tuning the rules:

The default roles

Mission Control defines seven roles over the RocketJob::Job, RocketJob::DirmonEntry, RocketJob::Server, and RocketJob::Worker documents:

Role Grants
admin Create and destroy jobs; destroy Dirmon entries.
editor View, edit, and update the data inside a batch job’s slices, including encrypted records.
operator Stop, kill, pause, resume, and destroy servers; destroy zombies; request thread dumps.
manager Edit, pause, resume, retry, abort, fail, update, and run jobs now.
dirmon Create, edit, update, copy, replicate, enable, and disable Dirmon entries.
user Edit, pause, resume, retry, abort, and update jobs, but only the user’s own jobs (where the job’s login matches).
view Read-only access to jobs, Dirmon entries, servers, and workers.

Roles are hierarchical. They are ordered from most to least privileged (admin, editor, operator, manager, dirmon, user, view), and granting a role also grants every role below it. So an admin can do everything, a manager also gets dirmon, user, and view permissions, and a view user gets read-only access and nothing more.

The user role is special: its permissions are scoped by a login, so a user granted that role can only act on jobs whose login matches their own. Supply the current user’s login from the configuration callback described below.

When no authorization is configured, every request is treated as a full admin. This keeps development friction-free, and makes wiring up your own authorization an opt-in step. Configure it before exposing Mission Control beyond a trusted network.

Configuration

Authorization is wired up with a single callback in an initializer in the host application, for example config/initializers/rocket_job_mission_control.rb:

RocketJobMissionControl::Engine.config.rocket_job_mission_control.authorization_callback =
  lambda do
    {
      roles: current_user.rocket_job_roles, # e.g. [:manager, :dirmon]
      login: current_user.email
    }
  end

The callback:

Supplying an unknown role name raises an ArgumentError, so typos surface immediately rather than silently granting or denying access.

To change the permissions themselves rather than who receives which role, supply your own policy class. Define a class that includes AccessGranted::Policy (subclassing the shipped RocketJobMissionControl::AccessPolicy is the easiest starting point) and point the engine at it:

class MyAccessPolicy < RocketJobMissionControl::AccessPolicy
  def configure
    super # keep the built-in roles, then add or override below

    role :auditor do
      can :read, RocketJob::Job
    end
  end
end

RocketJobMissionControl::Engine.config.rocket_job_mission_control.access_policy_class =
  MyAccessPolicy

access_policy_class accepts either the class itself or a String naming it (for example "MyAccessPolicy"), which avoids autoloading the class too early from an initializer. When it is not set, Mission Control uses RocketJobMissionControl::AccessPolicy. Because access-granted is a plain authorization library, the same role/can API shown in the shipped policy is all a custom policy needs.

Monitoring jobs

The interface opens on the list of running jobs, newest first. Each entry shows:

Jobs are grouped by state, each on its own tab in the left sidebar:

The All tab lists every job regardless of state, with a search box for finding a specific job by class or description. The valid actions for each job (Retry, Pause, Destroy, and so on) are available inline, so common operations can be done without opening the job.

All jobs

Managing a job

Select any job to open its detail page. It shows the job’s current state, its fields and timing, and the actions that are valid for its current state. For a batch job, the Details panel also shows the record count, progress, estimated time remaining, and a Slices bar that breaks the work down into queued, active, failed, and completed slices at a glance.

Job detail

The available actions are:

A failed job shows the captured exception so the cause can be diagnosed before retrying. The Slices bar turns solid red when every slice has failed, which is a quick signal that the failure is systemic rather than caused by a few bad records.

Failed job detail

Recovering a failed batch job

When a batch job fails, the problem is often a handful of bad records rather than the whole data set. Because each slice fails independently, Mission Control lets you find the offending records, fix or remove them, and retry, all without rerunning the records that already succeeded.

The failed job’s detail page includes an Exceptions section that groups the failures by exception class and lists the messages raised. This is the fastest way to tell whether every slice failed for the same reason or for several different ones.

Exceptions summary

Select an exception class to open its detail page. It shows the class name, message, and the record number that triggered the failure, along with the backtrace (with a Full Backtrace toggle for the complete stack). The arrows at the top step through the other failed exceptions on the job.

Exception detail

Select View Slice to see the actual records in the failed slice. The record that raised the exception is highlighted, and every record has an Edit button.

View slice

Editing a record opens it in a text area. Correct the data and select Save to put the repaired record back into the slice, or select Delete to drop a record that should not be processed at all. Unprintable bytes are shown as \xHH escapes (for example a null byte as \x00) and are converted back to the original bytes when saved, so binary and oddly encoded data can be edited safely.

Edit slice

Once the bad records are fixed or removed, Retry the job. Only the previously failed and unprocessed slices run again, so the job finishes without redoing the records that already succeeded.

Monitoring workers

The Workers view lists every active worker across the cluster and the job each one is currently processing. Each row shows the worker name (in hostname:pid:worker-NNN form), the job class it is running (linked to that job), the job’s description, and how long the worker has been on its current work. It is the quickest way to see whether the cluster is busy and where its capacity is going right now.

Active workers

Because a single server runs many workers, the same server name appears on several rows when it is processing several jobs at once. Use the refresh control at the top right to re-read the current state.

Managing servers

The Servers view lists the running Rocket Job server processes (each Server is one running process; see Architecture), grouped in the sidebar by state: starting, running, paused, stopping, and zombie. Each row shows the hostname and PID, the current and maximum worker counts, when the server started, and when it last checked in.

Servers

Servers can be controlled from here without shell access to the hosts:

These actions are delivered to the servers over Rocket Job’s MongoDB-backed pub/sub mechanism, so they take effect across every process in the cluster without a separate message broker.

Zombie servers

Every server writes a periodic heartbeat. A server that stops checking in, usually because its process was terminated abnormally rather than shut down gracefully, is flagged as a zombie so dead processes can be spotted and cleaned up.

Zombie servers

Zombies matter because a job that was being processed by a server that died would otherwise sit stalled. Rocket Job’s HousekeepingJob watches for zombie servers and re-queues any jobs they were processing so another worker can pick them up. You can also clear them by hand: Destroy zombies removes all of them at once, or use the per-row action to destroy a single one.

Managing directory monitors

Mission Control includes a full management screen for Dirmon, Rocket Job’s directory monitor. A Dirmon entry watches a path pattern and starts a job whenever a matching file appears, so the files and schedules a system reacts to can be changed from the web UI without a code deploy.

The Directory Monitoring view lists every entry with its name, job class, and the file pattern it watches. Entries are grouped in the sidebar by state (pending, enabled, failed, disabled), and each entry’s name is colored and icon-tagged by its state.

All Dirmon entries

Select Create to add a new entry. At minimum an entry needs a name, the job class to start, and a pattern (a glob such as input_files/orders/*.{csv,txt}) identifying the files to watch. An optional archive directory tells Rocket Job where to move each file after it has been queued. The properties button reveals the job’s own fields so their default values can be set on the entry.

Create a Dirmon entry

Selecting an entry opens its detail page, showing its current state, job class, pattern, archive directory, and any job properties. From here an entry can be enabled or disabled, edited, copied to a new entry, or destroyed.

Dirmon entry detail

Editing works the same way as creating, on the same form. A failed entry, one whose last run raised an error, can be corrected here and re-enabled in place.

Edit a Dirmon entry

See the Dirmon guide for what each field controls and how patterns and archiving behave.