que 1.0.0.beta3 → 1.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,27 +0,0 @@
1
- ## Job Helper Methods
2
-
3
- There are a number of instance methods on Que::Job that you can use in your jobs, preferably in transactions. See [Writing Reliable Jobs](/writing_reliable_jobs.md) for more information on where to use these methods.
4
-
5
- ### destroy
6
-
7
- This method deletes the job from the queue table, ensuring that it won't be worked a second time.
8
-
9
- ### finish
10
-
11
- This method marks the current job as finished, ensuring that it won't be worked a second time. This is like destroy, in that it finalizes a job, but this method leaves the job in the table, in case you want to query it later.
12
-
13
- ### expire
14
-
15
- This method marks the current job as expired. It will be left in the table and won't be retried, but it will be easy to query for expired jobs. This method is called if the job exceeds its maximum_retry_count.
16
-
17
- ### retry_in
18
-
19
- This method marks the current job to be retried later. You can pass a numeric to this method, in which case that is the number of seconds after which it can be retried (`retry_in(10)`, `retry_in(0.5)`), or, if you're using ActiveSupport, you can pass in a duration object (`retry_in(10.minutes)`). This automatically happens, with an exponentially-increasing interval, when the job encounters an error.
20
-
21
- ### error_count
22
-
23
- This method returns the total number of times the job has errored, in case you want to modify the job's behavior after it has failed a given number of times.
24
-
25
- ### default_resolve_action
26
-
27
- If you don't perform a resolve action (destroy, finish, expire, retry_in) while the job is worked, Que will call this method for you. By default it simply calls `destroy`, but you can override it in your Job subclasses if you wish - for example, to call `finish`, or to invoke some more complicated logic.
data/docs/logging.md DELETED
@@ -1,62 +0,0 @@
1
- ## Logging
2
-
3
- By default, Que logs important information in JSON to either Rails' logger (when running in a Rails web process) or STDOUT (when running via the `que` executable). So, your logs will look something like:
4
-
5
- ```
6
- I, [2017-08-12T05:07:31.094201 #4687] INFO -- : {"lib":"que","hostname":"lovelace","pid":21626,"thread":21471100,"event":"job_worked","job_id":6157665,"elapsed":0.531411}
7
- ```
8
-
9
- Of course you can have it log wherever you like:
10
-
11
- ```ruby
12
- Que.logger = Logger.new(...)
13
- ```
14
-
15
- If you don't like logging in JSON, you can also customize the format of the logging output by passing a callable object (such as a proc) to Que.log_formatter=. The proc should take a hash (the keys are symbols) and return a string. The keys and values are just as you would expect from the JSON output:
16
-
17
- ```ruby
18
- Que.log_formatter = proc do |data|
19
- "Thread number #{data[:thread]} experienced a #{data[:event]}"
20
- end
21
- ```
22
-
23
- If the log formatter returns nil or false, nothing will be logged at all. You could use this to narrow down what you want to emit, for example:
24
-
25
- ```ruby
26
- Que.log_formatter = proc do |data|
27
- if [:job_worked, :job_unavailable].include?(data[:event])
28
- JSON.dump(data)
29
- end
30
- end
31
- ```
32
-
33
- ## Logging Job Completion
34
-
35
- Que logs a `job_worked` event whenever a job completes, though by default this event is logged at the `DEBUG` level. Since people often run their applications at the `INFO` level or above, this can make the logs too silent for some use cases. Similarly, you may want to log at a higher level if a time-sensitive job begins taking too long to run.
36
-
37
- You can solve these problems by configuring the level at which a job is logged on a per-job basis. Simply define a `log_level` method in your job class - it will be called with a float representing the number of seconds it took for the job to run, and it should return a symbol indicating what level to log the job at:
38
-
39
- ```ruby
40
- class TimeSensitiveJob < Que::Job
41
- def run(*args)
42
- RemoteAPI.execute_important_request
43
- end
44
-
45
- def log_level(elapsed)
46
- if elapsed > 60
47
- # This job took over a minute! We should complain about it!
48
- :warn
49
- elsif elapsed > 30
50
- # A little long, but no big deal!
51
- :info
52
- else
53
- # This is fine, don't bother logging at all.
54
- false
55
- end
56
- end
57
- end
58
- ```
59
-
60
- This method should return a symbol that is a valid logging level (one of `[:debug, :info, :warn, :error, :fatal, :unknown]`). If the method returns anything other than one of these symbols, the job won't be logged.
61
-
62
- If a job errors, a `job_errored` event will be emitted at the `ERROR` log level. This is not currently configurable.
@@ -1,25 +0,0 @@
1
- ## Managing Workers
2
-
3
- Que uses a multithreaded pool of workers to run jobs in parallel - this allows you to save memory by working many jobs simultaneously in the same process. The `que` executable starts up a pool of 6 workers by default. This is fine for most use cases, but the ideal number for your app will depend on your interpreter and what types of jobs you're running.
4
-
5
- Ruby MRI has a global interpreter lock (GIL), which prevents it from using more than one CPU core at a time. Having multiple workers running makes sense if your jobs tend to spend a lot of time in I/O (waiting on complex database queries, sending emails, making HTTP requests, etc.), as most jobs do. However, if your jobs are doing a lot of work in Ruby, they'll be spending a lot of time blocking each other, and having too many workers running will cause you to lose efficiency to context-switching. So, you'll want to choose the appropriate number of workers for your use case.
6
-
7
- ### Working Jobs Via Executable
8
-
9
- ```shell
10
- # Run a pool of 6 workers:
11
- que
12
-
13
- # Or configure the number of workers:
14
- que --worker-count 10
15
- ```
16
-
17
- See `que -h` for a complete list of command-line options.
18
-
19
- ### Thread-Unsafe Application Code
20
-
21
- If your application code is not thread-safe, you won't want any workers to be processing jobs while anything else is happening in the Ruby process. So, you'll want to run a single worker at a time, like so:
22
-
23
- ```shell
24
- que --worker-count 1
25
- ```
data/docs/middleware.md DELETED
@@ -1,36 +0,0 @@
1
- ## Middleware
2
-
3
- A new feature in 1.0 is support for custom middleware around various actions.
4
-
5
- This API is experimental for the 1.0 beta and may change.
6
-
7
- ### Defining Middleware For Jobs
8
-
9
- You can define middleware to wrap worked jobs. You can use this to add custom instrumentation around jobs, log how long they take to complete, etc.
10
-
11
- ``` ruby
12
- Que.job_middleware.push(
13
- -> (job, &block) {
14
- # Do stuff with the job object - report on it, count time elapsed, etc.
15
- block.call
16
- nil # Doesn't matter what's returned.
17
- }
18
- )
19
- ```
20
-
21
- ### Defining Middleware For SQL statements
22
-
23
- SQL middleware wraps queries that Que executes, or which you might decide to execute via Que.execute(). You can use hook this into NewRelic or a similar service to instrument how long SQL queries take, for example.
24
-
25
- ``` ruby
26
- Que.sql_middleware.push(
27
- -> (sql, params, &block) {
28
- Service.instrument(sql: sql, params: params) do
29
- block.call
30
- end
31
- nil # Still doesn't matter what's returned.
32
- }
33
- )
34
- ```
35
-
36
- Please be careful with what you do inside an SQL middleware - this code will execute inside Que's locking thread, which runs in a fairly tight loop that is optimized for performance. If you do something inside this block that incurs blocking I/O (like synchronously touching an external service) you may find Que being less able to pick up jobs quickly.
data/docs/migrating.md DELETED
@@ -1,27 +0,0 @@
1
- ## Migrating
2
-
3
- Some new releases of Que may require updates to the database schema. It's recommended that you integrate these updates alongside your other database migrations. For example, when Que released version 0.6.0, the schema version was updated from 2 to 3. If you're running ActiveRecord, you could make a migration to perform this upgrade like so:
4
-
5
- ```ruby
6
- class UpdateQue < ActiveRecord::Migration[5.0]
7
- def self.up
8
- Que.migrate! version: 3
9
- end
10
-
11
- def self.down
12
- Que.migrate! version: 2
13
- end
14
- end
15
- ```
16
-
17
- This will make sure that your database schema stays consistent with your codebase. If you're looking for something quicker and dirtier, you can always manually migrate in a console session:
18
-
19
- ```ruby
20
- # Change schema to version 3.
21
- Que.migrate! version: 3
22
-
23
- # Check your current schema version.
24
- Que.db_version #=> 3
25
- ```
26
-
27
- Note that you can remove Que from your database completely by migrating to version 0.
@@ -1,31 +0,0 @@
1
- ## Multiple Queues
2
-
3
- Que supports the use of multiple queues in a single job table. Please note that this feature is intended to support the case where multiple codebases are sharing the same job queue - if you want to support jobs of differing priorities, the numeric priority system offers better flexibility and performance.
4
-
5
- For instance, you might have a separate Ruby application that handles only processing credit cards. In that case, you can run that application's workers against a specific queue:
6
-
7
- ```shell
8
- que --queue-name credit_cards
9
- # The -q flag is equivalent, and either can be passed multiple times.
10
- que -q default -q credit_cards
11
- ```
12
-
13
- Then you can set jobs to be enqueued in that queue specifically:
14
-
15
- ```ruby
16
- ProcessCreditCard.enqueue current_user.id, queue: 'credit_cards'
17
-
18
- # Or:
19
-
20
- class ProcessCreditCard < Que::Job
21
- # Set a default queue for this job class; this can be overridden by
22
- # passing the :queue parameter to enqueue like above.
23
- self.queue = 'credit_cards'
24
- end
25
- ```
26
-
27
- In some cases, the ProcessCreditCard class may not be defined in the application that is enqueueing the job. In that case, you can specify the job class as a string:
28
-
29
- ```ruby
30
- Que.enqueue current_user.id, job_class: 'ProcessCreditCard', queue: 'credit_cards'
31
- ```
@@ -1,7 +0,0 @@
1
- ## Shutting Down Safely
2
-
3
- To ensure safe operation, Que needs to be very careful in how it shuts down. When a Ruby process ends normally, it calls Thread#kill on any threads that are still running - unfortunately, if a thread is in the middle of a transaction when this happens, there is a risk that it will be prematurely commited, resulting in data corruption. See [here](http://blog.headius.com/2008/02/ruby-threadraise-threadkill-timeoutrb.html) and [here](http://coderrr.wordpress.com/2011/05/03/beware-of-threadkill-or-your-activerecord-transactions-are-in-danger-of-being-partially-committed/) for more detail on this.
4
-
5
- To prevent this, Que will block the worker process from exiting until all jobs it is working have completed normally. Unfortunately, if you have long-running jobs, this may take a very long time (and if something goes wrong with a job's logic, it may never happen). The solution in this case is SIGKILL - luckily, Ruby processes that are killed via SIGKILL will end without using Thread#kill on its running threads. This is safer than exiting normally - when PostgreSQL loses the connection it will simply roll back the open transaction, if any, and unlock the job so it can be retried later by another worker. Be sure to read [Writing Reliable Jobs](https://github.com/chanks/que/blob/master/docs/writing_reliable_jobs.md) for information on how to design your jobs to fail safely.
6
-
7
- So, be prepared to use SIGKILL on your Ruby processes if they run for too long. For example, Heroku takes a good approach to this - when Heroku's platform is shutting down a process, it sends SIGTERM, waits ten seconds, then sends SIGKILL if the process still hasn't exited. This is a nice compromise - it will give each of your currently running jobs ten seconds to complete, and any jobs that haven't finished by then will be interrupted and retried later.
@@ -1,65 +0,0 @@
1
- ## Using Plain Postgres Connections
2
-
3
- If you're not using an ORM like ActiveRecord or Sequel, you can use a distinct connection pool to manage your Postgres connections. Please be aware that if you **are** using ActiveRecord or Sequel, there's no reason for you to be using any of these methods - it's less efficient (unnecessary connections will waste memory on your database server) and you lose the reliability benefits of wrapping jobs in the same transactions as the rest of your data.
4
-
5
- ## Using ConnectionPool or Pond
6
-
7
- Support for two connection pool gems is included in Que. The first is the ConnectionPool gem (be sure to add `gem 'connection_pool'` to your Gemfile):
8
-
9
- ```ruby
10
- require 'uri'
11
- require 'pg'
12
- require 'connection_pool'
13
-
14
- uri = URI.parse(ENV['DATABASE_URL'])
15
-
16
- Que.connection = ConnectionPool.new(size: 10) do
17
- PG::Connection.open(
18
- host: uri.host,
19
- user: uri.user,
20
- password: uri.password,
21
- port: uri.port || 5432,
22
- dbname: uri.path[1..-1]
23
- )end
24
- ```
25
-
26
- Be sure to pick your pool size carefully - if you use 10 for the size, you'll incur the overhead of having 10 connections open to Postgres even if you never use more than a couple of them.
27
-
28
- The Pond gem doesn't have this drawback - it is very similar to ConnectionPool, but establishes connections lazily (add `gem 'pond'` to your Gemfile):
29
-
30
- ```ruby
31
- require 'uri'
32
- require 'pg'
33
- require 'pond'
34
-
35
- uri = URI.parse(ENV['DATABASE_URL'])
36
-
37
- Que.connection = Pond.new(maximum_size: 10) do
38
- PG::Connection.open(
39
- host: uri.host,
40
- user: uri.user,
41
- password: uri.password,
42
- port: uri.port || 5432,
43
- dbname: uri.path[1..-1]
44
- )
45
- end
46
- ```
47
-
48
- ## Using Any Other Connection Pool
49
-
50
- You can use any other in-process connection pool by defining access to it in a proc that's passed to `Que.connection_proc = proc`. The proc you pass should accept a block and call it with a connection object. For instance, Que's built-in interface to Sequel's connection pool is basically implemented like:
51
-
52
- ```ruby
53
- Que.connection_proc = proc do |&block|
54
- DB.synchronize do |connection|
55
- block.call(connection)
56
- end
57
- end
58
- ```
59
-
60
- This proc must meet a few requirements:
61
- - The yielded object must be an instance of `PG::Connection`.
62
- - It must be reentrant - if it is called with a block, and then called again inside that block, it must return the same object. For example, in `proc.call{|conn1| proc.call{|conn2| conn1.object_id == conn2.object_id}}` the innermost condition must be true.
63
- - It must lock the connection object and prevent any other thread from accessing it for the duration of the block.
64
-
65
- If any of these conditions aren't met, Que will raise an error.
data/docs/using_sequel.md DELETED
@@ -1,49 +0,0 @@
1
- ## Using Sequel
2
-
3
- If you're using Sequel, with or without Rails, you'll need to give Que a specific database instance to use:
4
-
5
- ```ruby
6
- DB = Sequel.connect(ENV['DATABASE_URL'])
7
- Que.connection = DB
8
- ```
9
-
10
- If you are using Sequel's migrator, your app initialization won't happen, so you may need to tweak your migrations to `require 'que'` and set its connection:
11
-
12
- ```ruby
13
- require 'que'
14
- Sequel.migration do
15
- up do
16
- Que.connection = self
17
- Que.migrate! :version => 3
18
- end
19
- down do
20
- Que.connection = self
21
- Que.migrate! :version => 0
22
- end
23
- end
24
- ```
25
-
26
- Then you can safely use the same database object to transactionally protect your jobs:
27
-
28
- ```ruby
29
- class MyJob < Que::Job
30
- def run(user_id:)
31
- # Do stuff.
32
-
33
- DB.transaction do
34
- # Make changes to the database.
35
-
36
- # Destroying this job will be protected by the same transaction.
37
- destroy
38
- end
39
- end
40
- end
41
-
42
- # Or, in your controller action:
43
- DB.transaction do
44
- @user = User.create(params[:user])
45
- MyJob.enqueue user_id: @user.id
46
- end
47
- ```
48
-
49
- Sequel automatically wraps model persistance actions (create, update, destroy) in transactions, so you can simply call #enqueue methods from your models' callbacks, if you wish.
@@ -1,108 +0,0 @@
1
- ## Writing Reliable Jobs
2
-
3
- Que does everything it can to ensure that jobs are worked exactly once, but if something bad happens when a job is halfway completed, there's no way around it - the job will need be repeated over again from the beginning, probably by a different worker. When you're writing jobs, you need to be prepared for this to happen.
4
-
5
- The safest type of job is one that reads in data, either from the database or from external APIs, then does some number crunching and writes the results to the database. These jobs are easy to make safe - simply write the results to the database inside a transaction, and also destroy the job inside that transaction, like so:
6
-
7
- ```ruby
8
- class UpdateWidgetPrice < Que::Job
9
- def run(widget_id)
10
- widget = Widget[widget_id]
11
- price = ExternalService.get_widget_price(widget_id)
12
-
13
- ActiveRecord::Base.transaction do
14
- # Make changes to the database.
15
- widget.update price: price
16
-
17
- # Mark the job as destroyed, so it doesn't run again.
18
- destroy
19
- end
20
- end
21
- end
22
- ```
23
-
24
- Here, you're taking advantage of the guarantees of an [ACID](https://en.wikipedia.org/wiki/ACID) database. The job is destroyed along with the other changes, so either the write will succeed and the job will be run only once, or it will fail and the database will be left untouched. But even if it fails, the job can simply be retried, and there are no lingering effects from the first attempt, so no big deal.
25
-
26
- The more difficult type of job is one that makes changes that can't be controlled transactionally. For example, writing to an external service:
27
-
28
- ```ruby
29
- class ChargeCreditCard < Que::Job
30
- def run(user_id, credit_card_id)
31
- CreditCardService.charge(credit_card_id, amount: "$10.00")
32
-
33
- ActiveRecord::Base.transaction do
34
- User.where(id: user_id).update_all charged_at: Time.now
35
- destroy
36
- end
37
- end
38
- end
39
- ```
40
-
41
- What if the process abruptly dies after we tell the provider to charge the credit card, but before we finish the transaction? Que will retry the job, but there's no way to tell where (or even if) it failed the first time. The credit card will be charged a second time, and then you've got an angry customer. The ideal solution in this case is to make the job [idempotent](https://en.wikipedia.org/wiki/Idempotence), meaning that it will have the same effect no matter how many times it is run:
42
-
43
- ```ruby
44
- class ChargeCreditCard < Que::Job
45
- def run(user_id, credit_card_id)
46
- unless CreditCardService.check_for_previous_charge(credit_card_id)
47
- CreditCardService.charge(credit_card_id, amount: "$10.00")
48
- end
49
-
50
- ActiveRecord::Base.transaction do
51
- User.where(id: user_id).update_all charged_at: Time.now
52
- destroy
53
- end
54
- end
55
- end
56
- ```
57
-
58
- This makes the job slightly more complex, but reliable (or, at least, as reliable as your credit card service).
59
-
60
- Finally, there are some jobs where you won't want to write to the database at all:
61
-
62
- ```ruby
63
- class SendVerificationEmail < Que::Job
64
- def run(email_address)
65
- Mailer.verification_email(email_address).deliver
66
- end
67
- end
68
- ```
69
-
70
- In this case, we don't have any no way to prevent the occasional double-sending of an email. But, for ease of use, you can leave out the transaction and the `destroy` call entirely - Que will recognize that the job wasn't destroyed and will clean it up for you.
71
-
72
- ### Timeouts
73
-
74
- Long-running jobs aren't necessarily a problem for the database, since the overhead of an individual job is very small (just an advisory lock held in memory). But jobs that hang indefinitely can tie up a worker and [block the Ruby process from exiting gracefully](https://github.com/chanks/que/blob/master/docs/shutting_down_safely.md), which is a pain.
75
-
76
- If there's part of your job that is prone to hang (due to an API call or other HTTP request that never returns, for example), you can (and should) timeout those parts of your job. For example, consider a job that needs to make an HTTP request and then write to the database:
77
-
78
- ```ruby
79
- class ScrapeStuff < Que::Job
80
- def run(url_to_scrape)
81
- result = YourHTTPLibrary.get(url_to_scrape)
82
-
83
- ActiveRecord::Base.transaction do
84
- # Insert result...
85
-
86
- destroy
87
- end
88
- end
89
- end
90
- ```
91
-
92
- That request could take a very long time, or never return at all. Let's use the timeout feature that almost all HTTP libraries offer some version of:
93
-
94
- ```ruby
95
- class ScrapeStuff < Que::Job
96
- def run(url_to_scrape)
97
- result = YourHTTPLibrary.get(url_to_scrape, timeout: 5)
98
-
99
- ActiveRecord::Base.transaction do
100
- # Insert result...
101
-
102
- destroy
103
- end
104
- end
105
- end
106
- ```
107
-
108
- Now, if the request takes more than five seconds, an error will be raised (probably - check your library's documentation) and Que will just retry the job later.