sidekiq-unique-jobs 4.0.17 → 4.0.18

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sidekiq-unique-jobs might be problematic. Click here for more details.

Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +4 -0
  3. data/.gitignore +1 -0
  4. data/.travis.yml +3 -3
  5. data/CHANGELOG.md +9 -0
  6. data/Gemfile +1 -0
  7. data/README.md +6 -3
  8. data/circle.yml +3 -6
  9. data/lib/sidekiq-unique-jobs.rb +4 -0
  10. data/lib/sidekiq_unique_jobs/cli.rb +2 -2
  11. data/lib/sidekiq_unique_jobs/lock/until_executed.rb +5 -1
  12. data/lib/sidekiq_unique_jobs/lock/while_executing.rb +3 -1
  13. data/lib/sidekiq_unique_jobs/script_mock.rb +68 -0
  14. data/lib/sidekiq_unique_jobs/scripts.rb +8 -5
  15. data/lib/sidekiq_unique_jobs/testing.rb +36 -0
  16. data/lib/sidekiq_unique_jobs/testing/sidekiq_overrides.rb +1 -1
  17. data/lib/sidekiq_unique_jobs/version.rb +1 -1
  18. data/rails_example/.env +12 -0
  19. data/rails_example/.rspec +2 -0
  20. data/rails_example/Gemfile +21 -7
  21. data/rails_example/app/channels/appearance_channel.rb +17 -0
  22. data/rails_example/app/channels/application_cable/channel.rb +4 -0
  23. data/rails_example/app/channels/application_cable/connection.rb +9 -0
  24. data/rails_example/app/channels/post_channel.rb +5 -0
  25. data/rails_example/app/controllers/work_controller.rb +8 -5
  26. data/rails_example/app/models/application_record.rb +3 -0
  27. data/rails_example/app/models/guest.rb +21 -0
  28. data/rails_example/app/models/post.rb +1 -1
  29. data/rails_example/app/workers/simple_worker.rb +5 -4
  30. data/rails_example/bin/check_or_setup_db +57 -0
  31. data/rails_example/bin/docker-setup +20 -0
  32. data/rails_example/bin/rails +1 -1
  33. data/rails_example/bin/setup +15 -10
  34. data/rails_example/bin/update +29 -0
  35. data/rails_example/cable.ru +6 -0
  36. data/rails_example/common-services.yml +50 -0
  37. data/rails_example/config/application.rb +5 -4
  38. data/rails_example/config/boot.rb +1 -1
  39. data/rails_example/config/cable.yml +9 -0
  40. data/rails_example/config/database.docker.yml +12 -0
  41. data/rails_example/config/database.yml +15 -22
  42. data/rails_example/config/environment.rb +1 -1
  43. data/rails_example/config/environments/development.rb +24 -11
  44. data/rails_example/config/environments/production.rb +24 -17
  45. data/rails_example/config/environments/test.rb +6 -6
  46. data/rails_example/config/initializers/application_controller_renderer.rb +6 -0
  47. data/rails_example/config/initializers/backtrace_silencers.rb +3 -2
  48. data/rails_example/config/initializers/cookies_serializer.rb +2 -0
  49. data/rails_example/config/initializers/new_framework_defaults.rb +23 -0
  50. data/rails_example/config/initializers/session_store.rb +1 -1
  51. data/rails_example/config/initializers/sidekiq.rb +2 -2
  52. data/rails_example/config/initializers/wrap_parameters.rb +2 -2
  53. data/rails_example/config/puma.rb +38 -0
  54. data/rails_example/config/routes.rb +2 -1
  55. data/rails_example/config/secrets.yml +3 -3
  56. data/rails_example/config/spring.rb +6 -0
  57. data/rails_example/db/migrate/20160724111322_create_posts.rb +12 -0
  58. data/rails_example/db/schema.rb +8 -4
  59. data/rails_example/dev-entrypoint.sh +55 -0
  60. data/rails_example/dev.env +12 -0
  61. data/rails_example/docker-compose.yml +90 -0
  62. data/rails_example/docker/rails.Dockerfile +27 -0
  63. data/rails_example/spec/controllers/work_controller_spec.rb +172 -0
  64. data/rails_example/spec/factories/posts.rb +8 -0
  65. data/rails_example/spec/models/post_spec.rb +4 -0
  66. data/rails_example/spec/rails_helper.rb +21 -0
  67. data/rails_example/spec/spec_helper.rb +20 -0
  68. data/rails_example/spec/support/sidekiq_meta.rb +12 -0
  69. data/rails_example/spec/workers/simple_worker_spec.rb +4 -0
  70. data/spec/jobs/my_unique_job.rb +7 -2
  71. data/spec/jobs/my_unique_job_with_filter_method.rb +17 -0
  72. data/spec/jobs/my_unique_job_with_filter_proc.rb +15 -0
  73. data/spec/jobs/notify_worker.rb +1 -1
  74. data/spec/lib/sidekiq_unique_jobs/client/middleware_spec.rb +34 -0
  75. data/spec/lib/sidekiq_unique_jobs/lock/until_and_while_executing_spec.rb +5 -1
  76. data/spec/lib/sidekiq_unique_jobs/lock/until_executed_spec.rb +5 -8
  77. data/spec/lib/sidekiq_unique_jobs/options_with_fallback_spec.rb +2 -1
  78. data/spec/lib/sidekiq_unique_jobs/script_mock_spec.rb +84 -0
  79. data/spec/lib/sidekiq_unique_jobs/scripts_spec.rb +4 -0
  80. data/spec/lib/sidekiq_unique_jobs/sidekiq_testing_enabled_spec.rb +7 -11
  81. data/spec/lib/sidekiq_unique_jobs/sidekiq_unique_ext_spec.rb +2 -1
  82. data/spec/spec_helper.rb +1 -2
  83. data/spec/support/sidekiq_meta.rb +16 -11
  84. metadata +39 -7
  85. data/rails_example/db/development.sqlite3 +0 -0
  86. data/rails_example/db/migrate/20151107231835_create_posts.rb +0 -10
  87. data/rails_example/db/test.sqlite3 +0 -0
