que 0.11.3 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/tests.yml +51 -0
  3. data/.gitignore +2 -0
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +502 -97
  6. data/Dockerfile +20 -0
  7. data/LICENSE.txt +1 -1
  8. data/README.md +205 -59
  9. data/auto/dev +21 -0
  10. data/auto/pre-push-hook +30 -0
  11. data/auto/psql +9 -0
  12. data/auto/test +5 -0
  13. data/auto/test-postgres-14 +17 -0
  14. data/bin/que +8 -81
  15. data/docker-compose.yml +47 -0
  16. data/docs/README.md +881 -0
  17. data/lib/que/active_job/extensions.rb +114 -0
  18. data/lib/que/active_record/connection.rb +51 -0
  19. data/lib/que/active_record/model.rb +48 -0
  20. data/lib/que/command_line_interface.rb +259 -0
  21. data/lib/que/connection.rb +198 -0
  22. data/lib/que/connection_pool.rb +78 -0
  23. data/lib/que/job.rb +210 -103
  24. data/lib/que/job_buffer.rb +255 -0
  25. data/lib/que/job_methods.rb +176 -0
  26. data/lib/que/listener.rb +176 -0
  27. data/lib/que/locker.rb +507 -0
  28. data/lib/que/metajob.rb +47 -0
  29. data/lib/que/migrations/4/down.sql +48 -0
  30. data/lib/que/migrations/4/up.sql +267 -0
  31. data/lib/que/migrations/5/down.sql +73 -0
  32. data/lib/que/migrations/5/up.sql +76 -0
  33. data/lib/que/migrations/6/down.sql +8 -0
  34. data/lib/que/migrations/6/up.sql +8 -0
  35. data/lib/que/migrations/7/down.sql +5 -0
  36. data/lib/que/migrations/7/up.sql +13 -0
  37. data/lib/que/migrations.rb +37 -18
  38. data/lib/que/poller.rb +274 -0
  39. data/lib/que/rails/railtie.rb +12 -0
  40. data/lib/que/result_queue.rb +35 -0
  41. data/lib/que/sequel/model.rb +52 -0
  42. data/lib/que/utils/assertions.rb +62 -0
  43. data/lib/que/utils/constantization.rb +19 -0
  44. data/lib/que/utils/error_notification.rb +68 -0
  45. data/lib/que/utils/freeze.rb +20 -0
  46. data/lib/que/utils/introspection.rb +50 -0
  47. data/lib/que/utils/json_serialization.rb +21 -0
  48. data/lib/que/utils/logging.rb +79 -0
  49. data/lib/que/utils/middleware.rb +46 -0
  50. data/lib/que/utils/queue_management.rb +18 -0
  51. data/lib/que/utils/ruby2_keywords.rb +19 -0
  52. data/lib/que/utils/transactions.rb +34 -0
  53. data/lib/que/version.rb +5 -1
  54. data/lib/que/worker.rb +145 -149
  55. data/lib/que.rb +103 -159
  56. data/que.gemspec +17 -4
  57. data/scripts/docker-entrypoint +14 -0
  58. data/scripts/test +6 -0
  59. metadata +59 -95
  60. data/.rspec +0 -2
  61. data/.travis.yml +0 -17
  62. data/Gemfile +0 -24
  63. data/docs/advanced_setup.md +0 -106
  64. data/docs/customizing_que.md +0 -200
  65. data/docs/error_handling.md +0 -47
  66. data/docs/inspecting_the_queue.md +0 -114
  67. data/docs/logging.md +0 -50
  68. data/docs/managing_workers.md +0 -80
  69. data/docs/migrating.md +0 -30
  70. data/docs/multiple_queues.md +0 -27
  71. data/docs/shutting_down_safely.md +0 -7
  72. data/docs/using_plain_connections.md +0 -41
  73. data/docs/using_sequel.md +0 -31
  74. data/docs/writing_reliable_jobs.md +0 -117
  75. data/lib/generators/que/install_generator.rb +0 -24
  76. data/lib/generators/que/templates/add_que.rb +0 -13
  77. data/lib/que/adapters/active_record.rb +0 -54
  78. data/lib/que/adapters/base.rb +0 -127
  79. data/lib/que/adapters/connection_pool.rb +0 -16
  80. data/lib/que/adapters/pg.rb +0 -21
  81. data/lib/que/adapters/pond.rb +0 -16
  82. data/lib/que/adapters/sequel.rb +0 -20
  83. data/lib/que/railtie.rb +0 -16
  84. data/lib/que/rake_tasks.rb +0 -59
  85. data/lib/que/sql.rb +0 -152
  86. data/spec/adapters/active_record_spec.rb +0 -152
  87. data/spec/adapters/connection_pool_spec.rb +0 -22
  88. data/spec/adapters/pg_spec.rb +0 -41
  89. data/spec/adapters/pond_spec.rb +0 -22
  90. data/spec/adapters/sequel_spec.rb +0 -57
  91. data/spec/gemfiles/Gemfile1 +0 -18
  92. data/spec/gemfiles/Gemfile2 +0 -18
  93. data/spec/spec_helper.rb +0 -118
  94. data/spec/support/helpers.rb +0 -19
  95. data/spec/support/jobs.rb +0 -35
  96. data/spec/support/shared_examples/adapter.rb +0 -37
  97. data/spec/support/shared_examples/multi_threaded_adapter.rb +0 -46
  98. data/spec/travis.rb +0 -23
  99. data/spec/unit/connection_spec.rb +0 -14
  100. data/spec/unit/customization_spec.rb +0 -251
  101. data/spec/unit/enqueue_spec.rb +0 -245
  102. data/spec/unit/helper_spec.rb +0 -12
  103. data/spec/unit/logging_spec.rb +0 -101
  104. data/spec/unit/migrations_spec.rb +0 -84
  105. data/spec/unit/pool_spec.rb +0 -365
  106. data/spec/unit/run_spec.rb +0 -14
  107. data/spec/unit/states_spec.rb +0 -50
  108. data/spec/unit/stats_spec.rb +0 -46
  109. data/spec/unit/transaction_spec.rb +0 -36
  110. data/spec/unit/work_spec.rb +0 -407
  111. data/spec/unit/worker_spec.rb +0 -167
  112. data/tasks/benchmark.rb +0 -3
  113. data/tasks/rspec.rb +0 -14
  114. data/tasks/safe_shutdown.rb +0 -67
