active_job_basics.md 9.1 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(*guests)
74 75 76 77 78
    # Do something later
  end
end
```

79 80
Note that you can define `perform` with as many arguments as you want.

81 82 83 84 85
### Enqueue the Job

Enqueue a job like so:

```ruby
86
# Enqueue a job to be performed as soon the queuing system is
87
# free.
88
GuestsCleanupJob.perform_later guest
89 90 91
```

```ruby
92
# Enqueue a job to be performed tomorrow at noon.
93
GuestsCleanupJob.set(wait_until: Date.tomorrow.noon).perform_later(guest)
94 95 96
```

```ruby
97
# Enqueue a job to be performed 1 week from now.
98
GuestsCleanupJob.set(wait: 1.week).perform_later(guest)
99 100
```

101 102 103 104 105
```ruby
# `perform_now` and `perform_later` will call `perform` under the hood so
# you can pass as many arguments as defined in the latter.
GuestsCleanupJob.perform_later(guest1, guest2, filter: 'some_filter')
```
106

107
That's it!
108 109 110 111

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

112 113 114 115
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.
116 117 118

### Backends

119
Active Job has built-in adapters for multiple queuing backends (Sidekiq,
120 121 122
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).

123
### Setting the Backend
124

125
You can easily set your queuing backend:
126 127

```ruby
128 129 130
# config/application.rb
module YourApp
  class Application < Rails::Application
131 132 133
    # Be sure to have the adapter's gem in your Gemfile
    # and follow the adapter's specific installation
    # and deployment instructions.
134 135 136
    config.active_job.queue_adapter = :sidekiq
  end
end
137 138
```

139 140 141 142
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.
143

144 145 146
Queues
------

147
Most of the adapters support multiple queues. With Active Job you can schedule
148
the job to run on a specific queue:
149 150 151

```ruby
class GuestsCleanupJob < ActiveJob::Base
152
  queue_as :low_priority
153 154 155 156
  #....
end
```

C
Cristian Bica 已提交
157
You can prefix the queue name for all your jobs using
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
`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 已提交
174
# Now your job will run on queue production_low_priority on your
175 176
# production environment and on staging_low_priority
# on your staging environment
177 178
```

179
The default queue name prefix delimiter is '\_'.  This can be changed by setting
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
`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
198 199
# production environment and on staging.low_priority
# on your staging environment
200 201
```

202 203
If you want more control on what queue a job will be run you can pass a `:queue`
option to `#set`:
C
Cristian Bica 已提交
204 205 206 207 208

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

209 210
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 已提交
211 212 213 214 215 216 217 218 219 220 221 222 223 224
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)
225
    # Do process video
C
Cristian Bica 已提交
226 227 228 229 230 231
  end
end

ProcessVideoJob.perform_later(Video.last)
```

232
NOTE: Make sure your queuing backend "listens" on your queue name. For some
233
backends you need to specify the queues to listen to.
234 235 236 237 238


Callbacks
---------

239 240
Active Job provides hooks during the life cycle of a job. Callbacks allow you to
trigger logic during the life cycle of a job.
241 242 243

### Available callbacks

244 245 246 247 248 249
* `before_enqueue`
* `around_enqueue`
* `after_enqueue`
* `before_perform`
* `around_perform`
* `after_perform`
250 251 252 253 254 255 256 257

### Usage

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

  before_enqueue do |job|
258
    # Do something with the job instance
259 260 261
  end

  around_perform do |job, block|
262
    # Do something before perform
263
    block.call
264
    # Do something after perform
265 266 267 268 269 270
  end

  def perform
    # Do something later
  end
end
271 272
```

273

G
Guo Xiang Tan 已提交
274
Action Mailer
275
------------
276

277 278
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
279
is integrated with Action Mailer so you can easily send emails asynchronously:
280 281

```ruby
282 283
# If you want to send the email now use #deliver_now
UserMailer.welcome(@user).deliver_now
284

X
Xavier Noria 已提交
285
# If you want to send the email through Active Job use #deliver_later
286
UserMailer.welcome(@user).deliver_later
287 288
```

289

J
Johannes Opper 已提交
290 291 292 293 294 295 296 297 298
Internationalization
--------------------

Each job uses the `I18n.locale` set when the job was created. Useful if you send
emails asynchronously:

```ruby
I18n.locale = :eo

M
Miguel Parramon 已提交
299
UserMailer.welcome(@user).deliver_later # Email will be localized to Esperanto.
J
Johannes Opper 已提交
300 301 302
```


303 304
GlobalID
--------
305

306 307 308
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:
309 310

```ruby
311
class TrashableCleanupJob < ActiveJob::Base
312 313 314 315 316 317 318 319 320 321
  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
322
class TrashableCleanupJob < ActiveJob::Base
323 324 325 326 327 328
  def perform(trashable, depth)
    trashable.cleanup(depth)
  end
end
```

329
This works with any class that mixes in `GlobalID::Identification`, which
330
by default has been mixed into Active Record classes.
331 332 333 334


Exceptions
----------
335

336 337 338 339 340 341 342
Active Job provides a way to catch exceptions raised during the execution of the
job:

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

343
  rescue_from(ActiveRecord::RecordNotFound) do |exception|
344
   # Do something with the exception
345 346 347 348 349 350 351
  end

  def perform
    # Do something later
  end
end
```
352

353 354 355 356 357 358 359
### Deserialization

GlobalID allows serializing full Active Record objects passed to `#perform`.

If a passed record is deleted after the job is enqueued but before the `#perform`
method is called Active Job will raise an `ActiveJob::DeserializationError`
exception.
360 361 362 363 364 365

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

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