@@ -0,0 +1,8 @@
1
+ FactoryGirl.define do
2
+ factory :post do
3
+ title 'MyString'
4
+ body 'MyText'
5
+ excerpt 'MyString'
6
+ read_count 1
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ require 'rails_helper'
2
+
3
+ describe Post, type: :model do
4
+ end
@@ -0,0 +1,21 @@
1
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
2
+ ENV['RAILS_ENV'] ||= 'test'
3
+ require File.expand_path('../../config/environment', __FILE__)
4
+ # Prevent database truncation if the environment is production
5
+ abort('The Rails environment is running in production mode!') if Rails.env.production?
6
+ require 'spec_helper'
7
+ require 'rspec/rails'
8
+ require 'sidekiq_unique_jobs/testing'
9
+
10
+ Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
11
+
12
+ ActiveRecord::Migration.maintain_test_schema!
13
+
14
+ MOCK_REDIS ||= MockRedis.new
15
+
16
+ RSpec.configure do |config|
17
+ config.use_transactional_fixtures = true
18
+ config.infer_spec_type_from_file_location!
19
+ config.filter_rails_from_backtrace!
20
+ # config.filter_gems_from_backtrace("gem name")
21
+ end
@@ -0,0 +1,20 @@
1
+ RSpec.configure do |config|
2
+ config.expect_with :rspec do |expectations|
3
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
4
+ end
5
+
6
+ config.mock_with :rspec do |mocks|
7
+ mocks.verify_partial_doubles = true
8
+ end
9
+
10
+ config.shared_context_metadata_behavior = :apply_to_host_groups
11
+ config.filter_run_when_matching :focus
12
+ config.example_status_persistence_file_path = 'spec/examples.txt'
13
+ # config.disable_monkey_patching!
14
+
15
+ config.default_formatter = 'doc' if config.files_to_run.one?
16
+
17
+ config.profile_examples = 10
18
+ config.order = :random
19
+ Kernel.srand config.seed
20
+ end
@@ -0,0 +1,12 @@
1
+ RSpec.configure do |config|
2
+ config.before(:each) do |example|
3
+ if (sidekiq = example.metadata[:sidekiq])
4
+ sidekiq = :fake if sidekiq == true
5
+ Sidekiq::Testing.send("#{sidekiq}!")
6
+ end
7
+ end
8
+
9
+ config.after(:each) do |example|
10
+ Sidekiq::Testing.disable! unless example.metadata[:sidekiq].nil?
11
+ end
12
+ end
@@ -0,0 +1,4 @@
1
+ require 'rails_helper'
2
+
3
+ describe SimpleWorker do
4
+ end
@@ -1,7 +1,12 @@
1
1
  class MyUniqueJob
2
2
  include Sidekiq::Worker
3
- sidekiq_options queue: :customqueue, retry: true, unique: :until_executed,
4
- unique_expiration: 7_200, retry_count: 10
3
+ sidekiq_options(
4
+ queue: :customqueue,
5
+ retry: true,
6
+ unique: :until_executed,
7
+ unique_expiration: 7_200,
8
+ retry_count: 10
9
+ )
5
10
  def perform(_one, _two)
6
11
  end
7
12
  end
@@ -0,0 +1,17 @@
1
+ class MyUniqueJobWithFilterMethod
2
+ include Sidekiq::Worker
3
+ sidekiq_options queue: :customqueue,
4
+ retry: true,
5
+ backtrace: true,
6
+ unique: :until_executed,
7
+ unique_args: :filtered_args
8
+
9
+ def perform(*)
10
+ # NO-OP
11
+ end
12
+
13
+ def self.filtered_args(args)
14
+ options = args.extract_options!
15
+ [args.first, options['type']]
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ class MyUniqueJobWithFilterProc
2
+ include Sidekiq::Worker
3
+ sidekiq_options queue: :customqueue,
4
+ retry: true,
5
+ backtrace: true,
6
+ unique: :until_executed,
7
+ unique_args: (lambda do |args|
8
+ options = args.extract_options!
9
+ [args.first, options['type']]
10
+ end)
11
+
12
+ def perform(*)
13
+ # NO-OP
14
+ end
15
+ end
@@ -5,6 +5,6 @@ class NotifyWorker
5
5
  unique: :until_executed
6
6
 
7
7
  def perform(id, _blob)
8
- puts "NotifyWorker -- #{id}"
8
+ # puts "NotifyWorker -- #{id}"
9
9
  end
10
10
  end
@@ -117,6 +117,40 @@ RSpec.describe SidekiqUniqueJobs::Client::Middleware do
117
117
  end
118
118
  end
119
119
 
120
+ it 'does not push duplicate messages when unique_args are filtered with a proc' do
121
+ 10.times { MyUniqueJobWithFilterProc.perform_async(1) }
122
+ Sidekiq.redis { |c| expect(c.llen('queue:customqueue')).to eq(1) }
123
+
124
+ Sidekiq.redis(&:flushdb)
125
+ Sidekiq.redis { |c| expect(c.llen('queue:customqueue')).to eq(0) }
126
+
127
+ 10.times do
128
+ Sidekiq::Client.push(
129
+ 'class' => MyUniqueJobWithFilterProc,
130
+ 'queue' => 'customqueue',
131
+ 'args' => [1, type: 'value', some: 'not used']
132
+ )
133
+ end
134
+ Sidekiq.redis { |c| expect(c.llen('queue:customqueue')).to eq(1) }
135
+ end
136
+
137
+ it 'does not push duplicate messages when unique_args are filtered with a method' do
138
+ 10.times { MyUniqueJobWithFilterMethod.perform_async(1) }
139
+ Sidekiq.redis do |c|
140
+ expect(c.llen('queue:customqueue')).to eq(1)
141
+ end
142
+ Sidekiq.redis(&:flushdb)
143
+ Sidekiq.redis { |c| expect(c.llen('queue:customqueue')).to eq(0) }
144
+ 10.times do
145
+ Sidekiq::Client.push(
146
+ 'class' => MyUniqueJobWithFilterMethod,
147
+ 'queue' => 'customqueue',
148
+ 'args' => [1, type: 'value', some: 'not used']
149
+ )
150
+ end
151
+ Sidekiq.redis { |c| expect(c.llen('queue:customqueue')).to eq(1) }
152
+ end
153
+
120
154
  it 'does push duplicate messages to different queues' do
121
155
  Sidekiq::Client.push('class' => MyUniqueJob, 'queue' => 'customqueue', 'args' => [1, 2])
122
156
  Sidekiq::Client.push('class' => MyUniqueJob, 'queue' => 'customqueue2', 'args' => [1, 2])
@@ -13,7 +13,11 @@ RSpec.describe SidekiqUniqueJobs::Lock::UntilAndWhileExecuting do
13
13
  let(:callback) { -> {} }
14
14
  subject { described_class.new(item) }
15
15
  describe '#execute' do
16
- before { subject.lock(:client) }
16
+ before do
17
+ Sidekiq.redis(&:flushdb)
18
+ Sidekiq::Worker.clear_all
19
+ subject.lock(:client)
20
+ end
17
21
  let(:runtime_lock) { SidekiqUniqueJobs::Lock::WhileExecuting.new(item, nil) }
18
22
 
19
23
  it 'unlocks the unique key before yielding' do
@@ -15,26 +15,23 @@ RSpec.describe SidekiqUniqueJobs::Lock::UntilExecuted do
15
15
  context 'when yield fails with Sidekiq::Shutdown' do
16
16
  before do
17
17
  allow(subject).to receive(:after_yield_yield) { raise Sidekiq::Shutdown }
18
- end
19
-
20
- it 'raises Sidekiq::Shutdown' do
21
18
  allow(subject).to receive(:unlock).and_return(true)
22
19
  expect(subject).not_to receive(:unlock)
20
+ expect(subject.logger).to receive(:fatal)
23
21
  expect(empty_callback).not_to receive(:call)
24
- expect { subject.execute(empty_callback) }.to raise_error(Sidekiq::Shutdown)
25
22
  end
23
+
24
+ specify { expect { subject.execute(empty_callback) }.to raise_error(Sidekiq::Shutdown) }
26
25
  end
27
26
 
28
27
  context 'when yield fails with other errors' do
29
28
  before do
30
29
  allow(subject).to receive(:after_yield_yield) { raise 'Hell' }
31
- end
32
-
33
- it 'raises Sidekiq::Shutdown' do
34
30
  expect(subject).to receive(:unlock).and_return(true)
35
31
  expect(empty_callback).to receive(:call)
36
- expect { subject.execute(empty_callback) }.to raise_error('Hell')
37
32
  end
33
+
34
+ specify { expect { subject.execute(empty_callback) }.to raise_error('Hell') }
38
35
  end
39
36
  end
40
37
  end
@@ -13,7 +13,8 @@ RSpec.describe SidekiqUniqueJobs::OptionsWithFallback do
13
13
  .to receive(:warn)
14
14
  .with(
15
15
  'unique: true is no longer valid. Please set it to the type of lock required like: ' \
16
- '`unique: :until_executed`')
16
+ '`unique: :until_executed`'
17
+ )
17
18
 
18
19
  subject.unique_lock
19
20
  end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+ require 'mock_redis'
