active_job_resque_solo 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f6e6f9c6d697e4099b2c8f301c6be99352942def
4
- data.tar.gz: de97bbe74f0173a9893ccb391f249ad39a3ff035
3
+ metadata.gz: f760861754e12e2de29870716638d38d93ceaa09
4
+ data.tar.gz: 553f61b9c2b9653afc9217dc45f8439273311a06
5
5
  SHA512:
6
- metadata.gz: 57fca794bd86021a0478302cd1887b200c640b3ed964dadea99973130497cd06ff5a57e8d0d029a7e539b8fe0030e19c33915170ca00513c51d36f620598b5aa
7
- data.tar.gz: 8bdf98dd0cf360097cad44cda342140b3afc990a44e96df81d78d41925517f51235ee594eea3aec78382cbbcd0c3637543daf3b40b60907057a2a0ede2c7cd99
6
+ metadata.gz: e558f3fef406f0bf9b39740b697c05e889dda701a6aecc24684c6dd5106ce9b28ffd163da394ede37bcd972d9f9590033f1ed51ff786dc7f0ae4fe7c965fe7ab
7
+ data.tar.gz: f671a673aa14aacece35e289639331fd3dee4b5a723c7fa4efdce1ec10c806d71e194f2b0820aa6f8dc11e457e4c668bfb8cf998fad43c3c0c7829c05c15b1f2
data/README.md CHANGED
@@ -38,8 +38,8 @@ Solo will prevent a new instance of the job from being enqueued.
38
38
 
39
39
  You can control which named arguments are used to determine uniqueness in the queue:
40
40
 
41
- `solo_only_args`
42
- `solo_except_args`
41
+ * `solo_only_args`
42
+ * `solo_except_args`
43
43
 
