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
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