cloudtasker 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.rubocop.yml +5 -0
- data/.travis.yml +10 -1
- data/Appraisals +25 -0
- data/Gemfile.lock +10 -4
- data/README.md +550 -4
- data/app/controllers/cloudtasker/application_controller.rb +2 -0
- data/app/controllers/cloudtasker/worker_controller.rb +22 -2
- data/cloudtasker.gemspec +4 -3
- data/docs/BATCH_JOBS.md +66 -0
- data/docs/CRON_JOBS.md +63 -0
- data/docs/UNIQUE_JOBS.md +127 -0
- data/exe/cloudtasker +15 -0
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/google_cloud_tasks_1.0.gemfile +9 -0
- data/gemfiles/google_cloud_tasks_1.0.gemfile.lock +263 -0
- data/gemfiles/google_cloud_tasks_1.1.gemfile +9 -0
- data/gemfiles/google_cloud_tasks_1.1.gemfile.lock +263 -0
- data/gemfiles/google_cloud_tasks_1.2.gemfile +9 -0
- data/gemfiles/google_cloud_tasks_1.2.gemfile.lock +263 -0
- data/gemfiles/google_cloud_tasks_1.3.gemfile +9 -0
- data/gemfiles/google_cloud_tasks_1.3.gemfile.lock +264 -0
- data/gemfiles/rails_4.0.gemfile +10 -0
- data/gemfiles/rails_4.1.gemfile +9 -0
- data/gemfiles/rails_4.2.gemfile +9 -0
- data/gemfiles/rails_5.0.gemfile +9 -0
- data/gemfiles/rails_5.1.gemfile +9 -0
- data/gemfiles/rails_5.2.gemfile +9 -0
- data/gemfiles/rails_5.2.gemfile.lock +247 -0
- data/gemfiles/rails_6.0.gemfile +9 -0
- data/gemfiles/rails_6.0.gemfile.lock +263 -0
- data/lib/cloudtasker.rb +19 -1
- data/lib/cloudtasker/backend/google_cloud_task.rb +139 -0
- data/lib/cloudtasker/backend/memory_task.rb +190 -0
- data/lib/cloudtasker/backend/redis_task.rb +248 -0
- data/lib/cloudtasker/batch/batch_progress.rb +19 -1
- data/lib/cloudtasker/batch/job.rb +81 -20
- data/lib/cloudtasker/cli.rb +194 -0
- data/lib/cloudtasker/cloud_task.rb +91 -0
- data/lib/cloudtasker/config.rb +64 -2
- data/lib/cloudtasker/cron/job.rb +2 -2
- data/lib/cloudtasker/cron/schedule.rb +15 -5
- data/lib/cloudtasker/dead_worker_error.rb +6 -0
- data/lib/cloudtasker/local_server.rb +74 -0
- data/lib/cloudtasker/railtie.rb +10 -0
- data/lib/cloudtasker/testing.rb +133 -0
- data/lib/cloudtasker/unique_job/job.rb +1 -1
- data/lib/cloudtasker/unique_job/lock/base_lock.rb +1 -1
- data/lib/cloudtasker/unique_job/lock/until_executed.rb +3 -1
- data/lib/cloudtasker/unique_job/lock/while_executing.rb +3 -1
- data/lib/cloudtasker/version.rb +1 -1
- data/lib/cloudtasker/worker.rb +59 -16
- data/lib/cloudtasker/{task.rb → worker_handler.rb} +10 -77
- data/lib/cloudtasker/worker_logger.rb +155 -0
- data/lib/tasks/setup_queue.rake +10 -0
- metadata +55 -6
data/lib/cloudtasker/config.rb
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'logger'
|
|
4
|
+
|
|
3
5
|
module Cloudtasker
|
|
4
6
|
# Holds cloudtasker configuration. See Cloudtasker#configure
|
|
5
7
|
class Config
|
|
6
8
|
attr_accessor :redis
|
|
7
9
|
attr_writer :secret, :gcp_location_id, :gcp_project_id,
|
|
8
|
-
:gcp_queue_id, :
|
|
10
|
+
:gcp_queue_id, :processor_path, :logger, :mode, :max_retries
|
|
11
|
+
|
|
12
|
+
# Retry header in Cloud Task responses
|
|
13
|
+
RETRY_HEADER = 'X-CloudTasks-TaskExecutionCount'
|
|
9
14
|
|
|
15
|
+
# Default values
|
|
10
16
|
DEFAULT_LOCATION_ID = 'us-east1'
|
|
11
17
|
DEFAULT_PROCESSOR_PATH = '/cloudtasker/run'
|
|
12
18
|
|
|
19
|
+
# The number of times jobs will be attempted before declaring them dead
|
|
20
|
+
DEFAULT_MAX_RETRY_ATTEMPTS = 25
|
|
21
|
+
|
|
13
22
|
PROCESSOR_HOST_MISSING = <<~DOC
|
|
14
23
|
Missing host for processing.
|
|
15
24
|
Please specify a processor hostname in form of `https://some-public-dns.example.com`'
|
|
@@ -27,6 +36,46 @@ module Cloudtasker
|
|
|
27
36
|
Please specify a secret in the cloudtasker initializer or add Rails secret_key_base in your credentials
|
|
28
37
|
DOC
|
|
29
38
|
|
|
39
|
+
#
|
|
40
|
+
# The number of times jobs will be retried. This number of
|
|
41
|
+
# retries does not include failures due to the application being unreachable.
|
|
42
|
+
#
|
|
43
|
+
#
|
|
44
|
+
# @return [Integer] The number of retries
|
|
45
|
+
#
|
|
46
|
+
def max_retries
|
|
47
|
+
@max_retries ||= DEFAULT_MAX_RETRY_ATTEMPTS
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
#
|
|
51
|
+
# The operating mode.
|
|
52
|
+
# - :production => process tasks via GCP Cloud Task.
|
|
53
|
+
# - :development => process tasks locally via Redis.
|
|
54
|
+
#
|
|
55
|
+
# @return [<Type>] <description>
|
|
56
|
+
#
|
|
57
|
+
def mode
|
|
58
|
+
@mode ||= environment == 'development' ? :development : :production
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
#
|
|
62
|
+
# Return the current environment.
|
|
63
|
+
#
|
|
64
|
+
# @return [String] The environment name.
|
|
65
|
+
#
|
|
66
|
+
def environment
|
|
67
|
+
ENV['CLOUDTASKER_ENV'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
#
|
|
71
|
+
# Return the Cloudtasker logger.
|
|
72
|
+
#
|
|
73
|
+
# @return [Logger, any] The cloudtasker logger.
|
|
74
|
+
#
|
|
75
|
+
def logger
|
|
76
|
+
@logger ||= defined?(Rails) ? Rails.logger : ::Logger.new(STDOUT)
|
|
77
|
+
end
|
|
78
|
+
|
|
30
79
|
#
|
|
31
80
|
# Return the full URL of the processor. Worker payloads will be sent
|
|
32
81
|
# to this URL.
|
|
@@ -37,6 +86,19 @@ module Cloudtasker
|
|
|
37
86
|
File.join(processor_host, processor_path)
|
|
38
87
|
end
|
|
39
88
|
|
|
89
|
+
#
|
|
90
|
+
# Set the processor host. In the context of Rails the host will
|
|
91
|
+
# also be added to the list of authorized Rails hosts.
|
|
92
|
+
#
|
|
93
|
+
# @param [String] val The processor host to set.
|
|
94
|
+
#
|
|
95
|
+
def processor_host=(val)
|
|
96
|
+
@processor_host = val
|
|
97
|
+
|
|
98
|
+
# Add processor host to the list of authorized hosts
|
|
99
|
+
Rails.application.config.hosts << val.gsub(%r{https?://}, '') if val && defined?(Rails)
|
|
100
|
+
end
|
|
101
|
+
|
|
40
102
|
#
|
|
41
103
|
# The hostname of the application processing the workers. The hostname must
|
|
42
104
|
# be reachable from Cloud Task.
|
|
@@ -93,7 +155,7 @@ module Cloudtasker
|
|
|
93
155
|
#
|
|
94
156
|
def secret
|
|
95
157
|
@secret || (
|
|
96
|
-
defined?(Rails) && Rails.application.credentials&.secret_key_base
|
|
158
|
+
defined?(Rails) && Rails.application.credentials&.dig(:secret_key_base)
|
|
97
159
|
) || raise(StandardError, SECRET_MISSING_ERROR)
|
|
98
160
|
end
|
|
99
161
|
|
data/lib/cloudtasker/cron/job.rb
CHANGED
|
@@ -179,8 +179,8 @@ module Cloudtasker
|
|
|
179
179
|
next_worker = worker.new_instance.tap { |e| e.job_meta.set(key(:time_at), next_time.iso8601) }
|
|
180
180
|
|
|
181
181
|
# Schedule next worker
|
|
182
|
-
|
|
183
|
-
cron_schedule.update(task_id:
|
|
182
|
+
task = next_worker.schedule(time_at: next_time)
|
|
183
|
+
cron_schedule.update(task_id: task.id, job_id: next_worker.job_id)
|
|
184
184
|
end
|
|
185
185
|
|
|
186
186
|
#
|
|
@@ -56,7 +56,7 @@ module Cloudtasker
|
|
|
56
56
|
#
|
|
57
57
|
def self.load_from_hash!(hash)
|
|
58
58
|
schedules = hash.map do |id, config|
|
|
59
|
-
schedule_config = JSON.parse(config.to_json, symbolize_names: true).merge(id: id)
|
|
59
|
+
schedule_config = JSON.parse(config.to_json, symbolize_names: true).merge(id: id.to_s)
|
|
60
60
|
create(schedule_config)
|
|
61
61
|
end
|
|
62
62
|
|
|
@@ -99,7 +99,7 @@ module Cloudtasker
|
|
|
99
99
|
return false unless schedule
|
|
100
100
|
|
|
101
101
|
# Delete task and stored schedule
|
|
102
|
-
|
|
102
|
+
CloudTask.delete(schedule.task_id) if schedule.task_id
|
|
103
103
|
redis.del(schedule.gid)
|
|
104
104
|
end
|
|
105
105
|
|
|
@@ -252,17 +252,27 @@ module Cloudtasker
|
|
|
252
252
|
# then any existing cloud task is removed and a task is recreated.
|
|
253
253
|
#
|
|
254
254
|
def save(update_task: true)
|
|
255
|
-
return false unless valid?
|
|
255
|
+
return false unless valid?
|
|
256
256
|
|
|
257
257
|
# Save schedule
|
|
258
258
|
config_was_changed = config_changed?
|
|
259
259
|
redis.write(gid, to_h)
|
|
260
260
|
|
|
261
261
|
# Stop there if backend does not need update
|
|
262
|
-
return true unless update_task && config_was_changed
|
|
262
|
+
return true unless update_task && (config_was_changed || !task_id || !CloudTask.find(task_id))
|
|
263
263
|
|
|
264
|
+
# Update backend
|
|
265
|
+
persist_cloud_task
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
private
|
|
269
|
+
|
|
270
|
+
#
|
|
271
|
+
# Update the task in backend.
|
|
272
|
+
#
|
|
273
|
+
def persist_cloud_task
|
|
264
274
|
# Delete previous instance
|
|
265
|
-
|
|
275
|
+
CloudTask.delete(task_id) if task_id
|
|
266
276
|
|
|
267
277
|
# Schedule worker
|
|
268
278
|
worker_instance = Object.const_get(worker).new
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'cloudtasker/backend/redis_task'
|
|
4
|
+
|
|
5
|
+
module Cloudtasker
|
|
6
|
+
# Process jobs stored in Redis.
|
|
7
|
+
# Only to be used in development.
|
|
8
|
+
class LocalServer
|
|
9
|
+
# Max number of task requests sent to the processing server
|
|
10
|
+
CONCURRENCY = (ENV['CLOUDTASKER_CONCURRENCY'] || 5).to_i
|
|
11
|
+
|
|
12
|
+
#
|
|
13
|
+
# Stop the local server.
|
|
14
|
+
#
|
|
15
|
+
def stop
|
|
16
|
+
@done = true
|
|
17
|
+
|
|
18
|
+
# Terminate threads and repush tasks
|
|
19
|
+
@threads&.each do |t|
|
|
20
|
+
t.terminate
|
|
21
|
+
t['task']&.retry_later(0, is_error: false)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Wait for main server to be done
|
|
25
|
+
sleep 1 while @start&.alive?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
#
|
|
29
|
+
# Start the local server
|
|
30
|
+
#
|
|
31
|
+
#
|
|
32
|
+
def start
|
|
33
|
+
@start ||= Thread.new do
|
|
34
|
+
until @done
|
|
35
|
+
process_jobs
|
|
36
|
+
sleep 1
|
|
37
|
+
end
|
|
38
|
+
Cloudtasker.logger.info('[Cloudtasker/Server] Local server exiting...')
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
#
|
|
43
|
+
# Process enqueued workers.
|
|
44
|
+
#
|
|
45
|
+
#
|
|
46
|
+
def process_jobs
|
|
47
|
+
@threads ||= []
|
|
48
|
+
|
|
49
|
+
# Remove any done thread
|
|
50
|
+
@threads.select!(&:alive?)
|
|
51
|
+
|
|
52
|
+
# Process tasks
|
|
53
|
+
while @threads.count < CONCURRENCY && (task = Cloudtasker::Backend::RedisTask.pop)
|
|
54
|
+
@threads << Thread.new do
|
|
55
|
+
Thread.current['task'] = task
|
|
56
|
+
Thread.current['attempts'] = 0
|
|
57
|
+
|
|
58
|
+
# Deliver task
|
|
59
|
+
begin
|
|
60
|
+
Thread.current['task'].deliver
|
|
61
|
+
rescue Errno::ECONNREFUSED => e
|
|
62
|
+
raise(e) unless Thread.current['attempts'] < 3
|
|
63
|
+
|
|
64
|
+
# Retry on connection error, in case the web server is not
|
|
65
|
+
# started yet.
|
|
66
|
+
Thread.current['attempts'] += 1
|
|
67
|
+
sleep(3)
|
|
68
|
+
retry
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'cloudtasker/backend/memory_task'
|
|
4
|
+
|
|
5
|
+
module Cloudtasker
|
|
6
|
+
# Enable/Disable test mode for Cloudtasker
|
|
7
|
+
module Testing
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
#
|
|
11
|
+
# Set the test mode, either permanently or
|
|
12
|
+
# temporarily (via block).
|
|
13
|
+
#
|
|
14
|
+
# @param [Symbol] mode The test mode.
|
|
15
|
+
#
|
|
16
|
+
# @return [Symbol] The test mode.
|
|
17
|
+
#
|
|
18
|
+
def switch_test_mode(mode)
|
|
19
|
+
if block_given?
|
|
20
|
+
current_mode = @test_mode
|
|
21
|
+
begin
|
|
22
|
+
@test_mode = mode
|
|
23
|
+
yield
|
|
24
|
+
ensure
|
|
25
|
+
@test_mode = current_mode
|
|
26
|
+
end
|
|
27
|
+
else
|
|
28
|
+
@test_mode = mode
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#
|
|
33
|
+
# Set cloudtasker to real mode temporarily
|
|
34
|
+
#
|
|
35
|
+
# @param [Proc] &block The block to run in real mode
|
|
36
|
+
#
|
|
37
|
+
def enable!(&block)
|
|
38
|
+
switch_test_mode(:enabled, &block)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
#
|
|
42
|
+
# Set cloudtasker to fake mode temporarily
|
|
43
|
+
#
|
|
44
|
+
# @param [Proc] &block The block to run in fake mode
|
|
45
|
+
#
|
|
46
|
+
def fake!(&block)
|
|
47
|
+
switch_test_mode(:fake, &block)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
#
|
|
51
|
+
# Set cloudtasker to inline mode temporarily
|
|
52
|
+
#
|
|
53
|
+
# @param [Proc] &block The block to run in inline mode
|
|
54
|
+
#
|
|
55
|
+
def inline!(&block)
|
|
56
|
+
switch_test_mode(:inline, &block)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
#
|
|
60
|
+
# Return true if Cloudtasker is enabled.
|
|
61
|
+
#
|
|
62
|
+
def enabled?
|
|
63
|
+
!@test_mode || @test_mode == :enabled
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
#
|
|
67
|
+
# Return true if Cloudtasker is in fake mode.
|
|
68
|
+
#
|
|
69
|
+
# @return [Boolean] True if jobs must be processed through drain calls.
|
|
70
|
+
#
|
|
71
|
+
def fake?
|
|
72
|
+
@test_mode == :fake
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
#
|
|
76
|
+
# Return true if Cloudtasker is in inline mode.
|
|
77
|
+
#
|
|
78
|
+
# @return [Boolean] True if jobs are run inline.
|
|
79
|
+
#
|
|
80
|
+
def inline?
|
|
81
|
+
@test_mode == :inline
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
#
|
|
85
|
+
# Return true if tasks should be managed in memory.
|
|
86
|
+
#
|
|
87
|
+
# @return [Boolean] True if jobs are managed in memory.
|
|
88
|
+
#
|
|
89
|
+
def in_memory?
|
|
90
|
+
!enabled?
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Add extra methods for testing purpose
|
|
95
|
+
module Worker
|
|
96
|
+
#
|
|
97
|
+
# Clear all jobs.
|
|
98
|
+
#
|
|
99
|
+
def self.clear_all
|
|
100
|
+
Backend::MemoryTask.clear
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
#
|
|
104
|
+
# Run all the jobs.
|
|
105
|
+
#
|
|
106
|
+
# @return [Array<any>] The return values of the workers perform method.
|
|
107
|
+
#
|
|
108
|
+
def self.drain_all
|
|
109
|
+
Backend::MemoryTask.drain
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Module class methods
|
|
113
|
+
module ClassMethods
|
|
114
|
+
#
|
|
115
|
+
# Return all jobs related to this worker class.
|
|
116
|
+
#
|
|
117
|
+
# @return [Array<Cloudtasker::Worker] The list of workers
|
|
118
|
+
#
|
|
119
|
+
def jobs
|
|
120
|
+
Backend::MemoryTask.jobs(to_s)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
#
|
|
124
|
+
# Run all jobs related to this worker class.
|
|
125
|
+
#
|
|
126
|
+
# @return [Array<any>] The return values of the workers perform method.
|
|
127
|
+
#
|
|
128
|
+
def drain
|
|
129
|
+
Backend::MemoryTask.drain(to_s)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -40,7 +40,7 @@ module Cloudtasker
|
|
|
40
40
|
@lock_instance ||=
|
|
41
41
|
begin
|
|
42
42
|
# Infer lock class and get instance
|
|
43
|
-
lock_name = options[:lock]
|
|
43
|
+
lock_name = options[:lock]
|
|
44
44
|
lock_klass = Lock.const_get(lock_name.to_s.split('_').collect(&:capitalize).join)
|
|
45
45
|
lock_klass.new(self)
|
|
46
46
|
rescue NameError
|
|
@@ -43,7 +43,7 @@ module Cloudtasker
|
|
|
43
43
|
@conflict_instance ||=
|
|
44
44
|
begin
|
|
45
45
|
# Infer lock class and get instance
|
|
46
|
-
strategy_name = options[:on_conflict]
|
|
46
|
+
strategy_name = options[:on_conflict]
|
|
47
47
|
strategy_klass = ConflictStrategy.const_get(strategy_name.to_s.split('_').collect(&:capitalize).join)
|
|
48
48
|
strategy_klass.new(job)
|
|
49
49
|
rescue NameError
|
data/lib/cloudtasker/version.rb
CHANGED
data/lib/cloudtasker/worker.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Cloudtasker
|
|
|
6
6
|
# Add class method to including class
|
|
7
7
|
def self.included(base)
|
|
8
8
|
base.extend(ClassMethods)
|
|
9
|
-
base.attr_accessor :job_args, :job_id, :job_meta, :job_reenqueued
|
|
9
|
+
base.attr_accessor :job_args, :job_id, :job_meta, :job_reenqueued, :job_retries
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
#
|
|
@@ -44,7 +44,7 @@ module Cloudtasker
|
|
|
44
44
|
return nil unless worker_klass.include?(self)
|
|
45
45
|
|
|
46
46
|
# Return instantiated worker
|
|
47
|
-
worker_klass.new(payload.slice(:job_args, :job_id, :job_meta))
|
|
47
|
+
worker_klass.new(payload.slice(:job_args, :job_id, :job_meta, :job_retries))
|
|
48
48
|
rescue NameError
|
|
49
49
|
nil
|
|
50
50
|
end
|
|
@@ -54,12 +54,12 @@ module Cloudtasker
|
|
|
54
54
|
#
|
|
55
55
|
# Set the worker runtime options.
|
|
56
56
|
#
|
|
57
|
-
# @param [Hash] opts The worker options
|
|
57
|
+
# @param [Hash] opts The worker options.
|
|
58
58
|
#
|
|
59
|
-
# @return [
|
|
59
|
+
# @return [Hash] The options set.
|
|
60
60
|
#
|
|
61
61
|
def cloudtasker_options(opts = {})
|
|
62
|
-
opt_list = opts&.map { |k, v| [k.
|
|
62
|
+
opt_list = opts&.map { |k, v| [k.to_sym, v] } || [] # symbolize
|
|
63
63
|
@cloudtasker_options_hash = Hash[opt_list]
|
|
64
64
|
end
|
|
65
65
|
|
|
@@ -69,7 +69,7 @@ module Cloudtasker
|
|
|
69
69
|
# @return [Hash] The worker runtime options.
|
|
70
70
|
#
|
|
71
71
|
def cloudtasker_options_hash
|
|
72
|
-
@cloudtasker_options_hash
|
|
72
|
+
@cloudtasker_options_hash || {}
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
#
|
|
@@ -77,7 +77,7 @@ module Cloudtasker
|
|
|
77
77
|
#
|
|
78
78
|
# @param [Array<any>] *args List of worker arguments
|
|
79
79
|
#
|
|
80
|
-
# @return [
|
|
80
|
+
# @return [Cloudtasker::CloudTask] The Google Task response
|
|
81
81
|
#
|
|
82
82
|
def perform_async(*args)
|
|
83
83
|
perform_in(nil, *args)
|
|
@@ -89,7 +89,7 @@ module Cloudtasker
|
|
|
89
89
|
# @param [Integer, nil] interval The delay in seconds.
|
|
90
90
|
# @param [Array<any>] *args List of worker arguments.
|
|
91
91
|
#
|
|
92
|
-
# @return [
|
|
92
|
+
# @return [Cloudtasker::CloudTask] The Google Task response
|
|
93
93
|
#
|
|
94
94
|
def perform_in(interval, *args)
|
|
95
95
|
new(job_args: args).schedule(interval: interval)
|
|
@@ -101,11 +101,20 @@ module Cloudtasker
|
|
|
101
101
|
# @param [Time, Integer] time_at The time at which the job should run.
|
|
102
102
|
# @param [Array<any>] *args List of worker arguments
|
|
103
103
|
#
|
|
104
|
-
# @return [
|
|
104
|
+
# @return [Cloudtasker::CloudTask] The Google Task response
|
|
105
105
|
#
|
|
106
106
|
def perform_at(time_at, *args)
|
|
107
107
|
new(job_args: args).schedule(time_at: time_at)
|
|
108
108
|
end
|
|
109
|
+
|
|
110
|
+
#
|
|
111
|
+
# Return the numbeer of times this worker will be retried.
|
|
112
|
+
#
|
|
113
|
+
# @return [Integer] The number of retries.
|
|
114
|
+
#
|
|
115
|
+
def max_retries
|
|
116
|
+
cloudtasker_options_hash[:max_retries] || Cloudtasker.config.max_retries
|
|
117
|
+
end
|
|
109
118
|
end
|
|
110
119
|
|
|
111
120
|
#
|
|
@@ -114,10 +123,20 @@ module Cloudtasker
|
|
|
114
123
|
# @param [Array<any>] job_args The list of perform args.
|
|
115
124
|
# @param [String] job_id A unique ID identifying this job.
|
|
116
125
|
#
|
|
117
|
-
def initialize(job_args: [], job_id: nil, job_meta: {})
|
|
126
|
+
def initialize(job_args: [], job_id: nil, job_meta: {}, job_retries: 0)
|
|
118
127
|
@job_args = job_args
|
|
119
128
|
@job_id = job_id || SecureRandom.uuid
|
|
120
129
|
@job_meta = MetaStore.new(job_meta)
|
|
130
|
+
@job_retries = job_retries || 0
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
#
|
|
134
|
+
# Return the Cloudtasker logger instance.
|
|
135
|
+
#
|
|
136
|
+
# @return [Logger, any] The cloudtasker logger.
|
|
137
|
+
#
|
|
138
|
+
def logger
|
|
139
|
+
@logger ||= WorkerLogger.new(self)
|
|
121
140
|
end
|
|
122
141
|
|
|
123
142
|
#
|
|
@@ -126,9 +145,22 @@ module Cloudtasker
|
|
|
126
145
|
# @return [Any] The result of the perform.
|
|
127
146
|
#
|
|
128
147
|
def execute
|
|
129
|
-
|
|
130
|
-
|
|
148
|
+
logger.info('Starting job...')
|
|
149
|
+
resp = Cloudtasker.config.server_middleware.invoke(self) do
|
|
150
|
+
begin
|
|
151
|
+
perform(*job_args)
|
|
152
|
+
rescue StandardError => e
|
|
153
|
+
try(:on_error, e)
|
|
154
|
+
return raise(e) unless job_dead?
|
|
155
|
+
|
|
156
|
+
# Flag job as dead
|
|
157
|
+
logger.info('Job dead')
|
|
158
|
+
try(:on_dead, e)
|
|
159
|
+
raise(DeadWorkerError, e)
|
|
160
|
+
end
|
|
131
161
|
end
|
|
162
|
+
logger.info('Job done')
|
|
163
|
+
resp
|
|
132
164
|
end
|
|
133
165
|
|
|
134
166
|
#
|
|
@@ -138,11 +170,11 @@ module Cloudtasker
|
|
|
138
170
|
#
|
|
139
171
|
# @param [Time, Integer] interval The time at which the job should run
|
|
140
172
|
#
|
|
141
|
-
# @return [
|
|
173
|
+
# @return [Cloudtasker::CloudTask] The Google Task response
|
|
142
174
|
#
|
|
143
175
|
def schedule(interval: nil, time_at: nil)
|
|
144
176
|
Cloudtasker.config.client_middleware.invoke(self) do
|
|
145
|
-
|
|
177
|
+
WorkerHandler.new(self).schedule(interval: interval, time_at: time_at)
|
|
146
178
|
end
|
|
147
179
|
end
|
|
148
180
|
|
|
@@ -155,7 +187,7 @@ module Cloudtasker
|
|
|
155
187
|
#
|
|
156
188
|
# @param [Integer] interval Delay to wait before processing the job again (in seconds).
|
|
157
189
|
#
|
|
158
|
-
# @return [
|
|
190
|
+
# @return [Cloudtasker::CloudTask] The Google Task response
|
|
159
191
|
#
|
|
160
192
|
def reenqueue(interval)
|
|
161
193
|
@job_reenqueued = true
|
|
@@ -182,7 +214,8 @@ module Cloudtasker
|
|
|
182
214
|
worker: self.class.to_s,
|
|
183
215
|
job_id: job_id,
|
|
184
216
|
job_args: job_args,
|
|
185
|
-
job_meta: job_meta.to_h
|
|
217
|
+
job_meta: job_meta.to_h,
|
|
218
|
+
job_retries: job_retries
|
|
186
219
|
}
|
|
187
220
|
end
|
|
188
221
|
|
|
@@ -207,5 +240,15 @@ module Cloudtasker
|
|
|
207
240
|
def ==(other)
|
|
208
241
|
other.is_a?(self.class) && other.job_id == job_id
|
|
209
242
|
end
|
|
243
|
+
|
|
244
|
+
#
|
|
245
|
+
# Return true if the job has excceeded its maximum number
|
|
246
|
+
# of retries
|
|
247
|
+
#
|
|
248
|
+
# @return [Boolean] True if the job is dead
|
|
249
|
+
#
|
|
250
|
+
def job_dead?
|
|
251
|
+
job_retries >= Cloudtasker.config.max_retries
|
|
252
|
+
end
|
|
210
253
|
end
|
|
211
254
|
end
|