good_job 1.8.0 → 1.9.4
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +111 -11
- data/README.md +15 -12
- data/engine/app/controllers/good_job/dashboards_controller.rb +7 -5
- data/engine/app/views/shared/_chart.erb +3 -2
- data/exe/good_job +3 -2
- data/lib/active_job/queue_adapters/good_job_adapter.rb +0 -4
- data/lib/good_job.rb +26 -8
- data/lib/good_job/adapter.rb +35 -17
- data/lib/good_job/cli.rb +17 -5
- data/lib/good_job/configuration.rb +26 -34
- data/lib/good_job/current_execution.rb +0 -1
- data/lib/good_job/daemon.rb +6 -0
- data/lib/good_job/execution_result.rb +20 -0
- data/lib/good_job/job.rb +47 -37
- data/lib/good_job/job_performer.rb +2 -2
- data/lib/good_job/lockable.rb +4 -7
- data/lib/good_job/log_subscriber.rb +15 -14
- data/lib/good_job/multi_scheduler.rb +10 -1
- data/lib/good_job/notifier.rb +7 -6
- data/lib/good_job/poller.rb +10 -6
- data/lib/good_job/scheduler.rb +55 -20
- data/lib/good_job/version.rb +1 -1
- metadata +4 -17
@@ -24,7 +24,7 @@ module GoodJob
|
|
24
24
|
end
|
25
25
|
|
26
26
|
# Perform the next eligible job
|
27
|
-
# @return [
|
27
|
+
# @return [Object, nil] Returns job result or +nil+ if no job was found
|
28
28
|
def next
|
29
29
|
job_query.perform_with_advisory_lock
|
30
30
|
end
|
@@ -54,7 +54,7 @@ module GoodJob
|
|
54
54
|
# @param after [DateTime, Time, nil] future jobs scheduled after this time
|
55
55
|
# @param limit [Integer] number of future timestamps to return
|
56
56
|
# @param now_limit [Integer] number of past timestamps to return
|
57
|
-
# @return [Array<
|
57
|
+
# @return [Array<DateTime, Time>, nil]
|
58
58
|
def next_at(after: nil, limit: nil, now_limit: nil)
|
59
59
|
job_query.next_scheduled_at(after: after, limit: limit, now_limit: now_limit)
|
60
60
|
end
|
data/lib/good_job/lockable.rb
CHANGED
@@ -143,7 +143,7 @@ module GoodJob
|
|
143
143
|
def supports_cte_materialization_specifiers?
|
144
144
|
return @_supports_cte_materialization_specifiers if defined?(@_supports_cte_materialization_specifiers)
|
145
145
|
|
146
|
-
@_supports_cte_materialization_specifiers =
|
146
|
+
@_supports_cte_materialization_specifiers = connection.postgresql_version >= 120000
|
147
147
|
end
|
148
148
|
end
|
149
149
|
|
@@ -158,7 +158,7 @@ module GoodJob
|
|
158
158
|
WHERE pg_try_advisory_lock(('x'||substr(md5($1 || $2::text), 1, 16))::bit(64)::bigint)
|
159
159
|
SQL
|
160
160
|
binds = [[nil, self.class.table_name], [nil, send(self.class.primary_key)]]
|
161
|
-
|
161
|
+
self.class.connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Lock', binds).any?
|
162
162
|
end
|
163
163
|
|
164
164
|
# Releases an advisory lock on this record if it is locked by this database
|
@@ -245,11 +245,8 @@ module GoodJob
|
|
245
245
|
|
246
246
|
private
|
247
247
|
|
248
|
-
|
249
|
-
|
250
|
-
self.class.send(:sanitize_sql_for_conditions, *args)
|
251
|
-
end
|
252
|
-
|
248
|
+
# @param query [String]
|
249
|
+
# @return [Boolean]
|
253
250
|
def pg_or_jdbc_query(query)
|
254
251
|
if Concurrent.on_jruby?
|
255
252
|
# Replace $1 bind parameters with ?
|
@@ -14,6 +14,7 @@ module GoodJob
|
|
14
14
|
|
15
15
|
# @!macro notification_responder
|
16
16
|
# Responds to the +$0.good_job+ notification.
|
17
|
+
# @param event [ActiveSupport::Notifications::Event]
|
17
18
|
# @return [void]
|
18
19
|
def create(event)
|
19
20
|
# FIXME: This method does not match any good_job notifications.
|
@@ -24,7 +25,7 @@ module GoodJob
|
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
27
|
-
#
|
28
|
+
# @!macro notification_responder
|
28
29
|
def finished_timer_task(event)
|
29
30
|
exception = event.payload[:error]
|
30
31
|
return unless exception
|
@@ -34,7 +35,7 @@ module GoodJob
|
|
34
35
|
end
|
35
36
|
end
|
36
37
|
|
37
|
-
#
|
38
|
+
# @!macro notification_responder
|
38
39
|
def finished_job_task(event)
|
39
40
|
exception = event.payload[:error]
|
40
41
|
return unless exception
|
@@ -44,7 +45,7 @@ module GoodJob
|
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
47
|
-
#
|
48
|
+
# @!macro notification_responder
|
48
49
|
def scheduler_create_pool(event)
|
49
50
|
max_threads = event.payload[:max_threads]
|
50
51
|
performer_name = event.payload[:performer_name]
|
@@ -55,7 +56,7 @@ module GoodJob
|
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
58
|
-
#
|
59
|
+
# @!macro notification_responder
|
59
60
|
def scheduler_shutdown_start(event)
|
60
61
|
process_id = event.payload[:process_id]
|
61
62
|
|
@@ -64,7 +65,7 @@ module GoodJob
|
|
64
65
|
end
|
65
66
|
end
|
66
67
|
|
67
|
-
#
|
68
|
+
# @!macro notification_responder
|
68
69
|
def scheduler_shutdown(event)
|
69
70
|
process_id = event.payload[:process_id]
|
70
71
|
|
@@ -73,7 +74,7 @@ module GoodJob
|
|
73
74
|
end
|
74
75
|
end
|
75
76
|
|
76
|
-
#
|
77
|
+
# @!macro notification_responder
|
77
78
|
def scheduler_restart_pools(event)
|
78
79
|
process_id = event.payload[:process_id]
|
79
80
|
|
@@ -82,7 +83,7 @@ module GoodJob
|
|
82
83
|
end
|
83
84
|
end
|
84
85
|
|
85
|
-
#
|
86
|
+
# @!macro notification_responder
|
86
87
|
def perform_job(event)
|
87
88
|
good_job = event.payload[:good_job]
|
88
89
|
process_id = event.payload[:process_id]
|
@@ -93,14 +94,14 @@ module GoodJob
|
|
93
94
|
end
|
94
95
|
end
|
95
96
|
|
96
|
-
#
|
97
|
-
def notifier_listen(
|
97
|
+
# @!macro notification_responder
|
98
|
+
def notifier_listen(event) # rubocop:disable Lint/UnusedMethodArgument
|
98
99
|
info do
|
99
100
|
"Notifier subscribed with LISTEN"
|
100
101
|
end
|
101
102
|
end
|
102
103
|
|
103
|
-
#
|
104
|
+
# @!macro notification_responder
|
104
105
|
def notifier_notified(event)
|
105
106
|
payload = event.payload[:payload]
|
106
107
|
|
@@ -109,7 +110,7 @@ module GoodJob
|
|
109
110
|
end
|
110
111
|
end
|
111
112
|
|
112
|
-
#
|
113
|
+
# @!macro notification_responder
|
113
114
|
def notifier_notify_error(event)
|
114
115
|
error = event.payload[:error]
|
115
116
|
|
@@ -118,14 +119,14 @@ module GoodJob
|
|
118
119
|
end
|
119
120
|
end
|
120
121
|
|
121
|
-
#
|
122
|
-
def notifier_unlisten(
|
122
|
+
# @!macro notification_responder
|
123
|
+
def notifier_unlisten(event) # rubocop:disable Lint/UnusedMethodArgument
|
123
124
|
info do
|
124
125
|
"Notifier unsubscribed with UNLISTEN"
|
125
126
|
end
|
126
127
|
end
|
127
128
|
|
128
|
-
#
|
129
|
+
# @!macro notification_responder
|
129
130
|
def cleanup_preserved_jobs(event)
|
130
131
|
timestamp = event.payload[:timestamp]
|
131
132
|
deleted_records_count = event.payload[:deleted_records_count]
|
@@ -4,31 +4,40 @@ module GoodJob
|
|
4
4
|
# @return [Array<Scheduler>] List of the scheduler delegates
|
5
5
|
attr_reader :schedulers
|
6
6
|
|
7
|
+
# @param schedulers [Array<Scheduler>]
|
7
8
|
def initialize(schedulers)
|
8
9
|
@schedulers = schedulers
|
9
10
|
end
|
10
11
|
|
11
12
|
# Delegates to {Scheduler#running?}.
|
13
|
+
# @return [Boolean, nil]
|
12
14
|
def running?
|
13
15
|
schedulers.all?(&:running?)
|
14
16
|
end
|
15
17
|
|
16
18
|
# Delegates to {Scheduler#shutdown?}.
|
19
|
+
# @return [Boolean, nil]
|
17
20
|
def shutdown?
|
18
21
|
schedulers.all?(&:shutdown?)
|
19
22
|
end
|
20
23
|
|
21
24
|
# Delegates to {Scheduler#shutdown}.
|
25
|
+
# @param timeout [Numeric, nil]
|
26
|
+
# @return [void]
|
22
27
|
def shutdown(timeout: -1)
|
23
28
|
GoodJob._shutdown_all(schedulers, timeout: timeout)
|
24
29
|
end
|
25
30
|
|
26
31
|
# Delegates to {Scheduler#restart}.
|
32
|
+
# @param timeout [Numeric, nil]
|
33
|
+
# @return [void]
|
27
34
|
def restart(timeout: -1)
|
28
35
|
GoodJob._shutdown_all(schedulers, :restart, timeout: timeout)
|
29
36
|
end
|
30
37
|
|
31
38
|
# Delegates to {Scheduler#create_thread}.
|
39
|
+
# @param state [Hash]
|
40
|
+
# @return [Boolean, nil]
|
32
41
|
def create_thread(state = nil)
|
33
42
|
results = []
|
34
43
|
|
@@ -44,7 +53,7 @@ module GoodJob
|
|
44
53
|
|
45
54
|
if results.any?
|
46
55
|
true
|
47
|
-
elsif results.any?
|
56
|
+
elsif results.any?(false)
|
48
57
|
false
|
49
58
|
else # rubocop:disable Style/EmptyElse
|
50
59
|
nil
|
data/lib/good_job/notifier.rb
CHANGED
@@ -30,13 +30,13 @@ module GoodJob # :nodoc:
|
|
30
30
|
# @!attribute [r] instances
|
31
31
|
# @!scope class
|
32
32
|
# List of all instantiated Notifiers in the current process.
|
33
|
-
# @return [Array<GoodJob
|
33
|
+
# @return [Array<GoodJob::Notifier>, nil]
|
34
34
|
cattr_reader :instances, default: [], instance_reader: false
|
35
35
|
|
36
36
|
# Send a message via Postgres NOTIFY
|
37
37
|
# @param message [#to_json]
|
38
38
|
def self.notify(message)
|
39
|
-
connection =
|
39
|
+
connection = Job.connection
|
40
40
|
connection.exec_query <<~SQL.squish
|
41
41
|
NOTIFY #{CHANNEL}, #{connection.quote(message.to_json)}
|
42
42
|
SQL
|
@@ -64,18 +64,19 @@ module GoodJob # :nodoc:
|
|
64
64
|
end
|
65
65
|
|
66
66
|
# Tests whether the notifier is running.
|
67
|
+
# @!method running?
|
67
68
|
# @return [true, false, nil]
|
68
69
|
delegate :running?, to: :executor, allow_nil: true
|
69
70
|
|
70
71
|
# Tests whether the scheduler is shutdown.
|
72
|
+
# @!method shutdown?
|
71
73
|
# @return [true, false, nil]
|
72
74
|
delegate :shutdown?, to: :executor, allow_nil: true
|
73
75
|
|
74
76
|
# Shut down the notifier.
|
75
77
|
# This stops the background LISTENing thread.
|
76
78
|
# Use {#shutdown?} to determine whether threads have stopped.
|
77
|
-
# @param timeout [
|
78
|
-
#
|
79
|
+
# @param timeout [Numeric, nil] Seconds to wait for active threads.
|
79
80
|
# * +nil+, the scheduler will trigger a shutdown but not wait for it to complete.
|
80
81
|
# * +-1+, the scheduler will wait until the shutdown is complete.
|
81
82
|
# * +0+, the scheduler will immediately shutdown and stop any threads.
|
@@ -159,8 +160,8 @@ module GoodJob # :nodoc:
|
|
159
160
|
end
|
160
161
|
|
161
162
|
def with_listen_connection
|
162
|
-
ar_conn =
|
163
|
-
|
163
|
+
ar_conn = Job.connection_pool.checkout.tap do |conn|
|
164
|
+
Job.connection_pool.remove(conn)
|
164
165
|
end
|
165
166
|
pg_conn = ar_conn.raw_connection
|
166
167
|
raise AdapterCannotListenError unless pg_conn.respond_to? :wait_for_notify
|
data/lib/good_job/poller.rb
CHANGED
@@ -16,7 +16,7 @@ module GoodJob # :nodoc:
|
|
16
16
|
# @!attribute [r] instances
|
17
17
|
# @!scope class
|
18
18
|
# List of all instantiated Pollers in the current process.
|
19
|
-
# @return [Array<GoodJob
|
19
|
+
# @return [Array<GoodJob::Poller>, nil]
|
20
20
|
cattr_reader :instances, default: [], instance_reader: false
|
21
21
|
|
22
22
|
# Creates GoodJob::Poller from a GoodJob::Configuration instance.
|
@@ -30,8 +30,8 @@ module GoodJob # :nodoc:
|
|
30
30
|
# @return [Array<#call, Array(Object, Symbol)>]
|
31
31
|
attr_reader :recipients
|
32
32
|
|
33
|
-
# @param recipients [Array
|
34
|
-
# @param poll_interval [
|
33
|
+
# @param recipients [Array<Proc, #call, Array(Object, Symbol)>]
|
34
|
+
# @param poll_interval [Integer, nil] number of seconds between polls
|
35
35
|
def initialize(*recipients, poll_interval: nil)
|
36
36
|
@recipients = Concurrent::Array.new(recipients)
|
37
37
|
|
@@ -54,7 +54,6 @@ module GoodJob # :nodoc:
|
|
54
54
|
# Shut down the notifier.
|
55
55
|
# Use {#shutdown?} to determine whether threads have stopped.
|
56
56
|
# @param timeout [nil, Numeric] Seconds to wait for active threads.
|
57
|
-
#
|
58
57
|
# * +nil+, the scheduler will trigger a shutdown but not wait for it to complete.
|
59
58
|
# * +-1+, the scheduler will wait until the shutdown is complete.
|
60
59
|
# * +0+, the scheduler will immediately shutdown and stop any threads.
|
@@ -73,7 +72,7 @@ module GoodJob # :nodoc:
|
|
73
72
|
|
74
73
|
# Restart the poller.
|
75
74
|
# When shutdown, start; or shutdown and start.
|
76
|
-
# @param timeout [
|
75
|
+
# @param timeout [Numeric, nil] Seconds to wait; shares same values as {#shutdown}.
|
77
76
|
# @return [void]
|
78
77
|
def restart(timeout: -1)
|
79
78
|
shutdown(timeout: timeout) if running?
|
@@ -82,16 +81,21 @@ module GoodJob # :nodoc:
|
|
82
81
|
|
83
82
|
# Invoked on completion of TimerTask task.
|
84
83
|
# @!visibility private
|
84
|
+
# @param time [Integer]
|
85
|
+
# @param executed_task [Object, nil]
|
86
|
+
# @param thread_error [Exception, nil]
|
85
87
|
# @return [void]
|
86
88
|
def timer_observer(time, executed_task, thread_error)
|
87
89
|
GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
|
88
|
-
instrument("finished_timer_task", { result: executed_task, error: thread_error, time: time })
|
90
|
+
ActiveSupport::Notifications.instrument("finished_timer_task", { result: executed_task, error: thread_error, time: time })
|
89
91
|
end
|
90
92
|
|
91
93
|
private
|
92
94
|
|
95
|
+
# @return [Concurrent::TimerTask]
|
93
96
|
attr_reader :timer
|
94
97
|
|
98
|
+
# @return [void]
|
95
99
|
def create_timer
|
96
100
|
return if @timer_options[:execution_interval] <= 0
|
97
101
|
|
data/lib/good_job/scheduler.rb
CHANGED
@@ -30,14 +30,14 @@ module GoodJob # :nodoc:
|
|
30
30
|
# @!attribute [r] instances
|
31
31
|
# @!scope class
|
32
32
|
# List of all instantiated Schedulers in the current process.
|
33
|
-
# @return [Array<GoodJob
|
33
|
+
# @return [Array<GoodJob::Scheduler>, nil]
|
34
34
|
cattr_reader :instances, default: [], instance_reader: false
|
35
35
|
|
36
36
|
# Creates GoodJob::Scheduler(s) and Performers from a GoodJob::Configuration instance.
|
37
37
|
# @param configuration [GoodJob::Configuration]
|
38
38
|
# @param warm_cache_on_initialize [Boolean]
|
39
39
|
# @return [GoodJob::Scheduler, GoodJob::MultiScheduler]
|
40
|
-
def self.from_configuration(configuration, warm_cache_on_initialize:
|
40
|
+
def self.from_configuration(configuration, warm_cache_on_initialize: false)
|
41
41
|
schedulers = configuration.queue_string.split(';').map do |queue_string_and_max_threads|
|
42
42
|
queue_string, max_threads = queue_string_and_max_threads.split(':')
|
43
43
|
max_threads = (max_threads || configuration.max_threads).to_i
|
@@ -61,8 +61,8 @@ module GoodJob # :nodoc:
|
|
61
61
|
# @param performer [GoodJob::JobPerformer]
|
62
62
|
# @param max_threads [Numeric, nil] number of seconds between polls for jobs
|
63
63
|
# @param max_cache [Numeric, nil] maximum number of scheduled jobs to cache in memory
|
64
|
-
# @param warm_cache_on_initialize [Boolean] whether to warm the cache immediately
|
65
|
-
def initialize(performer, max_threads: nil, max_cache: nil, warm_cache_on_initialize:
|
64
|
+
# @param warm_cache_on_initialize [Boolean] whether to warm the cache immediately, or manually by calling +warm_cache+
|
65
|
+
def initialize(performer, max_threads: nil, max_cache: nil, warm_cache_on_initialize: false)
|
66
66
|
raise ArgumentError, "Performer argument must implement #next" unless performer.respond_to?(:next)
|
67
67
|
|
68
68
|
self.class.instances << self
|
@@ -82,18 +82,17 @@ module GoodJob # :nodoc:
|
|
82
82
|
end
|
83
83
|
|
84
84
|
# Tests whether the scheduler is running.
|
85
|
-
# @return [
|
85
|
+
# @return [Boolean, nil]
|
86
86
|
delegate :running?, to: :executor, allow_nil: true
|
87
87
|
|
88
88
|
# Tests whether the scheduler is shutdown.
|
89
|
-
# @return [
|
89
|
+
# @return [Boolean, nil]
|
90
90
|
delegate :shutdown?, to: :executor, allow_nil: true
|
91
91
|
|
92
92
|
# Shut down the scheduler.
|
93
93
|
# This stops all threads in the thread pool.
|
94
94
|
# Use {#shutdown?} to determine whether threads have stopped.
|
95
|
-
# @param timeout [
|
96
|
-
#
|
95
|
+
# @param timeout [Numeric, nil] Seconds to wait for actively executing jobs to finish
|
97
96
|
# * +nil+, the scheduler will trigger a shutdown but not wait for it to complete.
|
98
97
|
# * +-1+, the scheduler will wait until the shutdown is complete.
|
99
98
|
# * +0+, the scheduler will immediately shutdown and stop any active tasks.
|
@@ -129,8 +128,8 @@ module GoodJob # :nodoc:
|
|
129
128
|
end
|
130
129
|
|
131
130
|
# Wakes a thread to allow the performer to execute a task.
|
132
|
-
# @param state [
|
133
|
-
# @return [
|
131
|
+
# @param state [Hash, nil] Contextual information for the performer. See {JobPerformer#next?}.
|
132
|
+
# @return [Boolean, nil] Whether work was started.
|
134
133
|
#
|
135
134
|
# * +nil+ if the scheduler is unable to take new work, for example if the thread pool is shut down or at capacity.
|
136
135
|
# * +true+ if the performer started executing work.
|
@@ -187,38 +186,58 @@ module GoodJob # :nodoc:
|
|
187
186
|
}
|
188
187
|
end
|
189
188
|
|
189
|
+
# Preload existing runnable and future-scheduled jobs
|
190
|
+
# @return [void]
|
190
191
|
def warm_cache
|
191
192
|
return if @max_cache.zero?
|
192
193
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
194
|
+
future = Concurrent::Future.new(args: [self, @performer], executor: executor) do |thr_scheduler, thr_performer|
|
195
|
+
Rails.application.executor.wrap do
|
196
|
+
thr_performer.next_at(
|
197
|
+
limit: @max_cache,
|
198
|
+
now_limit: @executor_options[:max_threads]
|
199
|
+
).each do |scheduled_at|
|
200
|
+
thr_scheduler.create_thread({ scheduled_at: scheduled_at })
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
observer = lambda do |_time, _output, thread_error|
|
206
|
+
GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
|
207
|
+
create_task # If cache-warming exhausts the threads, ensure there isn't an executable task remaining
|
198
208
|
end
|
209
|
+
future.add_observer(observer, :call)
|
210
|
+
|
211
|
+
future.execute
|
199
212
|
end
|
200
213
|
|
201
214
|
private
|
202
215
|
|
203
216
|
attr_reader :performer, :executor, :timer_set
|
204
217
|
|
218
|
+
# @return [void]
|
205
219
|
def create_executor
|
206
220
|
instrument("scheduler_create_pool", { performer_name: performer.name, max_threads: @executor_options[:max_threads] }) do
|
207
|
-
@timer_set =
|
221
|
+
@timer_set = TimerSet.new
|
208
222
|
@executor = ThreadPoolExecutor.new(@executor_options)
|
209
223
|
end
|
210
224
|
end
|
211
225
|
|
226
|
+
# @param delay [Integer]
|
227
|
+
# @return [void]
|
212
228
|
def create_task(delay = 0)
|
213
229
|
future = Concurrent::ScheduledTask.new(delay, args: [performer], executor: executor, timer_set: timer_set) do |thr_performer|
|
214
|
-
|
215
|
-
|
216
|
-
|
230
|
+
Rails.application.executor.wrap do
|
231
|
+
thr_performer.next
|
232
|
+
end
|
217
233
|
end
|
218
234
|
future.add_observer(self, :task_observer)
|
219
235
|
future.execute
|
220
236
|
end
|
221
237
|
|
238
|
+
# @param name [String]
|
239
|
+
# @param payload [Hash]
|
240
|
+
# @return [void]
|
222
241
|
def instrument(name, payload = {}, &block)
|
223
242
|
payload = payload.reverse_merge({
|
224
243
|
scheduler: self,
|
@@ -230,7 +249,7 @@ module GoodJob # :nodoc:
|
|
230
249
|
end
|
231
250
|
|
232
251
|
def cache_count
|
233
|
-
timer_set.
|
252
|
+
timer_set.length
|
234
253
|
end
|
235
254
|
|
236
255
|
def remaining_cache_count
|
@@ -255,5 +274,21 @@ module GoodJob # :nodoc:
|
|
255
274
|
end
|
256
275
|
end
|
257
276
|
end
|
277
|
+
|
278
|
+
# Custom sub-class of +Concurrent::TimerSet+ for additional behavior.
|
279
|
+
# @private
|
280
|
+
class TimerSet < Concurrent::TimerSet
|
281
|
+
# Number of scheduled jobs in the queue
|
282
|
+
# @return [Integer]
|
283
|
+
def length
|
284
|
+
@queue.length
|
285
|
+
end
|
286
|
+
|
287
|
+
# Clear the queue
|
288
|
+
# @return [void]
|
289
|
+
def reset
|
290
|
+
synchronize { @queue.clear }
|
291
|
+
end
|
292
|
+
end
|
258
293
|
end
|
259
294
|
end
|