hirefire-resource 0.10.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +171 -0
  3. data/LICENSE +21 -66
  4. data/README.md +34 -78
  5. data/hirefire-resource.gemspec +21 -15
  6. data/lib/hirefire/configuration.rb +31 -0
  7. data/lib/hirefire/errors/job_queue_latency_unsupported.rb +12 -0
  8. data/lib/hirefire/errors.rb +9 -0
  9. data/lib/hirefire/hirefire.rb +15 -0
  10. data/lib/hirefire/macro/bunny.rb +55 -55
  11. data/lib/hirefire/macro/delayed_job.rb +80 -60
  12. data/lib/hirefire/macro/deprecated/bunny.rb +85 -0
  13. data/lib/hirefire/macro/deprecated/delayed_job.rb +62 -0
  14. data/lib/hirefire/macro/deprecated/good_job.rb +32 -0
  15. data/lib/hirefire/macro/deprecated/que.rb +76 -0
  16. data/lib/hirefire/macro/deprecated/queue_classic.rb +28 -0
  17. data/lib/hirefire/macro/deprecated/resque.rb +47 -0
  18. data/lib/hirefire/macro/deprecated/sidekiq.rb +138 -0
  19. data/lib/hirefire/macro/good_job.rb +48 -13
  20. data/lib/hirefire/macro/que.rb +56 -36
  21. data/lib/hirefire/macro/queue_classic.rb +86 -0
  22. data/lib/hirefire/macro/resque.rb +111 -25
  23. data/lib/hirefire/macro/sidekiq.rb +324 -74
  24. data/lib/hirefire/macro/solid_queue.rb +166 -0
  25. data/lib/hirefire/middleware.rb +50 -103
  26. data/lib/hirefire/railtie.rb +1 -1
  27. data/lib/hirefire/resource.rb +1 -47
  28. data/lib/hirefire/utility.rb +22 -0
  29. data/lib/hirefire/version.rb +5 -0
  30. data/lib/hirefire/web.rb +151 -0
  31. data/lib/hirefire/worker.rb +39 -0
  32. data/lib/hirefire-resource.rb +4 -9
  33. metadata +50 -25
  34. data/.github/workflows/main.yml +0 -16
  35. data/.gitignore +0 -5
  36. data/Gemfile +0 -10
  37. data/Gemfile.lock +0 -52
  38. data/Rakefile +0 -4
  39. data/bin/rake +0 -29
  40. data/lib/hirefire/macro/qc.rb +0 -23
  41. data/lib/hirefire/macro/qu.rb +0 -27
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HireFire
4
+ module Macro
5
+ module Deprecated
6
+ # Provides backward compatibility with the deprecated Sidekiq macro.
7
+ # For new implementations, refer to {HireFire::Macro::Sidekiq}.
8
+ module Sidekiq
9
+ # Calculates the latency (in seconds) for the specified Sidekiq queue.
10
+ #
11
+ # The method uses the Sidekiq::Queue class to obtain the latency of a queue, which
12
+ # is the duration since the oldest job in the queue was enqueued.
13
+ #
14
+ # @param queue [String, Symbol] The name of the queue to measure latency.
15
+ # Defaults to "default" if no queue name is provided.
16
+ # @return [Float] The latency of the queue in seconds.
17
+ # @example Calculating latency for the default queue
18
+ # HireFire::Macro::Sidekiq.latency
19
+ # @example Calculating latency for the "critical" queue
20
+ # HireFire::Macro::Sidekiq.latency("critical")
21
+ def latency(queue = "default")
22
+ ::Sidekiq::Queue.new(queue.to_s).latency
23
+ end
24
+
25
+ # Counts the number of jobs in the specified Sidekiq queue(s).
26
+ #
27
+ # The method supports various options to include or exclude jobs from
28
+ # specific sets like scheduled, retries, or in-progress jobs.
29
+ #
30
+ # @param args [Array<String, Symbol, Hash>] Queue names to count jobs in and an optional hash of options.
31
+ # Pass an empty array or no arguments to count jobs in all queues.
32
+ # The last argument can be a Hash of options to modify the count behavior.
33
+ # Possible keys are :skip_scheduled, :skip_retries, :skip_working.
34
+ # These keys are booleans which default to false.
35
+ # @return [Integer] Total number of jobs in the specified queues.
36
+ # @example Counting jobs in all queues
37
+ # HireFire::Macro::Sidekiq.queue
38
+ # @example Counting jobs in the "default" and "critical" queues
39
+ # HireFire::Macro::Sidekiq.queue("default", "critical")
40
+ # @example Counting jobs in the "default" queue, excluding scheduled jobs
41
+ # HireFire::Macro::Sidekiq.queue("default", skip_scheduled: true)
42
+ # @example Counting jobs in the "default" queue, excluding retryable jobs
43
+ # HireFire::Macro::Sidekiq.queue("default", skip_retries: true)
44
+ # @example Counting jobs in the "default" queue, excluding in-progress jobs
45
+ # HireFire::Macro::Sidekiq.queue("default", skip_working: true)
46
+ def queue(*args)
47
+ require "sidekiq/api"
48
+
49
+ args.flatten!
50
+
51
+ options = args.last.is_a?(Hash) ? args.pop : {}
52
+ queues = args.map(&:to_s)
53
+ all_queues = ::Sidekiq::Queue.all.map(&:name)
54
+ queues = all_queues if queues.empty?
55
+
56
+ if fast_lookup_capable?(queues, all_queues)
57
+ fast_lookup(options)
58
+ else
59
+ dynamic_lookup(queues, options)
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def fast_lookup_capable?(queues, all_queues)
66
+ # When no queue names are provided (or all of them are), we know we
67
+ # can perform much faster counts using Sidekiq::Stats and Redis
68
+ queues.sort == all_queues.sort
69
+ end
70
+
71
+ def fast_lookup(options)
72
+ stats = ::Sidekiq::Stats.new
73
+
74
+ in_queues = stats.enqueued
75
+
76
+ if !options[:skip_scheduled]
77
+ in_schedule = ::Sidekiq.redis { |c| c.zcount("schedule", "-inf", Time.now.to_f) }
78
+ end
79
+
80
+ if !options[:skip_retries]
81
+ in_retry = ::Sidekiq.redis { |c| c.zcount("retry", "-inf", Time.now.to_f) }
82
+ end
83
+
84
+ if !options[:skip_working]
85
+ in_progress = stats.workers_size
86
+ end
87
+
88
+ [in_queues, in_schedule, in_retry, in_progress].compact.inject(&:+)
89
+ end
90
+
91
+ def dynamic_lookup(queues, options)
92
+ in_queues = queues.inject(0) do |memo, name|
93
+ memo += ::Sidekiq::Queue.new(name).size
94
+ memo
95
+ end
96
+
97
+ if !options[:skip_scheduled]
98
+ max = options[:max_scheduled]
99
+
100
+ # For potentially long-running loops, compare all jobs against
101
+ # time when the set snapshot was taken to avoid incorrect counts.
102
+ now = Time.now
103
+
104
+ in_schedule = ::Sidekiq::ScheduledSet.new.inject(0) do |memo, job|
105
+ memo += 1 if queues.include?(job["queue"]) && job.at <= now
106
+ break memo if max && memo >= max
107
+ memo
108
+ end
109
+ end
110
+
111
+ if !options[:skip_retries]
112
+ now = Time.now
113
+
114
+ in_retry = ::Sidekiq::RetrySet.new.inject(0) do |memo, job|
115
+ memo += 1 if queues.include?(job["queue"]) && job.at <= now
116
+ memo
117
+ end
118
+ end
119
+
120
+ now = Time.now
121
+ now_as_i = now.to_i
122
+
123
+ if !options[:skip_working]
124
+ in_progress = ::Sidekiq::Workers.new.count do |key, tid, job|
125
+ if Gem::Version.new(::Sidekiq::VERSION) >= Gem::Version.new("7.0.0")
126
+ queues.include?(job.queue) && job.run_at <= now
127
+ else
128
+ queues.include?(job["queue"]) && job["run_at"] <= now_as_i
129
+ end
130
+ end
131
+ end
132
+
133
+ [in_queues, in_schedule, in_retry, in_progress].compact.inject(&:+)
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -1,25 +1,60 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "deprecated/good_job"
4
+
3
5
  module HireFire