@@ -1,41 +0,0 @@
1
- ## Using Plain Postgres Connections
2
-
3
- If you're not using an ORM like ActiveRecord or Sequel, you can use one of two gems to manage a connection pool for your PG connections. The first is the ConnectionPool gem (be sure to add `gem 'connection_pool'` to your Gemfile):
4
-
5
- ```ruby
6
- require 'uri'
7
- require 'pg'
8
- require 'connection_pool'
9
-
10
- uri = URI.parse(ENV['DATABASE_URL'])
11
-
12
- Que.connection = ConnectionPool.new :size => 10 do
13
- PG::Connection.open :host => uri.host,
14
- :user => uri.user,
15
- :password => uri.password,
16
- :port => uri.port || 5432,
17
- :dbname => uri.path[1..-1]
18
- end
19
- ```
20
-
21
- 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.
22
-
23
- The Pond gem doesn't have this drawback - it is very similar to ConnectionPool, but establishes connections lazily (add `gem 'pond'` to your Gemfile):
24
-
25
- ```ruby
26
- require 'uri'
27
- require 'pg'
28
- require 'pond'
29
-
30
- uri = URI.parse(ENV['DATABASE_URL'])
31
-
32
- Que.connection = Pond.new :maximum_size => 10 do
33
- PG::Connection.open :host => uri.host,
34
- :user => uri.user,
35
- :password => uri.password,
36
- :port => uri.port || 5432,
37
- :dbname => uri.path[1..-1]
38
- end
39
- ```
40
-
41
- Please be aware that if you're using ActiveRecord or Sequel to manage your data, 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. In general, your app should probably be using a connection pool, and Que should probably hook into whatever connection pool you're already using.
data/docs/using_sequel.md DELETED
@@ -1,31 +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
- Then you can safely use the same database object to transactionally protect your jobs:
11
-
12
- ```ruby
13
- class MyJob < Que::Job
14
- def run
15
- # Do stuff.
16
-
17
- DB.transaction do
18
- # Make changes to the database.
19
-
20
- # Destroying this job will be protected by the same transaction.
21
- destroy
22
- end
23
- end
24
- end
25
-
26
- # In your controller action:
27
- DB.transaction do
28
- @user = User.create(params[:user])
29
- MyJob.enqueue :user_id => @user.id
30
- end
31
- ```
@@ -1,117 +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 have the job destroy itself 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
- # Destroy the job.
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 in Que, since the overhead of an individual job isn't that big (just an open PG connection and 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
- Que doesn't offer a general way to kill jobs that have been running too long, because that currently can't be done safely in Ruby. Typically, one would use Ruby's Timeout module for this sort of thing, but wrapping a database transaction inside a timeout introduces a risk of premature commits, which can corrupt your data. 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 detail on why this is.
77
-
78
- However, 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 timeout those individual parts of your job relatively safely. For example, consider a job that needs to make an HTTP request and then write to the database:
79
-
80
- ```ruby
81
- require 'net/http'
82
-
83
- class ScrapeStuff < Que::Job
84
- def run(domain_to_scrape, path_to_scrape)
85
- result = Net::HTTP.get(domain_to_scrape, path_to_scrape)
86
-
87
- ActiveRecord::Base.transaction do
88
- # Insert result...
89
-
90
- destroy
91
- end
92
- end
93
- end
94
- ```
95
-
96
- That request could take a very long time, or never return at all. Let's wrap it in a five-second timeout:
97
-
98
- ```ruby
99
- require 'net/http'
100
- require 'timeout'
101
-
102
- class ScrapeStuff < Que::Job
103
- def run(domain_to_scrape, path_to_scrape)
104
- result = Timeout.timeout(5){Net::HTTP.get(domain_to_scrape, path_to_scrape)}
105
-
106
- ActiveRecord::Base.transaction do
107
- # Insert result...
108
-
109
- destroy
110
- end
111
- end
112
- end
113
- ```
114
-
115
- Now, if the request takes more than five seconds, a `Timeout::Error` will be raised and Que will just retry the job later. This solution isn't perfect, since Timeout uses Thread#kill under the hood, which can lead to unpredictable behavior. But it's separate from our transaction, so there's no risk of losing data - even a catastrophic error that left Net::HTTP in a bad state would be fixable by restarting the process.
116
-
117
- Finally, remember that if you're using a library that offers its own timeout functionality, that's usually preferable to using the Timeout module.
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rails/generators'
4
- require 'rails/generators/migration'
5
- require 'active_record'
6
-
7
- module Que
8
- class InstallGenerator < Rails::Generators::Base
9
- include Rails::Generators::Migration
10
-
11
- namespace 'que:install'
12
- self.source_paths << File.join(File.dirname(__FILE__), 'templates')
13
- desc "Generates a migration to add Que's job table."
14
-
15
- def self.next_migration_number(dirname)
16
- next_migration_number = current_migration_number(dirname) + 1
17
- ActiveRecord::Migration.next_migration_number(next_migration_number)
18
- end
19
-
20
- def create_migration_file
21
- migration_template 'add_que.rb', 'db/migrate/add_que.rb'
22
- end
23
- end
24
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class AddQue < ActiveRecord::Migration
4
- def self.up
5
- # The current version as of this migration's creation.
6
- Que.migrate! :version => 3
7
- end
8
-
9
- def self.down
10
- # Completely removes Que's job queue.
11
- Que.migrate! :version => 0
12
- end
13
- end
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Que
4
- module Adapters
5
- class ActiveRecord < Base
6
- def checkout
7
- checkout_activerecord_adapter { |conn| yield conn.raw_connection }
8
- end
9
-
10
- def wake_worker_after_commit
11
- # Works with ActiveRecord 3.2 and 4 (possibly earlier, didn't check)
12
- if in_transaction?
13
- checkout_activerecord_adapter { |adapter| adapter.add_transaction_record(TransactionCallback.new) }
14
- else
15
- Que.wake!
16
- end
17
- end
18
-
19
- def cleanup!
20
- # ActiveRecord will check out connections to the current thread when
21
- # queries are executed and not return them to the pool until
22
- # explicitly requested to. The wisdom of this API is questionable, and
23
- # it doesn't pose a problem for the typical case of workers using a
24
- # single PG connection (since we ensure that connection is checked in
25
- # and checked out responsibly), but since ActiveRecord supports
26
- # connections to multiple databases, it's easy for people using that
27
- # feature to unknowingly leak connections to other databases. So, take
28
- # the additional step of telling ActiveRecord to check in all of the
29
- # current thread's connections between jobs.
30
- ::ActiveRecord::Base.clear_active_connections!
31
- end
32
-
33
- class TransactionCallback
34
- def has_transactional_callbacks?
35
- true
36
- end
37
-
38
- def rolledback!(force_restore_state = false, should_run_callbacks = true)
39
- # no-op
40
- end
41
-
42
- def committed!(should_run_callbacks = true)
43
- Que.wake!
44
- end
45
- end
46
-
47
- private
48
-
49
- def checkout_activerecord_adapter(&block)
50
- ::ActiveRecord::Base.connection_pool.with_connection(&block)
51
- end
52
- end
53
- end
54
- end
@@ -1,127 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'time' # For Time.parse.
4
-
5
- module Que
6
- module Adapters
7
- autoload :ActiveRecord, 'que/adapters/active_record'
8
- autoload :ConnectionPool, 'que/adapters/connection_pool'
9
- autoload :PG, 'que/adapters/pg'
10
- autoload :Pond, 'que/adapters/pond'
11
- autoload :Sequel, 'que/adapters/sequel'
12
-
13
- class Base
14
- def initialize(thing = nil)
15
- @prepared_statements = {}
16
- end
17
-
18
- # The only method that adapters really need to implement. Should lock a
19
- # PG::Connection (or something that acts like a PG::Connection) so that
20
- # no other threads are using it and yield it to the block. Should also
21
- # be re-entrant.
22
- def checkout(&block)
23
- raise NotImplementedError
24
- end
25
-
26
- # Called after Que has returned its connection to whatever pool it's
27
- # using.
28
- def cleanup!
29
- end
30
-
31
- # Called after a job is queued in async mode, to prompt a worker to
32
- # wake up after the current transaction commits. Not all adapters will
33
- # implement this.
34
- def wake_worker_after_commit
35
- false
36
- end
37
-
38
- def execute(command, params = [])
39
- params = params.map do |param|
40
- case param
41
- # The pg gem unfortunately doesn't convert fractions of time instances, so cast them to a string.
42
- when Time then param.strftime("%Y-%m-%d %H:%M:%S.%6N %z")
43
- when Array, Hash then JSON_MODULE.dump(param)
44
- else param
45
- end
46
- end
47
-
48
- cast_result \
49
- case command
50
- when Symbol then execute_prepared(command, params)
51
- when String then execute_sql(command, params)
52
- end
53
- end
54
-
55
- def in_transaction?
56
- checkout { |conn| conn.transaction_status != ::PG::PQTRANS_IDLE }
57
- end
58
-
59
- private
60
-
61
- def execute_sql(sql, params)
62
- args = params.empty? ? [sql] : [sql, params]
63
- checkout { |conn| conn.async_exec(*args) }
64
- end
65
-
66
- def execute_prepared(name, params)
67
- checkout do |conn|
68
- # Prepared statement errors have the potential to foul up the entire
69
- # transaction, so if we're in one, err on the side of safety.
70
- return execute_sql(SQL[name], params) if Que.disable_prepared_statements || in_transaction?
71
-
72
- statements = @prepared_statements[conn] ||= {}
73
-
74
- begin
75
- unless statements[name]
76
- conn.prepare("que_#{name}", SQL[name])
77
- prepared_just_now = statements[name] = true
78
- end
79
-
80
- conn.exec_prepared("que_#{name}", params)
81
- rescue ::PG::InvalidSqlStatementName => error
82
- # Reconnections on ActiveRecord can cause the same connection
83
- # objects to refer to new backends, so recover as well as we can.
84
-
85
- unless prepared_just_now
86
- Que.log :level => 'warn', :event => "reprepare_statement", :name => name
87
- statements[name] = false
88
- retry
89
- end
90
-
91
- raise error
92
- end
93
- end
94
- end
95
-
96
- CAST_PROCS = {}
97
-
98
- # Integer, bigint, smallint:
99
- CAST_PROCS[23] = CAST_PROCS[20] = CAST_PROCS[21] = proc(&:to_i)
100
-
101
- # Timestamp with time zone.
102
- CAST_PROCS[1184] = Time.method(:parse)
103
-
104
- # JSON.
105
- CAST_PROCS[114] = JSON_MODULE.method(:load)
106
-
107
- # Boolean:
108
- CAST_PROCS[16] = 't'.method(:==)
109
-
110
- def cast_result(result)
111
- output = result.to_a
112
-
113
- result.fields.each_with_index do |field, index|
114
- if converter = CAST_PROCS[result.ftype(index)]
115
- output.each do |hash|
116
- unless (value = hash[field]).nil?
117
- hash[field] = converter.call(value)
118
- end
119
- end
120
- end
121
- end
122
-
123
- output.map!(&Que.json_converter)
124
- end
125
- end
126
- end
127
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Que
4
- module Adapters
5
- class ConnectionPool < Base
6
- def initialize(pool)
7
- @pool = pool
8
- super
9
- end
10
-
11
- def checkout(&block)
12
- @pool.with(&block)
13
- end
14
- end
15
- end
16
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'monitor'
4
-
5
- module Que
6
- module Adapters
7
- class PG < Base
8
- attr_reader :lock
9
-
10
- def initialize(pg)
11
- @pg = pg
12
- @lock = Monitor.new # Must be re-entrant.
13
- super
14
- end
15
-
16
- def checkout
17
- @lock.synchronize { yield @pg }
18
- end
19
- end
20
- end
21
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Que
4
- module Adapters
5
- class Pond < Base
6
- def initialize(pond)
7
- @pond = pond
8
- super
9
- end
10
-
11
- def checkout(&block)
12
- @pond.checkout(&block)
13
- end
14
- end
15
- end
16
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Que
4
- module Adapters
5
- class Sequel < Base
6
- def initialize(db)
7
- @db = db
8
- super
9
- end
10
-
11
- def checkout(&block)
12
- @db.synchronize(&block)
13
- end
14
-
15
- def wake_worker_after_commit
16
- @db.after_commit { Que.wake! }
17
- end
18
- end
19
- end
20
- end
data/lib/que/railtie.rb DELETED
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Que
4
- class Railtie < Rails::Railtie
5
- config.que = Que
6
-
7
- Que.logger = proc { Rails.logger }
8
- Que.mode = :sync if Rails.env.test?
9
- Que.connection = ::ActiveRecord if defined? ::ActiveRecord
10
- Que.json_converter = :with_indifferent_access.to_proc
11
-
12
- rake_tasks do
13
- load 'que/rake_tasks.rb'
14
- end
15
- end
16
- end
@@ -1,59 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- namespace :que do
4
- desc "Process Que's jobs using a worker pool"
5
- task :work => :environment do
6
- $stdout.sync = true
7
-
8
- $stdout.puts "The que:work rake task has been deprecated and will be removed in Que 1.0. Please transition to the que command line interface instead."
9
-
10
- if defined?(::Rails) && Rails.respond_to?(:application)
11
- # ActiveSupport's dependency autoloading isn't threadsafe, and Que uses
12
- # multiple threads, which means that eager loading is necessary. Rails
13
- # explicitly prevents eager loading when the environment task is invoked,
14
- # so we need to manually eager load the app here.
15
- Rails.application.eager_load!
16
- end
17
-
18
- Que.logger.level = Logger.const_get((ENV['QUE_LOG_LEVEL'] || 'INFO').upcase)
19
- Que.worker_count = (ENV['QUE_WORKER_COUNT'] || 4).to_i
20
- Que.wake_interval = (ENV['QUE_WAKE_INTERVAL'] || 0.1).to_f
21
- Que.queue_name = ENV['QUE_QUEUE'] if ENV['QUE_QUEUE']
22
- Que.mode = :async
23
-
24
- # When changing how signals are caught, be sure to test the behavior with
25
- # the rake task in tasks/safe_shutdown.rb.
26
-
27
- stop = false
28
- %w( INT TERM ).each do |signal|
29
- trap(signal) {stop = true}
30
- end
31
-
32
- at_exit do
33
- $stdout.puts "Finishing Que's current jobs before exiting..."
34
- Que.worker_count = 0
35
- Que.mode = :off
36
- $stdout.puts "Que's jobs finished, exiting..."
37
- end
38
-
39
- loop do
40
- sleep 0.01
41
- break if stop
42
- end
43
- end
44
-
45
- desc "Migrate Que's job table to the most recent version (creating it if it doesn't exist)"
46
- task :migrate => :environment do
47
- Que.migrate!
48
- end
49
-
50
- desc "Drop Que's job table"
51
- task :drop => :environment do
52
- Que.drop!
53
- end
54
-
55
- desc "Clear Que's job table"
56
- task :clear => :environment do
57
- Que.clear!
58
- end
59
- end