3
+
4
+ MOCK_REDIS ||= MockRedis.new
5
+
6
+ RSpec.describe SidekiqUniqueJobs::ScriptMock do
7
+ MD5_DIGEST ||= 'unique'.freeze
8
+ UNIQUE_KEY ||= 'uniquejobs:unique'.freeze
9
+ JID ||= 'fuckit'.freeze
10
+ ANOTHER_JID ||= 'anotherjid'.freeze
11
+
12
+ before do
13
+ SidekiqUniqueJobs.configure do |config|
14
+ config.redis_test_mode = :mock
15
+ end
16
+ Sidekiq::Worker.clear_all
17
+
18
+ keys = MOCK_REDIS.keys
19
+ if keys.respond_to?(:each)
20
+ keys.each do |key|
21
+ MOCK_REDIS.del(key)
22
+ end
23
+ else
24
+ MOCK_REDIS.del(keys)
25
+ end
26
+
27
+ allow(Redis).to receive(:new).and_return(MOCK_REDIS)
28
+ end
29
+
30
+ after do
31
+ SidekiqUniqueJobs.configure do |config|
32
+ config.redis_test_mode = :redis
33
+ end
34
+ end
35
+
36
+ subject { SidekiqUniqueJobs::Scripts }
37
+
38
+ def lock_for(seconds = 1, jid = JID, key = UNIQUE_KEY)
39
+ subject.call(:acquire_lock, nil, keys: [key], argv: [jid, seconds])
40
+ end
41
+
42
+ def unlock(key = UNIQUE_KEY, jid = JID)
43
+ subject.call(:release_lock, nil, keys: [key], argv: [jid])
44
+ end
45
+
46
+ describe '.acquire_lock' do
47
+ context 'when job is unique' do
48
+ specify { expect(lock_for).to eq(1) }
49
+ specify do
50
+ expect(lock_for(1)).to eq(1)
51
+ expect(Redis)
52
+ .to have_key(UNIQUE_KEY)
53
+ .for_seconds(1)
54
+ .with_value('fuckit')
55
+ sleep 1
56
+ expect(lock_for).to eq(1)
57
+ end
58
+
59
+ context 'when job is locked' do
60
+ before { expect(lock_for(10)).to eq(1) }
61
+ specify { expect(lock_for(5, 'anotherjid')).to eq(0) }
62
+ end
63
+ end
64
+
65
+ describe '.release_lock' do
66
+ context 'when job is locked by another jid' do
67
+ before { expect(lock_for(10, 'anotherjid')).to eq(1) }
68
+ specify { expect(unlock).to eq(0) }
69
+ after { unlock(UNIQUE_KEY, ANOTHER_JID) }
70
+ end
71
+
72
+ context 'when job is not locked at all' do
73
+ specify { expect(unlock).to eq(-1) }
74
+ end
75
+
76
+ context 'when job is locked by the same jid' do
77
+ specify do
78
+ expect(lock_for(10)).to eq(1)
79
+ expect(unlock).to eq(1)
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -6,6 +6,10 @@ RSpec.describe SidekiqUniqueJobs::Scripts do
6
6
  ANOTHER_JID ||= 'anotherjid'.freeze
7
7
 
8
8
  context 'class methods' do
9
+ before do
10
+ Sidekiq.redis(&:flushdb)
11
+ Sidekiq::Worker.clear_all
12
+ end
9
13
  subject { SidekiqUniqueJobs::Scripts }
10
14
 
11
15
  it { is_expected.to respond_to(:call).with(3).arguments }
@@ -6,14 +6,19 @@ require 'sidekiq/scheduled'
6
6
  RSpec.describe 'When Sidekiq::Testing is enabled' do
7
7
  describe 'when set to :fake!', sidekiq: :fake do
8
8
  before do
9
+ SidekiqUniqueJobs.configure do |config|
10
+ config.redis_test_mode = :redis
11
+ end
9
12
  Sidekiq.redis = REDIS
10
13
  Sidekiq.redis(&:flushdb)
14
+ Sidekiq::Worker.clear_all
11
15
  Sidekiq::Testing.server_middleware do |chain|
12
16
  chain.add SidekiqUniqueJobs::Server::Middleware
13
17
  end if Sidekiq::Testing.respond_to?(:server_middleware)
14
18
  end
15
19
 
16
20
  after do
21
+ Sidekiq.redis(&:flushdb)
17
22
  Sidekiq::Testing.server_middleware(&:clear) if Sidekiq::Testing.respond_to?(:server_middleware)
18
23
  end
19
24
 
@@ -27,18 +32,10 @@ RSpec.describe 'When Sidekiq::Testing is enabled' do
27
32
  expect(UntilExecutedJob.jobs.size).to eq(1)
28
33
  end
29
34
 
30
- # it 'does not push duplicate messages', sidekiq_ver: '>= 4' do
31
- # param = 'work'
32
- # expect(UntilExecutedJob.jobs.size).to eq(0)
33
- # expect(UntilExecutedJob.perform_async(param)).to_not be_nil
34
- # expect(Sidekiq::Queues['working'].size).to eq(1)
35
- # expect(UntilExecutedJob.perform_async(param)).to be_nil
36
- # expect(Sidekiq::Queues['working'].size).to eq(1)
37
- # end
38
-
39
35
  it 'unlocks jobs after draining a worker' do