44
44
  ```ruby
45
45
  class MyJob < ActiveJob::Base
@@ -79,9 +79,9 @@ While this plugin will greatly reduce duplicate instances of a job from being
79
79
  enqueued, there are two scenarios where duplicates may still be enqueued,
80
80
  so be sure to check out other gems for locking if your job is not idempotent.
81
81
 
82
- 1. When multiple processes simultaneously attempt to enqueue the same job two or
83
- more may be enqueued.
84
- 2. If your queue has many jobs, and workers remove a job during while Solo scans
82
+ 1. When multiple processes simultaneously attempt to enqueue the same job, two or
83
+ more instances may be enqueued.
84
+ 2. If your queue has many jobs, and workers remove a job while Solo scans
85
85
  the queue, it's possible for the original enqueued job to be missed. Solo will allow
86
86
  the new instance of the job to be enqueued.
87
87
 
@@ -1,115 +1,30 @@
1
+ require_relative 'solo/inspector'
2
+
1
3
  module ActiveJob
2
4
  module Plugins
3
5
  module Resque
4
6
  module Solo
5
-
6
7
  def self.included(base_class)
7
8
  base_class.extend(ClassMethods)
8
9
 
9
10
  base_class.around_enqueue do |job, block|
10
- base_class.solo_around_enqueue(job, block)
11
+ base_class.solo_inspector.around_enqueue(job, block)
11
12
  end
12
13
  end
13
14
 
14
15
  module ClassMethods
15
- def resque_present?
16
- ActiveJob::Base.queue_adapter == ActiveJob::QueueAdapters::ResqueAdapter
17
- end
18
-
19
- def solo_around_enqueue(job, block)
20
- if resque_present?
21
- # always ignore the ActiveJob symbol hash key.
22
- @solo_except_args ||= []
23
- @solo_except_args << "_aj_symbol_keys" unless @solo_except_args.include?("_aj_symbol_keys")
24
-
25
- if !solo_job_enqueued?(job) && !solo_job_executing?(job)
26
- block.call
27
- end
28
- else
29
- # if resque is not present, always enqueue
30
- block.call
31
- end
32
- end
33
-
34
16
  def solo_only_args(*args)
35
17
  @solo_only_args = args.compact.map(&:to_s).uniq
36
- raise "Missing arguments for solo_only_args" if @solo_only_args.empty?
18
+ raise ArgumentError, "solo_only_args requires one or more field names" if @solo_only_args.empty?
37
19
  end
38
20
 
39
21
  def solo_except_args(*args)
40
22
  @solo_except_args = args.compact.map(&:to_s).uniq
41
- raise "Missing arguments for solo_except_args" if @solo_except_args.empty?
42
- end
43
-
44
- def solo_job_enqueued?(job)
45
- size = ::Resque.size(job.queue_name)
46
- return false if size.zero?
47
-
48
- page_size = 250
49
- pages = (size/page_size).to_i + 1
50
- jobs = []
51
-
52
- # It's possible for this loop to skip jobs if they
53
- # are dequeued while the loop is in progress.
54
- (0..pages).each do |i|
55
- page_start = i * page_size
56
- page = ::Resque.peek(job.queue_name, page_start, page_size)
57
- break if page.empty?
58
- jobs += page
59
- end
60
-
61
- job_class, job_arguments = solo_job(job)
62
-
63
- (jobs.size-1).downto(0) do |i|
64
- scheduled_job = jobs[i]
65
- return true if solo_job_enqueued_with_args?(job_class, job_arguments, scheduled_job)
66
- end
67
- false
68
- end
69
-
70
- def solo_job_executing?(job)
71
- job_class, job_arguments = solo_job(job)
72
-
73
- ::Resque.workers.any? do |worker|
74
- processing = worker.processing
75
- next false if processing.blank?
76
- args = processing["payload"]["args"][0]
77
- solo_job_with_args_eq?(job_class, job_arguments, args)
78
- end
79
- end
80
-
81
- def solo_job_enqueued_with_args?(job_class, job_arguments, scheduled_job)
82
- args = scheduled_job["args"][0]
83
- solo_job_with_args_eq?(job_class, job_arguments, args)
23
+ raise ArgumentError, "solo_except_args requires one or more field names" if @solo_except_args.empty?
84
24
  end
85
25
 
86
- def solo_job_with_args_eq?(job_class, job_arguments, wrapper_args)
87
- return false if wrapper_args['job_class'] != job_class
88
- encoded_arguments = wrapper_args['arguments']
89
- encoded_arguments = solo_job_args(encoded_arguments)
90
- encoded_arguments == job_arguments
91
- end
92
-
93
- def solo_job(job)
94
- job_arguments = ActiveJob::Arguments.serialize(job.arguments)
95
- job_arguments = solo_job_args(job_arguments)
96
- job_class = job.class.name
97
- [ job_class, job_arguments ]
98
- end
99
-
100
- def solo_job_args(args)
101
- if args.present?
102
- args.map do |arg|
103
- if arg.is_a? Hash
104
- arg.keep_if { |k,v| @solo_only_args.include?(k.to_s) } if @solo_only_args.present?
105
- arg.keep_if { |k,v| !@solo_except_args.include?(k.to_s) } if @solo_except_args.present?
106
- end
107
-
108
- arg
109
- end
110
- end
111
-
112
- args
26
+ def solo_inspector
27
+ @solo_inspector ||= Inspector.new(@solo_only_args, @solo_except_args)
113
28
  end
114
29
  end
115
30
  end
@@ -0,0 +1,104 @@
1
+ module ActiveJob
2
+ module Plugins
3
+ module Resque
4
+ module Solo
5
+ class Inspector
6
+
7
+ def initialize(only_args, except_args)
8
+ @only_args = only_args
9
+ @except_args = except_args || []
10
+ # always ignore the ActiveJob symbol hash key.
11
+ @except_args << "_aj_symbol_keys" unless @except_args.include?("_aj_symbol_keys")
12
+ end
13
+
14
+ def self.resque_present?
15
+ ActiveJob::Base.queue_adapter == ActiveJob::QueueAdapters::ResqueAdapter
16
+ end
17
+
18
+ def around_enqueue(job, block)
19
+ if Inspector::resque_present?
20
+
21
+ if !job_enqueued?(job) && !job_executing?(job)
22
+ block.call
23
+ end
24
+ else
25
+ # if resque is not present, always enqueue
26
+ block.call
27
+ end
28
+ end
29
+
30
+ def job_enqueued?(job)
31
+ size = ::Resque.size(job.queue_name)
32
+ return false if size.zero?
33
+
34
+ page_size = 250
35
+ pages = (size/page_size).to_i + 1
36
+ jobs = []
37
+
38
+ # It's possible for this loop to skip jobs if they
39
+ # are dequeued while the loop is in progress.
40
+ (0..pages).each do |i|
41
+ page_start = i * page_size
42
+ page = ::Resque.peek(job.queue_name, page_start, page_size)
43
+ break if page.empty?
44
+ jobs += page
45
+ end
46
+
47
+ job_class, job_arguments = job(job)
48
+
49
+ (jobs.size-1).downto(0) do |i|
50
+ scheduled_job = jobs[i]
51
+ return true if job_enqueued_with_args?(job_class, job_arguments, scheduled_job)
52
+ end
53
+ false
54
+ end
55
+
56
+ def job_executing?(job)
57
+ job_class, job_arguments = job(job)
58
+
59
+ ::Resque.workers.any? do |worker|
60
+ processing = worker.processing
61
+ next false if processing.blank?
62
+ args = processing["payload"]["args"][0]
63
+ job_with_args_eq?(job_class, job_arguments, args)
64
+ end
65
+ end
66
+
67
+ def job_enqueued_with_args?(job_class, job_arguments, scheduled_job)
68
+ args = scheduled_job["args"][0]
69
+ job_with_args_eq?(job_class, job_arguments, args)
70
+ end
71
+
72
+ def job_with_args_eq?(job_class, job_arguments, wrapper_args)
73
+ return false if wrapper_args['job_class'] != job_class
74
+ encoded_arguments = wrapper_args['arguments']
75
+ encoded_arguments = job_args(encoded_arguments)
76
+ encoded_arguments == job_arguments
77
+ end
78
+
79
+ def job(job)
80
+ job_arguments = ActiveJob::Arguments.serialize(job.arguments)
81
+ job_arguments = job_args(job_arguments)
82
+ job_class = job.class.name
83
+ [ job_class, job_arguments ]
84
+ end
85
+
86
+ def job_args(args)
87
+ if args.present?
88
+ args.map do |arg|
89
+ if arg.is_a? Hash
90
+ arg.keep_if { |k,v| @only_args.include?(k.to_s) } if @only_args.present?
91
+ arg.keep_if { |k,v| !@except_args.include?(k.to_s) } if @except_args.present?
92
+ end
93
+
94
+ arg
95
+ end
96
+ end
97
+
98
+ args
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -1,3 +1,3 @@
1
1
  module ActiveJobResqueSolo
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_job_resque_solo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Phillip Kinkade
@@ -106,6 +106,7 @@ files:
106
106
  - bin/console
107
107
  - bin/setup
108
108
  - lib/active_job/plugins/resque/solo.rb
109
+ - lib/active_job/plugins/resque/solo/inspector.rb
109
110
  - lib/active_job_resque_solo.rb
110
111
  - lib/active_job_resque_solo/version.rb
111
112
  homepage: