active_job_basics.md 8.3 KB
Newer Older
1
**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
X
Xavier Noria 已提交
2

3 4 5 6
Active Job Basics
=================

This guide provides you with all you need to get started in creating,
7
enqueuing and executing background jobs.
8 9 10 11 12 13

After reading this guide, you will know:

* How to create jobs.
* How to enqueue jobs.
* How to run jobs in the background.
14
* How to send emails from your application asynchronously.
15 16 17

--------------------------------------------------------------------------------

18

19 20 21 22
Introduction
------------

Active Job is a framework for declaring jobs and making them run on a variety
23
of queuing backends. These jobs can be everything from regularly scheduled
24
clean-ups, to billing charges, to mailings. Anything that can be chopped up
25 26 27
into small units of work and run in parallel, really.


28
The Purpose of Active Job
29 30
-----------------------------
The main point is to ensure that all Rails apps will have a job infrastructure
31 32 33 34 35 36 37 38
in place. We can then have framework features and other gems build on top of that,
without having to worry about API differences between various job runners such as
Delayed Job and Resque. Picking your queuing backend becomes more of an operational
concern, then. And you'll be able to switch between them without having to rewrite
your jobs.

NOTE: Rails by default comes with an "immediate runner" queuing implementation.
That means that each job that has been enqueued will run immediately.
39 40 41 42 43


Creating a Job
--------------

44
This section will provide a step-by-step guide to creating a job and enqueuing it.
45 46 47

### Create the Job

C
Cristian Bica 已提交
48
Active Job provides a Rails generator to create jobs. The following will create a
49
job in `app/jobs` (with an attached test case under `test/jobs`):
C
Cristian Bica 已提交
50

51 52
```bash
$ bin/rails generate job guests_cleanup
53 54
invoke  test_unit
create    test/jobs/guests_cleanup_job_test.rb
55 56 57
create  app/jobs/guests_cleanup_job.rb
```

C
Cristian Bica 已提交
58 59 60 61 62 63 64
You can also create a job that will run on a specific queue:

```bash
$ bin/rails generate job guests_cleanup --queue urgent
```

If you don't want to use a generator, you could create your own file inside of
65
`app/jobs`, just make sure that it inherits from `ActiveJob::Base`.
66

67
Here's what a job looks like:
68 69 70 71 72

```ruby
class GuestsCleanupJob < ActiveJob::Base
  queue_as :default

73
  def perform(*args)
74 75 76 77 78 79 80 81 82 83
    # Do something later
  end
end
```

### Enqueue the Job

Enqueue a job like so:

```ruby
84
# Enqueue a job to be performed as soon the queuing system is
85
# free.
86
MyJob.perform_later record
87 88 89
```

```ruby
90 91
# Enqueue a job to be performed tomorrow at noon.
MyJob.set(wait_until: Date.tomorrow.noon).perform_later(record)
92 93 94
```

```ruby
95 96
# Enqueue a job to be performed 1 week from now.
MyJob.set(wait: 1.week).perform_later(record)
97 98 99 100 101 102 103 104
```

That's it!


Job Execution
-------------

105 106 107 108
For enqueuing and executing jobs you need to set up a queuing backend, that is to
say you need to decide for a 3rd-party queuing library that Rails should use.
Rails itself does not provide a sophisticated queuing system and just executes the
job immediately if no adapter is set.
109 110 111

### Backends

112
Active Job has built-in adapters for multiple queuing backends (Sidekiq,
113 114 115
Resque, Delayed Job and others). To get an up-to-date list of the adapters
see the API Documentation for [ActiveJob::QueueAdapters](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html).

116
### Setting the Backend
117

118
You can easily set your queuing backend:
119 120

```ruby
121 122 123
# config/application.rb
module YourApp
  class Application < Rails::Application
124 125 126
    # Be sure to have the adapter's gem in your Gemfile
    # and follow the adapter's specific installation
    # and deployment instructions.
127 128 129
    config.active_job.queue_adapter = :sidekiq
  end
end
130 131
```

132 133 134 135
NOTE: Since jobs run in parallel to your Rails application, most queuing libraries
require that you start a library-specific queuing service (in addition to
starting your Rails app) for the job processing to work. For information on
how to do that refer to the documentation of your respective library.
136

137 138 139
Queues
------

140
Most of the adapters support multiple queues. With Active Job you can schedule
141
the job to run on a specific queue:
142 143 144

```ruby
class GuestsCleanupJob < ActiveJob::Base
145
  queue_as :low_priority
146 147 148 149
  #....
end
```

C
Cristian Bica 已提交
150
You can prefix the queue name for all your jobs using
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
`config.active_job.queue_name_prefix` in `application.rb`:

```ruby
# config/application.rb
module YourApp
  class Application < Rails::Application
    config.active_job.queue_name_prefix = Rails.env
  end
end

# app/jobs/guests_cleanup.rb
class GuestsCleanupJob < ActiveJob::Base
  queue_as :low_priority
  #....
end

C
Cristian Bica 已提交
167
# Now your job will run on queue production_low_priority on your
168 169
# production environment and on staging_low_priority
# on your staging environment
170 171
```

172
The default queue name prefix delimiter is '\_'.  This can be changed by setting
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
`config.active_job.queue_name_delimiter` in `application.rb`:

```ruby
# config/application.rb
module YourApp
  class Application < Rails::Application
    config.active_job.queue_name_prefix = Rails.env
    config.active_job.queue_name_delimiter = '.'
  end
end

# app/jobs/guests_cleanup.rb
class GuestsCleanupJob < ActiveJob::Base
  queue_as :low_priority
  #....
end

# Now your job will run on queue production.low_priority on your
191 192
# production environment and on staging.low_priority
# on your staging environment
193 194
```

195 196
If you want more control on what queue a job will be run you can pass a `:queue`
option to `#set`:
C
Cristian Bica 已提交
197 198 199 200 201

```ruby
MyJob.set(queue: :another_queue).perform_later(record)
```

202 203
To control the queue from the job level you can pass a block to `#queue_as`. The
block will be executed in the job context (so you can access `self.arguments`)
C
Cristian Bica 已提交
204 205 206 207 208 209 210 211 212 213 214 215 216 217
and you must return the queue name:

```ruby
class ProcessVideoJob < ActiveJob::Base
  queue_as do
    video = self.arguments.first
    if video.owner.premium?
      :premium_videojobs
    else
      :videojobs
    end
  end

  def perform(video)
218
    # Do process video
C
Cristian Bica 已提交
219 220 221 222 223 224
  end
end

ProcessVideoJob.perform_later(Video.last)
```

225
NOTE: Make sure your queuing backend "listens" on your queue name. For some
226
backends you need to specify the queues to listen to.
227 228 229 230 231


Callbacks
---------

232 233
Active Job provides hooks during the life cycle of a job. Callbacks allow you to
trigger logic during the life cycle of a job.
234 235 236

### Available callbacks

237 238 239 240 241 242
* `before_enqueue`
* `around_enqueue`
* `after_enqueue`
* `before_perform`
* `around_perform`
* `after_perform`
243 244 245 246 247 248 249 250

### Usage

```ruby
class GuestsCleanupJob < ActiveJob::Base
  queue_as :default

  before_enqueue do |job|
251
    # Do something with the job instance
252 253 254
  end

  around_perform do |job, block|
255
    # Do something before perform
256
    block.call
257
    # Do something after perform
258 259 260 261 262 263
  end

  def perform
    # Do something later
  end
end
264 265
```

266

G
Guo Xiang Tan 已提交
267
Action Mailer
268
------------
269

270 271
One of the most common jobs in a modern web application is sending emails outside
of the request-response cycle, so the user doesn't have to wait on it. Active Job
272
is integrated with Action Mailer so you can easily send emails asynchronously:
273 274

```ruby
275 276
# If you want to send the email now use #deliver_now
UserMailer.welcome(@user).deliver_now
277

X
Xavier Noria 已提交
278
# If you want to send the email through Active Job use #deliver_later
279
UserMailer.welcome(@user).deliver_later
280 281
```

282

283 284
GlobalID
--------
285

286 287 288
Active Job supports GlobalID for parameters. This makes it possible to pass live
Active Record objects to your job instead of class/id pairs, which you then have
to manually deserialize. Before, jobs would look like this:
289 290

```ruby
291
class TrashableCleanupJob < ActiveJob::Base
292 293 294 295 296 297 298 299 300 301
  def perform(trashable_class, trashable_id, depth)
    trashable = trashable_class.constantize.find(trashable_id)
    trashable.cleanup(depth)
  end
end
```

Now you can simply do:

```ruby
302
class TrashableCleanupJob < ActiveJob::Base
303 304 305 306 307 308
  def perform(trashable, depth)
    trashable.cleanup(depth)
  end
end
```

309
This works with any class that mixes in `GlobalID::Identification`, which
310
by default has been mixed into Active Record classes.
311 312 313 314


Exceptions
----------
315

316 317 318 319 320 321 322
Active Job provides a way to catch exceptions raised during the execution of the
job:

```ruby
class GuestsCleanupJob < ActiveJob::Base
  queue_as :default

323
  rescue_from(ActiveRecord::RecordNotFound) do |exception|
324
   # Do something with the exception
325 326 327 328 329 330 331
  end

  def perform
    # Do something later
  end
end
```
332 333 334 335 336 337 338


Job Testing
--------------

You can find detailed instructions on how to test your jobs in the
[testing guide](testing.html#testing-jobs).