que 0.11.3 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
data/lib/que/sql.rb DELETED
@@ -1,152 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Que
4
- SQL = {
5
- # Locks a job using a Postgres recursive CTE [1].
6
- #
7
- # As noted by the Postgres documentation, it may be slightly easier to
8
- # think about this expression as iteration rather than recursion, despite
9
- # the `RECURSION` nomenclature defined by the SQL standards committee.
10
- # Recursion is used here so that jobs in the table can be iterated one-by-
11
- # one until a lock can be acquired, where a non-recursive `SELECT` would
12
- # have the undesirable side-effect of locking multiple jobs at once. i.e.
13
- # Consider that the following would have the worker lock *all* unlocked
14
- # jobs:
15
- #
16
- # SELECT (j).*, pg_try_advisory_lock((j).job_id) AS locked
17
- # FROM que_jobs AS j;
18
- #
19
- # The CTE will initially produce an "anchor" from the non-recursive term
20
- # (i.e. before the `UNION`), and then use it as the contents of the
21
- # working table as it continues to iterate through `que_jobs` looking for
22
- # a lock. The jobs table has a sort on (priority, run_at, job_id) which
23
- # allows it to walk the jobs table in a stable manner. As noted above, the
24
- # recursion examines one job at a time so that it only ever acquires a
25
- # single lock.
26
- #
27
- # The recursion has two possible end conditions:
28
- #
29
- # 1. If a lock *can* be acquired, it bubbles up to the top-level `SELECT`
30
- # outside of the `job` CTE which stops recursion because it is
31
- # constrained with a `LIMIT` of 1.
32
- #
33
- # 2. If a lock *cannot* be acquired, the recursive term of the expression
34
- # (i.e. what's after the `UNION`) will return an empty result set
35
- # because there are no more candidates left that could possibly be
36
- # locked. This empty result automatically ends recursion.
37
- #
38
- # Note that this query can be easily modified to lock any number of jobs
39
- # by tweaking the LIMIT clause in the main SELECT statement.
40
- #
41
- # [1] http://www.postgresql.org/docs/devel/static/queries-with.html
42
- #
43
- # Thanks to RhodiumToad in #postgresql for help with the original version
44
- # of the job lock CTE.
45
- :lock_job => %{
46
- WITH RECURSIVE jobs AS (
47
- SELECT (j).*, pg_try_advisory_lock((j).job_id) AS locked
48
- FROM (
49
- SELECT j
50
- FROM que_jobs AS j
51
- WHERE queue = $1::text
52
- AND run_at <= now()
53
- ORDER BY priority, run_at, job_id
54
- LIMIT 1
55
- ) AS t1
56
- UNION ALL (
57
- SELECT (j).*, pg_try_advisory_lock((j).job_id) AS locked
58
- FROM (
59
- SELECT (
60
- SELECT j
61
- FROM que_jobs AS j
62
- WHERE queue = $1::text
63
- AND run_at <= now()
64
- AND (priority, run_at, job_id) > (jobs.priority, jobs.run_at, jobs.job_id)
65
- ORDER BY priority, run_at, job_id
66
- LIMIT 1
67
- ) AS j
68
- FROM jobs
69
- WHERE jobs.job_id IS NOT NULL
70
- LIMIT 1
71
- ) AS t1
72
- )
73
- )
74
- SELECT queue, priority, run_at, job_id, job_class, args, error_count
75
- FROM jobs
76
- WHERE locked
77
- LIMIT 1
78
- }.freeze,
79
-
80
- :check_job => %{
81
- SELECT 1 AS one
82
- FROM que_jobs
83
- WHERE queue = $1::text
84
- AND priority = $2::smallint
85
- AND run_at = $3::timestamptz
86
- AND job_id = $4::bigint
87
- }.freeze,
88
-
89
- :set_error => %{
90
- UPDATE que_jobs
91
- SET error_count = $1::integer,
92
- run_at = now() + $2::bigint * '1 second'::interval,
93
- last_error = $3::text
94
- WHERE queue = $4::text
95
- AND priority = $5::smallint
96
- AND run_at = $6::timestamptz
97
- AND job_id = $7::bigint
98
- }.freeze,
99
-
100
- :insert_job => %{
101
- INSERT INTO que_jobs
102
- (queue, priority, run_at, job_class, args)
103
- VALUES
104
- (coalesce($1, '')::text, coalesce($2, 100)::smallint, coalesce($3, now())::timestamptz, $4::text, coalesce($5, '[]')::json)
105
- RETURNING *
106
- }.freeze,
107
-
108
- :destroy_job => %{
109
- DELETE FROM que_jobs
110
- WHERE queue = $1::text
111
- AND priority = $2::smallint
112
- AND run_at = $3::timestamptz
113
- AND job_id = $4::bigint
114
- }.freeze,
115
-
116
- :job_stats => %{
117
- SELECT queue,
118
- job_class,
119
- count(*) AS count,
120
- count(locks.job_id) AS count_working,
121
- sum((error_count > 0)::int) AS count_errored,
122
- max(error_count) AS highest_error_count,
123
- min(run_at) AS oldest_run_at
124
- FROM que_jobs
125
- LEFT JOIN (
126
- SELECT (classid::bigint << 32) + objid::bigint AS job_id
127
- FROM pg_locks
128
- WHERE locktype = 'advisory'
129
- ) locks USING (job_id)
130
- GROUP BY queue, job_class
131
- ORDER BY count(*) DESC
132
- }.freeze,
133
-
134
- :worker_states => %{
135
- SELECT que_jobs.*,
136
- pg.pid AS pg_backend_pid,
137
- pg.state AS pg_state,
138
- pg.state_change AS pg_state_changed_at,
139
- pg.query AS pg_last_query,
140
- pg.query_start AS pg_last_query_started_at,
141
- pg.xact_start AS pg_transaction_started_at,
142
- pg.waiting AS pg_waiting_on_lock
143
- FROM que_jobs
144
- JOIN (
145
- SELECT (classid::bigint << 32) + objid::bigint AS job_id, pg_stat_activity.*
146
- FROM pg_locks
147
- JOIN pg_stat_activity USING (pid)
148
- WHERE locktype = 'advisory'
149
- ) pg USING (job_id)
150
- }.freeze
151
- }.freeze
152
- end
@@ -1,152 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Don't run these specs in JRuby until jruby-pg is compatible with ActiveRecord.
4
- unless defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
5
-
6
- require 'spec_helper'
7
- require 'active_record'
8
-
9
- if ActiveRecord.version.release >= Gem::Version.new('4.2')
10
- ActiveRecord::Base.raise_in_transactional_callbacks = true
11
- end
12
- ActiveRecord::Base.establish_connection(QUE_URL)
13
-
14
- Que.connection = ActiveRecord
15
- QUE_ADAPTERS[:active_record] = Que.adapter
16
-
17
- describe "Que using the ActiveRecord adapter" do
18
- before { Que.adapter = QUE_ADAPTERS[:active_record] }
19
-
20
- it_behaves_like "a multi-threaded Que adapter"
21
-
22
- it "should use the same connection that ActiveRecord does" do
23
- begin
24
- class ActiveRecordJob < Que::Job
25
- def run
26
- $pid1 = Integer(Que.execute("select pg_backend_pid()").first['pg_backend_pid'])
27
- $pid2 = Integer(ActiveRecord::Base.connection.select_value("select pg_backend_pid()"))
28
- end
29
- end
30
-
31
- ActiveRecordJob.enqueue
32
- Que::Job.work
33
-
34
- $pid1.should == $pid2
35
- ensure
36
- $pid1 = $pid2 = nil
37
- end
38
- end
39
-
40
- context "if the connection goes down and is reconnected" do
41
- before do
42
- Que::Job.enqueue
43
- ActiveRecord::Base.connection.reconnect!
44
- end
45
-
46
- it "should recreate the prepared statements" do
47
- expect { Que::Job.enqueue }.not_to raise_error
48
-
49
- DB[:que_jobs].count.should == 2
50
- end
51
-
52
- it "should work properly even in a transaction" do
53
- ActiveRecord::Base.transaction do
54
- expect { Que::Job.enqueue }.not_to raise_error
55
- end
56
-
57
- DB[:que_jobs].count.should == 2
58
- end
59
-
60
- it "should log this extraordinary event" do
61
- $logger.messages.clear
62
- Que::Job.enqueue
63
- $logger.messages.count.should == 1
64
- message = JSON.load($logger.messages.first)
65
- message['lib'].should == 'que'
66
- message['event'].should == 'reprepare_statement'
67
- message['name'].should == 'insert_job'
68
- end
69
- end
70
-
71
- it "should instantiate args as ActiveSupport::HashWithIndifferentAccess" do
72
- begin
73
- # Mimic the setting in the Railtie.
74
- Que.json_converter = :with_indifferent_access.to_proc
75
-
76
- ArgsJob.enqueue :param => 2
77
- Que::Job.work
78
- $passed_args.first[:param].should == 2
79
- $passed_args.first.should be_an_instance_of ActiveSupport::HashWithIndifferentAccess
80
- ensure
81
- Que.json_converter = Que::INDIFFERENTIATOR
82
- end
83
- end
84
-
85
- it "should support Rails' special extensions for times" do
86
- Que.mode = :async
87
- Que.worker_count = 4
88
- sleep_until { Que::Worker.workers.all? &:sleeping? }
89
-
90
- Que::Job.enqueue :run_at => 1.minute.ago
91
- DB[:que_jobs].get(:run_at).should be_within(3).of Time.now - 60
92
-
93
- Que.wake_interval = 0.005.seconds
94
- sleep_until { DB[:que_jobs].empty? }
95
- end
96
-
97
- it "should wake up a Worker after queueing a job in async mode, waiting for a transaction to commit if necessary" do
98
- Que.mode = :async
99
- Que.worker_count = 4
100
- sleep_until { Que::Worker.workers.all? &:sleeping? }
101
-
102
- # Wakes a worker immediately when not in a transaction.
103
- Que::Job.enqueue
104
- sleep_until { Que::Worker.workers.all?(&:sleeping?) && DB[:que_jobs].empty? }
105
-
106
- ActiveRecord::Base.transaction do
107
- Que::Job.enqueue
108
- Que::Worker.workers.each { |worker| worker.should be_sleeping }
109
- end
110
- sleep_until { Que::Worker.workers.all?(&:sleeping?) && DB[:que_jobs].empty? }
111
-
112
- # Do nothing when queueing with a specific :run_at.
113
- BlockJob.enqueue :run_at => Time.now
114
- Que::Worker.workers.each { |worker| worker.should be_sleeping }
115
- end
116
-
117
- it "should be able to survive an ActiveRecord::Rollback without raising an error" do
118
- ActiveRecord::Base.transaction do
119
- Que::Job.enqueue
120
- raise ActiveRecord::Rollback, "Call tech support!"
121
- end
122
- DB[:que_jobs].count.should be 0
123
- end
124
-
125
- it "should be able to tell when it's in an ActiveRecord transaction" do
126
- Que.adapter.should_not be_in_transaction
127
- ActiveRecord::Base.transaction do
128
- Que.adapter.should be_in_transaction
129
- end
130
- end
131
-
132
- it "should not leak connections to other databases when using ActiveRecord's multiple database support" do
133
- class SecondDatabaseModel < ActiveRecord::Base
134
- establish_connection(QUE_URL)
135
- end
136
-
137
- SecondDatabaseModel.clear_active_connections!
138
- SecondDatabaseModel.connection_handler.active_connections?.should == false
139
-
140
- class SecondDatabaseModelJob < Que::Job
141
- def run(*args)
142
- SecondDatabaseModel.connection.execute("SELECT 1")
143
- end
144
- end
145
-
146
- SecondDatabaseModelJob.enqueue
147
- Que::Job.work
148
-
149
- SecondDatabaseModel.connection_handler.active_connections?.should == false
150
- end
151
- end
152
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
- require 'connection_pool'
5
-
6
- Que.connection = QUE_SPEC_CONNECTION_POOL = ConnectionPool.new &NEW_PG_CONNECTION
7
- QUE_ADAPTERS[:connection_pool] = Que.adapter
8
-
9
- describe "Que using the ConnectionPool adapter" do
10
- before { Que.adapter = QUE_ADAPTERS[:connection_pool] }
11
-
12
- it_behaves_like "a multi-threaded Que adapter"
13
-
14
- it "should be able to tell when it's already in a transaction" do
15
- Que.adapter.should_not be_in_transaction
16
- QUE_SPEC_CONNECTION_POOL.with do |conn|
17
- conn.async_exec "BEGIN"
18
- Que.adapter.should be_in_transaction
19
- conn.async_exec "COMMIT"
20
- end
21
- end
22
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe "Que using a bare PG connection" do
6
- it_behaves_like "a Que adapter"
7
-
8
- it "should synchronize access to that connection" do
9
- lock = Que.adapter.lock
10
- q1, q2 = Queue.new, Queue.new
11
-
12
- thread1 = Thread.new do
13
- Que.adapter.checkout do
14
- q1.push nil
15
- q2.pop
16
- end
17
- end
18
-
19
- q1.pop
20
-
21
- thread2 = Thread.new do
22
- Que.adapter.checkout do
23
- q1.push nil
24
- q2.pop
25
- end
26
- end
27
-
28
- sleep_until { thread2.status == 'sleep' }
29
-
30
- thread1.should be_alive
31
- thread2.should be_alive
32
-
33
- lock.send(:instance_variable_get, :@mon_owner).should == thread1
34
- q2.push nil
35
- q1.pop
36
- lock.send(:instance_variable_get, :@mon_owner).should == thread2
37
- q2.push nil
38
- thread1.join
39
- thread2.join
40
- end
41
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
- require 'pond'
5
-
6
- Que.connection = QUE_SPEC_POND = Pond.new &NEW_PG_CONNECTION
7
- QUE_ADAPTERS[:pond] = Que.adapter
8
-
9
- describe "Que using the Pond adapter" do
10
- before { Que.adapter = QUE_ADAPTERS[:pond] }
11
-
12
- it_behaves_like "a multi-threaded Que adapter"
13
-
14
- it "should be able to tell when it's already in a transaction" do
15
- Que.adapter.should_not be_in_transaction
16
- QUE_SPEC_POND.checkout do |conn|
17
- conn.async_exec "BEGIN"
18
- Que.adapter.should be_in_transaction
19
- conn.async_exec "COMMIT"
20
- end
21
- end
22
- end
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- Que.connection = SEQUEL_ADAPTER_DB = Sequel.connect(QUE_URL)
6
- QUE_ADAPTERS[:sequel] = Que.adapter
7
-
8
- describe "Que using the Sequel adapter" do
9
- before { Que.adapter = QUE_ADAPTERS[:sequel] }
10
-
11
- it_behaves_like "a multi-threaded Que adapter"
12
-
13
- it "should use the same connection that Sequel does" do
14
- begin
15
- class SequelJob < Que::Job
16
- def run
17
- $pid1 = Integer(Que.execute("select pg_backend_pid()").first['pg_backend_pid'])
18
- $pid2 = Integer(SEQUEL_ADAPTER_DB['select pg_backend_pid()'].get)
19
- end
20
- end
21
-
22
- SequelJob.enqueue
23
- Que::Job.work
24
-
25
- $pid1.should == $pid2
26
- ensure
27
- $pid1 = $pid2 = nil
28
- end
29
- end
30
-
31
- it "should wake up a Worker after queueing a job in async mode, waiting for a transaction to commit if necessary" do
32
- Que.mode = :async
33
- Que.worker_count = 4
34
- sleep_until { Que::Worker.workers.all? &:sleeping? }
35
-
36
- # Wakes a worker immediately when not in a transaction.
37
- Que::Job.enqueue
38
- sleep_until { Que::Worker.workers.all?(&:sleeping?) && DB[:que_jobs].empty? }
39
-
40
- SEQUEL_ADAPTER_DB.transaction do
41
- Que::Job.enqueue
42
- Que::Worker.workers.each { |worker| worker.should be_sleeping }
43
- end
44
- sleep_until { Que::Worker.workers.all?(&:sleeping?) && DB[:que_jobs].empty? }
45
-
46
- # Do nothing when queueing with a specific :run_at.
47
- BlockJob.enqueue :run_at => Time.now
48
- Que::Worker.workers.each { |worker| worker.should be_sleeping }
49
- end
50
-
51
- it "should be able to tell when it's in a Sequel transaction" do
52
- Que.adapter.should_not be_in_transaction
53
- SEQUEL_ADAPTER_DB.transaction do
54
- Que.adapter.should be_in_transaction
55
- end
56
- end
57
- end
@@ -1,18 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'que', path: '../..'
4
-
5
- group :development, :test do
6
- gem 'rake'
7
-
8
- gem 'activerecord', '~> 3.2', :require => nil
9
- gem 'sequel', '~> 3', :require => nil
10
- gem 'connection_pool', :require => nil
11
- gem 'pg', :require => nil, :platform => :ruby
12
- gem 'pg_jruby', :require => nil, :platform => :jruby
13
- end
14
-
15
- group :test do
16
- gem 'rspec', '~> 2.14.1'
17
- gem 'pry'
18
- end
@@ -1,18 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'que', path: "../.."
4
-
5
- group :development, :test do
6
- gem 'rake'
7
-
8
- gem 'activerecord', '~> 4.0', :require => nil
9
- gem 'sequel', '~> 4', :require => nil
10
- gem 'connection_pool', :require => nil
11
- gem 'pg', :require => nil, :platform => :ruby
12
- gem 'pg_jruby', :require => nil, :platform => :jruby
13
- end
14
-
15
- group :test do
16
- gem 'rspec', '~> 2.14.1'
17
- gem 'pry'
18
- end
data/spec/spec_helper.rb DELETED
@@ -1,118 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'que'
4
- require 'uri'
5
- require 'pg'
6
- require 'logger'
7
- require 'json'
8
-
9
- Dir['./spec/support/**/*.rb'].sort.each &method(:require)
10
-
11
-
12
-
13
- # Handy constants for initializing PG connections:
14
- QUE_URL = ENV['DATABASE_URL'] || 'postgres://postgres:@localhost/que-test'
15
-
16
- NEW_PG_CONNECTION = proc do
17
- uri = URI.parse(QUE_URL)
18
- pg = PG::Connection.open :host => uri.host,
19
- :user => uri.user,
20
- :password => uri.password,
21
- :port => uri.port || 5432,
22
- :dbname => uri.path[1..-1]
23
-
24
- # Avoid annoying NOTICE messages in specs.
25
- pg.async_exec "SET client_min_messages TO 'warning'"
26
- pg
27
- end
28
-
29
-
30
-
31
- # Adapters track which statements have been prepared for their connections,
32
- # and if Que.connection= is called before each spec, we're constantly creating
33
- # new adapters and losing that information, which is bad. So instead, we hang
34
- # onto a few adapters and assign them using Que.adapter= as needed. The plain
35
- # pg adapter is the default.
36
-
37
- # Also, let Que initialize the adapter itself, to make sure that the
38
- # recognition logic works. Similar code can be found in the adapter specs.
39
- Que.connection = NEW_PG_CONNECTION.call
40
- QUE_ADAPTERS = {:pg => Que.adapter}
41
-
42
-
43
-
44
- # We use Sequel to examine the database in specs.
45
- require 'sequel'
46
- DB = Sequel.connect(QUE_URL)
47
-
48
-
49
-
50
- # Reset the table to the most up-to-date version.
51
- DB.drop_table? :que_jobs
52
- Que::Migrations.migrate!
53
-
54
-
55
-
56
- # Set up a dummy logger.
57
- Que.logger = $logger = Object.new
58
- $logger_mutex = Mutex.new # Protect against rare errors on Rubinius/JRuby.
59
-
60
- def $logger.messages
61
- @messages ||= []
62
- end
63
-
64
- def $logger.method_missing(m, message)
65
- $logger_mutex.synchronize { messages << message }
66
- end
67
-
68
- # Object includes Kernel#warn which is not what we expect, so remove:
69
- def $logger.warn(message)
70
- method_missing(:warn, message)
71
- end
72
-
73
-
74
-
75
- # Helper to display spec descriptions.
76
- description_builder = -> hash do
77
- if g = hash[:example_group]
78
- "#{description_builder.call(g)} #{hash[:description_args].first}"
79
- else
80
- hash[:description_args].first
81
- end
82
- end
83
-
84
- stdout = Logger.new(STDOUT)
85
-
86
- RSpec.configure do |config|
87
- config.around do |spec|
88
- # Figure out which spec is about to run, for logging purposes.
89
- data = example.metadata
90
- desc = description_builder.call(data)
91
- line = "rspec #{data[:file_path]}:#{data[:line_number]}"
92
-
93
- # Optionally log to STDOUT which spec is about to run. This is noisy, but
94
- # helpful in identifying hanging specs.
95
- stdout.info "Running spec: #{desc} @ #{line}" if ENV['LOG_SPEC']
96
-
97
- Que.adapter = QUE_ADAPTERS[:pg]
98
-
99
- Que.worker_count = 0
100
- Que.mode = :async
101
- Que.wake_interval = nil
102
-
103
- $logger.messages.clear
104
-
105
- spec.run
106
-
107
- Que.worker_count = 0
108
- Que.mode = :off
109
- Que.wake_interval = nil
110
-
111
- DB[:que_jobs].delete
112
-
113
- # A bit of lint: make sure that no advisory locks are left open.
114
- unless DB[:pg_locks].where(:locktype => 'advisory').empty?
115
- stdout.info "Advisory lock left open: #{desc} @ #{line}"
116
- end
117
- end
118
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Helper for testing threaded code.
4
- QUE_TEST_TIMEOUT ||= 2
5
- def sleep_until(timeout = QUE_TEST_TIMEOUT)
6
- deadline = Time.now + timeout
7
- loop do
8
- break if yield
9
- raise "Thing never happened!" if Time.now > deadline
10
- sleep 0.01
11
- end
12
- end
13
-
14
- def suppress_warnings
15
- original_verbosity, $VERBOSE = $VERBOSE, nil
16
- yield
17
- ensure
18
- $VERBOSE = original_verbosity
19
- end
data/spec/support/jobs.rb DELETED
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Common Job classes for use in specs.
4
-
5
- # Handy for blocking in the middle of processing a job.
6
- class BlockJob < Que::Job
7
- def run
8
- $q1.push nil
9
- $q2.pop
10
- end
11
- end
12
-
13
- RSpec.configure do |config|
14
- config.before { $q1, $q2 = Queue.new, Queue.new }
15
- end
16
-
17
-
18
-
19
- class ErrorJob < Que::Job
20
- def run
21
- raise "ErrorJob!"
22
- end
23
- end
24
-
25
-
26
-
27
- class ArgsJob < Que::Job
28
- def run(*args)
29
- $passed_args = args
30
- end
31
- end
32
-
33
- RSpec.configure do |config|
34
- config.before { $passed_args = nil }
35
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- shared_examples "a Que adapter" do
4
- it "should be able to execute arbitrary SQL and return indifferent hashes" do
5
- result = Que.execute("SELECT 1 AS one")
6
- result.should == [{'one'=>1}]
7
- result.first[:one].should == 1
8
- end
9
-
10
- it "should be able to execute multiple SQL statements in one string" do
11
- Que.execute("SELECT 1 AS one; SELECT 1 AS one")
12
- end
13
-
14
- it "should be able to queue and work a job" do
15
- Que::Job.enqueue
16
- result = Que::Job.work
17
- result[:event].should == :job_worked
18
- result[:job][:job_class].should == 'Que::Job'
19
- end
20
-
21
- it "should yield the same Postgres connection for the duration of the block" do
22
- Que.adapter.checkout do |conn|
23
- conn.should be_a PG::Connection
24
- pid1 = Que.execute "SELECT pg_backend_pid()"
25
- pid2 = Que.execute "SELECT pg_backend_pid()"
26
- pid1.should == pid2
27
- end
28
- end
29
-
30
- it "should allow nested checkouts" do
31
- Que.adapter.checkout do |a|
32
- Que.adapter.checkout do |b|
33
- a.object_id.should == b.object_id
34
- end
35
- end
36
- end
37
- end