40
36
  param = 'work'
41
37
  param2 = 'more work'
38
+
42
39
  expect(UntilExecutedJob.jobs.size).to eq(0)
43
40
  UntilExecutedJob.perform_async(param)
44
41
  UntilExecutedJob.perform_async(param2)
@@ -88,12 +85,11 @@ RSpec.describe 'When Sidekiq::Testing is enabled' do
88
85
  it 'handles clearing an empty worker queue' do
89
86
  param = 'work'
90
87
  UntilExecutedJob.perform_async(param)
91
- UntilExecutedJob.clear
88
+ UntilExecutedJob.drain
92
89
  expect(UntilExecutedJob.jobs.size).to eq(0)
93
90
  expect { UntilExecutedJob.clear }.not_to raise_error
94
91
  end
95
92
 
96
- # it 'unlocks jobs when all workers are cleared', :focus do
97
93
  it 'unlocks jobs when all workers are cleared' do
98
94
  param = 'work'
99
95
  expect(UntilExecutedJob.jobs.size).to eq(0)
@@ -22,7 +22,8 @@ RSpec.describe 'Sidekiq::Api' do
22
22
  'class' => JustAWorker,
23
23
  'queue' => 'testqueue',
24
24
  'args' => [foo: 'bar'],
25
- 'at' => (Date.today + 1).to_time.to_i)
25
+ 'at' => (Date.today + 1).to_time.to_i
26
+ )
26
27
  end
27
28
 
28
29
  def schedule_job
@@ -20,9 +20,8 @@ require 'celluloid_with_fallback'
20
20
  require 'sidekiq'
21
21
  require 'sidekiq/util'
22
22
  require 'sidekiq-unique-jobs'
23
- require 'sidekiq_unique_jobs/testing'
24
23
  require 'timecop'
25
-
24
+ require 'sidekiq_unique_jobs/testing'
26
25
  require 'sidekiq/simulator'
27
26
 
28
27
  Sidekiq::Testing.disable!
@@ -1,23 +1,28 @@
1
1
  RSpec.configure do |config|
2
2
  VERSION_REGEX = /(?<operator>[<>=]+)?\s?(?<version>(\d+.?)+)/m
3
3
  config.before(:each) do |example|
4
- Sidekiq.redis(&:flushdb)
5
- Sidekiq::Worker.clear_all
4
+ SidekiqUniqueJobs.configure do |config|
5
+ config.redis_test_mode = :redis
6
+ end
7
+
6
8
  if (sidekiq = example.metadata[:sidekiq])
7
9
  sidekiq = :fake if sidekiq == true
8
10
  Sidekiq::Testing.send("#{sidekiq}!")
9
11
  end
10
12
 
11
- sidekiq_ver = example.metadata[:sidekiq_ver]
12
- version, operator = VERSION_REGEX.match(sidekiq_ver.to_s) do |m|
13
- raise 'Please specify how to compare the version with >= or < or =' unless m[:operator]
14
- [m[:version], m[:operator]]
15
- end
13
+ if (sidekiq_ver = example.metadata[:sidekiq_ver])
14
+ VERSION_REGEX.match(sidekiq_ver.to_s) do |match|
15
+ version = match[:version]
16
+ operator = match[:operator]
16
17
 
17
- unless Sidekiq::VERSION.send(operator, version)
18
- skip('Skipped due to version check (requirement was that sidekiq version is ' \
19
- "#{operator} #{version}; was #{Sidekiq::VERSION})")
20
- end if version && operator
18
+ raise 'Please specify how to compare the version with >= or < or =' unless operator
19
+
20
+ unless Sidekiq::VERSION.send(operator, version)
21
+ skip('Skipped due to version check (requirement was that sidekiq version is ' \
22
+ "#{operator} #{version}; was #{Sidekiq::VERSION})")
23
+ end
24
+ end
25
+ end
21
26
  end
22
27
 
23
28
  config.after(:each) do |example|