4
6
  module Macro
5
7
  module GoodJob
8
+ extend HireFire::Macro::Deprecated::GoodJob
9
+ extend HireFire::Utility
6
10
  extend self
7
11
 
8
- # Queries the PostgreSQL database through GoodJob in order to
9
- # count the amount of jobs in the specified queue.
10
- #
11
- # @example Queue Macro Usage
12
- # HireFire::Macro::GoodJob.queue # counts all queues.
13
- # HireFire::Macro::GoodJob.queue("email") # counts the `email` queue.
12
+ # Calculates the maximum job queue latency using GoodJob. If no queues are specified, it
13
+ # measures latency across all available queues.
14
14
  #
15
- # @param [String] queue the queue name to count. (default: nil # gets all queues)
16
- # @return [Integer] the number of jobs in the queue(s).
15
+ # @param queues [Array<String, Symbol>] (optional) Names of the queues for latency
16
+ # measurement. If not provided, latency is measured across all queues.
17
+ # @return [Float] Maximum job queue latency in seconds.
18
+ # @example Calculate latency across all queues
19
+ # HireFire::Macro::GoodJob.job_queue_latency
20
+ # @example Calculate latency for the "default" queue
21
+ # HireFire::Macro::GoodJob.job_queue_latency(:default)
22
+ # @example Calculate latency across "default" and "mailer" queues
23
+ # HireFire::Macro::GoodJob.job_queue_latency(:default, :mailer)
24
+ def job_queue_latency(*queues)
25
+ queues = normalize_queues(queues, allow_empty: true)
26
+ query = ::GoodJob::Execution
27
+ query = query.where(queue_name: queues) if queues.any?
28
+ query = query.where(finished_at: nil)
29
+ query = query.where(scheduled_at: ..Time.now).or(query.where(scheduled_at: nil))
30
+ query = query.order(scheduled_at: :asc, created_at: :asc)
31
+
32
+ if (job = query.first)
33
+ Time.now - (job.scheduled_at || job.created_at)
34
+ else
35
+ 0.0
36
+ end
37
+ end
38
+
39
+ # Calculates the total job queue size using GoodJob. If no queues are specified, it
40
+ # measures size across all available queues.
17
41
  #
18
- def queue(*queues)
19
- base_class = defined?(::GoodJob::Execution) ? ::GoodJob::Execution : ::GoodJob::Job
20
- scope = base_class.only_scheduled.unfinished
21
- scope = scope.where(queue_name: queues) if queues.any?
22
- scope.count
42
+ # @param queues [Array<String, Symbol>] (optional) Names of the queues for size measurement.
43
+ # If not provided, size is measured across all queues.
44
+ # @return [Integer] Total job queue size.
45
+ # @example Calculate size across all queues
46
+ # HireFire::Macro::GoodJob.job_queue_size
47
+ # @example Calculate size for the "default" queue
48
+ # HireFire::Macro::GoodJob.job_queue_size(:default)
49
+ # @example Calculate size across "default" and "mailer" queues
50
+ # HireFire::Macro::GoodJob.job_queue_size(:default, :mailer)
51
+ def job_queue_size(*queues)
52
+ queues = normalize_queues(queues, allow_empty: true)
53
+ query = ::GoodJob::Execution
54
+ query = query.where(queue_name: queues) if queues.any?
55
+ query = query.where(finished_at: nil)
56
+ query = query.where(scheduled_at: ..Time.now).or(query.where(scheduled_at: nil))
57
+ query.count
23
58
  end
24
59
  end
25
60
  end
@@ -1,55 +1,75 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "deprecated/que"
4
+
3
5
  module HireFire
4
6
  module Macro
5
7
  module Que
8
+ extend HireFire::Macro::Deprecated::Que
9
+ extend HireFire::Utility
6
10
  extend self
7
11
 
8
- # Queries the PostgreSQL database through Que in order to
9
- # count the amount of jobs in the specified queue.
10
- #
11
- # @example Queue Macro Usage
12
- # HireFire::Macro::Que.queue # counts all queues.
13
- # HireFire::Macro::Que.queue("email") # counts the `email` queue.
14
- #
15
- # @param [String] queue the queue name to count. (default: nil # gets all queues)
16
- # @return [Integer] the number of jobs in the queue(s).
12
+ # Calculates the maximum job queue latency using Que. If no queues are specified, it
13
+ # measures latency across all available queues.
17
14
  #
18
- def queue(*queues)
19
- query = queues.empty? && base_query || base_query + " AND queue IN (#{names(queues)})"
20
- results = ::Que.execute(query).first
21
- (results[:total] || results["total"]).to_i
15
+ # @param queues [Array<String, Symbol>] (optional) Names of the queues for latency
16
+ # measurement. If not provided, latency is measured across all queues.
17
+ # @return [Float] Maximum job queue latency in seconds.
18
+ # @example Calculate latency across all queues
19
+ # HireFire::Macro::Que.job_queue_latency
20
+ # @example Calculate latency for the "default" queue
21
+ # HireFire::Macro::Que.job_queue_latency(:default)
22
+ # @example Calculate latency across "default" and "mailer" queues
23
+ # HireFire::Macro::Que.job_queue_latency(:default, :mailer)
24
+ def job_queue_latency(*queues)
25
+ query = <<~SQL
26
+ SELECT run_at FROM que_jobs
27
+ WHERE run_at <= NOW()
28
+ AND finished_at IS NULL
29
+ AND expired_at IS NULL
30
+ #{filter_by_queues_if_any(queues)}
31
+ ORDER BY run_at ASC LIMIT 1
32
+ SQL
33
+
34
+ result = ::Que.execute(query).first
35
+ result ? (Time.now - result[:run_at].to_time) : 0.0
22
36
  end
23
37
 
24
- private
38
+ # Calculates the total job queue size using Que. If no queues are specified, it
39
+ # measures size across all available queues.
40
+ #
41
+ # @param queues [Array<String, Symbol>] (optional) Names of the queues for size measurement.
42
+ # If not provided, size is measured across all queues.
43
+ # @return [Integer] Total job queue size.
44
+ # @example Calculate size across all queues
45
+ # HireFire::Macro::Que.job_queue_size
46
+ # @example Calculate size for the "default" queue
47
+ # HireFire::Macro::Que.job_queue_size(:default)
48
+ # @example Calculate size across "default" and "mailer" queues
49
+ # HireFire::Macro::Que.job_queue_size(:default, :mailer)
50
+ def job_queue_size(*queues)
51
+ query = <<~SQL
52
+ SELECT COUNT(*) AS total FROM que_jobs
53
+ WHERE run_at <= NOW()
54
+ AND finished_at IS NULL
55
+ AND expired_at IS NULL
56
+ #{filter_by_queues_if_any(queues)}
57
+ SQL
25
58
 
26
- def base_query
27
- return QUE_V0_QUERY if defined?(::Que::Version)
28
- return QUE_V1_QUERY if defined?(::Que::VERSION)
29
- raise "Couldn't find Que version"
59
+ ::Que.execute(query).first.fetch(:total).to_i
30
60
  end
31
61
 
32
- def names(queues)
33
- queues.map { |queue| "'#{queue}'" }.join(",")
34
- end
62
+ private
35
63
 
36
- def query_const(query)
37
- query.gsub(/\s+/, " ").strip.freeze
64
+ def filter_by_queues_if_any(queues)
65
+ queues = normalize_queues(queues, allow_empty: true)
66
+ queues = queues.map(&method(:sanitize_sql)).join(", ")
67
+ queues.empty? ? "" : "AND queue IN (#{queues})"
38
68
  end
39
69
 
40
- QUE_V0_QUERY = query_const(<<-QUERY)
41
- SELECT COUNT(*) AS total
42
- FROM que_jobs
43
- WHERE run_at < NOW()
44
- QUERY
45
-
46
- QUE_V1_QUERY = query_const(<<-QUERY)
47
- SELECT COUNT(*) AS total
48
- FROM que_jobs
49
- WHERE finished_at IS NULL
50
- AND expired_at IS NULL
51
- AND run_at <= NOW()
52
- QUERY
70
+ def sanitize_sql(value)
71
+ "'" + value.to_s.gsub(/['"\\]/, '\\\\\\&') + "'"
72
+ end
53
73
  end
54
74
  end
55
75
  end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "deprecated/queue_classic"
4
+
5
+ module HireFire
6
+ module Macro
7
+ module QC
8
+ extend HireFire::Macro::Deprecated::QC
9
+ extend HireFire::Utility
10
+ extend self
11
+
12
+ # Calculates the maximum job queue latency using Queue Classic. If no queues are specified, it
13
+ # measures latency across all available queues.
14
+ #
15
+ # @param queues [Array<String, Symbol>] (optional) Names of the queues for latency
16
+ # measurement. If not provided, latency is measured across all queues.
17
+ # @return [Float] Maximum job queue latency in seconds.
18
+ # @example Calculate latency across all queues
19
+ # HireFire::Macro::QC.job_queue_latency
20
+ # @example Calculate latency for the "default" queue
21
+ # HireFire::Macro::QC.job_queue_latency(:default)
22
+ # @example Calculate latency across "default" and "mailer" queues
23
+ # HireFire::Macro::QC.job_queue_latency(:default, :mailer)
24
+ def job_queue_latency(*queues)
25
+ queues = normalize_queues(queues, allow_empty: true)
26
+
27
+ query = <<~SQL
28
+ SELECT EXTRACT(EPOCH FROM (now() - scheduled_at)) AS latency
29
+ FROM #{::QC.table_name}
30
+ WHERE scheduled_at <= now()
31
+ #{filter_by_queues_if_any(queues)}
32
+ ORDER BY scheduled_at ASC
33
+ LIMIT 1
34
+ SQL
35
+
36
+ connection = ::QC.default_conn_adapter
37
+
38
+ result = if queues.any?
39
+ connection.execute(query, "{#{queues.to_a.join(",")}}")
40
+ else
41
+ connection.execute(query)
42
+ end
43
+
44
+ (result && result["latency"]) ? result["latency"].to_f : 0.0
45
+ end
46
+
47
+ # Calculates the total job queue size using Queue Classic. If no queues are specified, it
48
+ # measures size across all available queues.
49
+ #
50
+ # @param queues [Array<String, Symbol>] (optional) Names of the queues for size measurement.
51
+ # If not provided, size is measured across all queues.
52
+ # @return [Integer] Total job queue size.
53
+ # @example Calculate size across all queues
54
+ # HireFire::Macro::QC.job_queue_size
55
+ # @example Calculate size for the "default" queue
56
+ # HireFire::Macro::QC.job_queue_size(:default)
57
+ # @example Calculate size across "default" and "mailer" queues
58
+ # HireFire::Macro::QC.job_queue_size(:default, :mailer)
59
+ def job_queue_size(*queues)
60
+ queues = normalize_queues(queues, allow_empty: true)
61
+
62
+ query = <<~SQL
63
+ SELECT COUNT(*) FROM #{::QC.table_name}
64
+ WHERE scheduled_at <= now()
65
+ #{filter_by_queues_if_any(queues)}
66
+ SQL
67
+
68
+ connection = ::QC.default_conn_adapter
69
+
70
+ result = if queues.any?
71
+ connection.execute(query, "{#{queues.to_a.join(",")}}")
72
+ else
73
+ connection.execute(query)
74
+ end
75
+
76
+ result["count"].to_i
77
+ end
78
+
79
+ private
80
+
81
+ def filter_by_queues_if_any(queues)
82
+ queues.any? ? "AND q_name = ANY($1::text[])" : ""
83
+ end
84
+ end
85
+ end
86
+ end
@@ -1,43 +1,129 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "deprecated/resque"
4
+
3
5
  module HireFire
4
6
  module Macro
5
7
  module Resque
8
+ extend HireFire::Errors::JobQueueLatencyUnsupported
9
+ extend HireFire::Macro::Deprecated::Resque
10
+ extend HireFire::Utility
6
11
  extend self
7
12
 
8
- # Counts the amount of jobs in the (provided) Resque queue(s).
9
- #
10
- # @example Resque Macro Usage
11
- # HireFire::Macro::Resque.queue # all queues
12
- # HireFire::Macro::Resque.queue("email") # only email queue
13
- # HireFire::Macro::Resque.queue("audio", "video") # audio and video queues
14
- #
15
- # @param [Array] queues provide one or more queue names, or none for "all".
16
- # @return [Integer] the number of jobs in the queue(s).
13
+ SIZE_METHODS = [
14
+ :enqueued_size,
15
+ :working_size,
16
+ :scheduled_size
17
+ ].freeze
18
+
19
+ # Calculates the maximum job queue size using Resque. If no queues are specified, it
20
+ # measures size across all available queues.
17
21
  #
18
- def queue(*queues)
19
- queues = queues.flatten.map(&:to_s)
20
- queues = ::Resque.queues if queues.empty?
22
+ # @param queues [Array<String, Symbol>] (optional) Names of the queues for size measurement.
23
+ # If not provided, size is measured across all queues.
24
+ # @return [Integer] Total job queue size.
25
+ # @example Calculate size across all queues
26
+ # HireFire::Macro::Resque.job_queue_size
27
+ # @example Calculate size for the "default" queue
28
+ # HireFire::Macro::Resque.job_queue_size(:default)
29
+ # @example Calculate size across the "default" and "mailer" queues
30
+ # HireFire::Macro::Resque.job_queue_size(:default, :mailer)
31
+ def job_queue_size(*queues)
32
+ queues = normalize_queues(queues, allow_empty: true)
33
+
34
+ SIZE_METHODS.sum do |size_method|
35
+ method(size_method).call(queues)
36
+ end
37
+ end
21
38
 
22
- return 0 if queues.empty?
39
+ private
23
40
 
24
- redis = ::Resque.redis
25
- ids = Array(redis.smembers(:workers)).compact
26
- raw_jobs = redis.pipelined do |redis|
27
- ids.map { |id| redis.get("worker:#{id}") }
41
+ def enqueued_size(queues)
42
+ queues = registered_queues if queues.empty?
43
+
44
+ ::Resque.redis.pipelined do |pipeline|
45
+ queues.each do |queue|
46
+ pipeline.llen("queue:#{queue}")
47
+ end
48
+ end.sum
49
+ end
50
+
51
+ def working_size(queues)
52
+ ids = ::Resque.redis.smembers(:workers).compact
53
+
54
+ workers = ::Resque.redis.pipelined do |pipeline|
55
+ ids.each do |id|
56
+ pipeline.get("worker:#{id}")
57
+ end
58
+ end.compact
59
+
60
+ if queues.empty?
61
+ workers.count
62
+ else
63
+ workers.count do |worker|
64
+ queues.include?(::Resque.decode(worker)["queue"])
65
+ end
28
66
  end
29
- jobs = raw_jobs.map { |raw_job| ::Resque.decode(raw_job) || {} }
67
+ end
30
68
 
31
- in_queues = redis.pipelined do |redis|
32
- queues.map { |queue| redis.llen("queue:#{queue}") }
33
- end.map(&:to_i).inject(&:+)
69
+ def scheduled_size(queues)
70
+ cursor = 0
71
+ batch = 1000
72
+ total_size = 0
73
+ current_time = Time.now.to_i
34
74
 
35
- in_progress = jobs.inject(0) do |memo, job|
36
- memo += 1 if queues.include?(job["queue"])
37
- memo
75
+ loop do
76
+ timestamps = ::Resque.redis.zrangebyscore(
77
+ "delayed_queue_schedule",
78
+ "-inf",
79
+ current_time,
80
+ limit: [cursor, batch]
81
+ )
82
+
83
+ break if timestamps.empty?
84
+
85
+ if queues.empty?
86
+ total_size += ::Resque.redis.pipelined do |pipeline|
87
+ timestamps.each do |timestamp|
88
+ pipeline.llen("delayed:#{timestamp}")
89
+ end
90
+ end.sum
91
+ else
92
+ timestamps.each do |timestamp|
93
+ job_cursor = 0
94
+
95
+ loop do
96
+ encoded_jobs = ::Resque.redis.lrange(
97
+ "delayed:#{timestamp}",
98
+ job_cursor,
99
+ job_cursor + batch - 1
100
+ )
101
+
102
+ break if encoded_jobs.empty?
103
+
104
+ total_size += encoded_jobs.count do |encoded_job|
105
+ queues.include?(::Resque.decode(encoded_job)["queue"])
106
+ end
107
+
108
+ break if encoded_jobs.size < batch
109
+
110
+ job_cursor += batch
111
+ end
112
+ end
113
+ end
114
+
115
+ break if timestamps.size < batch
116
+
117
+ cursor += batch
38
118
  end
39
119
 
40
- in_queues + in_progress
120
+ total_size
121
+ end
122
+
123
+ private
124
+
125
+ def registered_queues
126
+ ::Resque.redis.keys("queue:*").map { |key| key[6..] }.to_set
41
127
  end
42
128
  end
43
129
  end