hirefire-resource 0.10.0 → 1.0.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +175 -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 +112 -24
  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 -24
  34. data/.github/workflows/main.yml +0 -16
  35. data/.gitignore +0 -5
  36. data/Gemfile +0 -10
  37. data/Gemfile.lock +0 -49
  38. data/Rakefile +0 -4
  39. data/lib/hirefire/macro/qc.rb +0 -23
  40. 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,41 +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
38
+
39
+ private
40
+
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
21
59
 
22
- return 0 if queues.empty?
60
+ if queues.empty?
61
+ workers.count
62
+ else
63
+ workers.count do |worker|
64
+ queues.include?(::Resque.decode(worker)["queue"])
65
+ end
66
+ end
67
+ end
68
+
69
+ def scheduled_size(queues)
70
+ cursor = 0
71
+ batch = 1000
72
+ total_size = 0
73
+ current_time = Time.now.to_i
74
+
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
+ )
23
101
 
24
- redis = ::Resque.redis
25
- ids = Array(redis.smembers(:workers)).compact
26
- raw_jobs = redis.pipelined { ids.map { |id| redis.get("worker:#{id}") } }
27
- jobs = raw_jobs.map { |raw_job| ::Resque.decode(raw_job) || {} }
102
+ break if encoded_jobs.empty?
28
103
 
29
- in_queues = redis.pipelined do
30
- queues.map { |queue| redis.llen("queue:#{queue}") }
31
- end.map(&:to_i).inject(&:+)
104
+ total_size += encoded_jobs.count do |encoded_job|
105
+ queues.include?(::Resque.decode(encoded_job)["queue"])
106
+ end
32
107
 
33
- in_progress = jobs.inject(0) do |memo, job|
34
- memo += 1 if queues.include?(job["queue"])
35
- memo
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
36
118
  end
37
119
 
38
- 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
39
127
  end
40
128
  end
41
129
  end