crono_trigger 0.5.4 → 0.6.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/README.md +15 -14
- data/lib/crono_trigger.rb +1 -0
- data/lib/crono_trigger/execution_tracker.rb +15 -2
- data/lib/crono_trigger/global_exception_handler.rb +29 -0
- data/lib/crono_trigger/models/execution.rb +2 -0
- data/lib/crono_trigger/polling_thread.rb +17 -11
- data/lib/crono_trigger/rollbar.rb +9 -3
- data/lib/crono_trigger/schedulable.rb +46 -31
- data/lib/crono_trigger/version.rb +1 -1
- data/lib/crono_trigger/worker.rb +8 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a9b3bad7ab74a886398bcffaa3f2d935ec35dcd83819abe6b24c0453771a4d1
|
4
|
+
data.tar.gz: 4afa776cb8305b9a95b92c180a3c6da86f0ed205e32a93f3887084b3f498e980
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7e42ef78268d05a3add743f0616fbaa0ea69dba395328a1304fd1a7b18450777ce9c97ccd7be58f59a47d5f5b7c0854098ca74f5756d1b07470b91d42305ba29
|
7
|
+
data.tar.gz: 1ae81221b5251c7ede8d204385910930162bebba7d74ed743fd5978461c9a9d008b9ede5e3c8890ab02b2867030d088032e5776441e073f26a080b8b53410b5b
|
data/README.md
CHANGED
@@ -111,7 +111,7 @@ class MailNotification < ActiveRecord::Base
|
|
111
111
|
send_mail
|
112
112
|
|
113
113
|
throw :retry # break execution and retry task
|
114
|
-
throw :abort # break execution
|
114
|
+
throw :abort # break execution
|
115
115
|
throw :ok # break execution and handle task as success
|
116
116
|
throw :ok_without_reset # break execution and handle task as success but without schedule reseting and unlocking
|
117
117
|
end
|
@@ -160,19 +160,20 @@ Usage: crono_trigger [options] MODEL [MODEL..]
|
|
160
160
|
|
161
161
|
### Columns
|
162
162
|
|
163
|
-
|name
|
164
|
-
|
165
|
-
|cron
|
166
|
-
|next_execute_at
|
167
|
-
|last_executed_at
|
168
|
-
|timezone
|
169
|
-
|execute_lock
|
170
|
-
|started_at
|
171
|
-
|finished_at
|
172
|
-
|last_error_name
|
173
|
-
|last_error_reason|string
|
174
|
-
|last_error_time
|
175
|
-
|retry_count
|
163
|
+
| name | type | required | rename | description |
|
164
|
+
| ----------------- | -------- | -------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
165
|
+
| cron | string | no | no | Recurring schedule formatted by cron style |
|
166
|
+
| next_execute_at | datetime | yes | yes | Timestamp of next execution. Worker executes task if this column <= now |
|
167
|
+
| last_executed_at | datetime | no | yes | Timestamp of last execution |
|
168
|
+
| timezone | datetime | no | yes | Timezone name (Parsed by tzinfo) |
|
169
|
+
| execute_lock | integer | yes | yes | Timestamp of fetching record in order to hide record from other transaction during execute lock timeout. <br> when execution complete this column is reset to 0 |
|
170
|
+
| started_at | datetime | no | yes | Timestamp of schedule activated |
|
171
|
+
| finished_at | datetime | no | yes | Timestamp of schedule deactivated |
|
172
|
+
| last_error_name | string | no | no | Class name of last error |
|
173
|
+
| last_error_reason | string | no | no | Error message of last error |
|
174
|
+
| last_error_time | datetime | no | no | Timestamp of last error occured |
|
175
|
+
| retry_count | integer | no | no | Retry count. <br> If execution succeed retry_count is reset to 0 |
|
176
|
+
| current_cycle_id | string | no | yes | UUID that is updated when the schedule is resetted successfully |
|
176
177
|
|
177
178
|
You can rename some columns.
|
178
179
|
ex. `crono_trigger_options[:next_execute_at_column_name] = "next_time"`
|
data/lib/crono_trigger.rb
CHANGED
@@ -4,12 +4,25 @@ module CronoTrigger
|
|
4
4
|
@schedulable = schedulable
|
5
5
|
end
|
6
6
|
|
7
|
+
def self.track(schedulable, &pr)
|
8
|
+
new(schedulable).track(&pr)
|
9
|
+
end
|
10
|
+
|
7
11
|
def track(&pr)
|
8
12
|
if @schedulable.track_execution
|
9
13
|
begin
|
10
14
|
execution = @schedulable.crono_trigger_executions.create_with_timestamp!
|
11
|
-
pr.call
|
12
|
-
|
15
|
+
result = pr.call
|
16
|
+
case result
|
17
|
+
when :ok
|
18
|
+
execution.complete!
|
19
|
+
when :retry
|
20
|
+
execution.retrying!
|
21
|
+
when :abort
|
22
|
+
execution.aborted!
|
23
|
+
else
|
24
|
+
execution.complete!
|
25
|
+
end
|
13
26
|
rescue => e
|
14
27
|
execution.error!(e)
|
15
28
|
raise
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module CronoTrigger
|
2
|
+
class GlobalExceptionHandler
|
3
|
+
def self.handle_global_exception(ex)
|
4
|
+
new.handle_global_exception(ex)
|
5
|
+
end
|
6
|
+
|
7
|
+
def handle_global_exception(ex)
|
8
|
+
handlers = CronoTrigger.config.global_error_handlers
|
9
|
+
handlers.each do |callable|
|
10
|
+
callable, arity = ensure_callable(callable)
|
11
|
+
|
12
|
+
args = [ex]
|
13
|
+
args = arity < 0 ? args : args.take(arity)
|
14
|
+
callable.call(*args)
|
15
|
+
end
|
16
|
+
rescue Exception => e
|
17
|
+
ActiveRecord::Base.logger.error("CronoTrigger error handler raises error")
|
18
|
+
ActiveRecord::Base.logger.error(e)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def ensure_callable(callable)
|
24
|
+
if callable.respond_to?(:call)
|
25
|
+
return callable, callable.arity
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -22,8 +22,9 @@ module CronoTrigger
|
|
22
22
|
poll(model)
|
23
23
|
rescue ThreadError => e
|
24
24
|
@logger.error(e) unless e.message == "queue empty"
|
25
|
-
rescue =>
|
26
|
-
@logger.error(
|
25
|
+
rescue => ex
|
26
|
+
@logger.error(ex)
|
27
|
+
CronoTrigger::GlobalExceptionHandler.handle_global_exception(ex)
|
27
28
|
ensure
|
28
29
|
@model_queue << model_name if model_name
|
29
30
|
end
|
@@ -63,14 +64,7 @@ module CronoTrigger
|
|
63
64
|
@executor.post do
|
64
65
|
@execution_counter.increment
|
65
66
|
begin
|
66
|
-
|
67
|
-
@logger.info "(executor-thread-#{Thread.current.object_id}) Execute #{record.class}-#{record.id}"
|
68
|
-
begin
|
69
|
-
record.do_execute
|
70
|
-
rescue Exception => e
|
71
|
-
@logger.error(e)
|
72
|
-
end
|
73
|
-
end
|
67
|
+
process_record(record)
|
74
68
|
ensure
|
75
69
|
@execution_counter.decrement
|
76
70
|
end
|
@@ -83,7 +77,19 @@ module CronoTrigger
|
|
83
77
|
end while overflowed_record_ids.empty? && records.any?
|
84
78
|
end
|
85
79
|
|
86
|
-
private
|
80
|
+
private
|
81
|
+
|
82
|
+
def process_record(record)
|
83
|
+
record.class.connection_pool.with_connection do
|
84
|
+
@logger.info "(executor-thread-#{Thread.current.object_id}) Execute #{record.class}-#{record.id}"
|
85
|
+
record.do_execute
|
86
|
+
end
|
87
|
+
rescue Exception => ex
|
88
|
+
@logger.error(ex)
|
89
|
+
CronoTrigger::GlobalExceptionHandler.handle_global_exception(ex)
|
90
|
+
end
|
91
|
+
|
92
|
+
def unlock_overflowed_records(model, overflowed_record_ids)
|
87
93
|
model.connection_pool.with_connection do
|
88
94
|
unless overflowed_record_ids.empty?
|
89
95
|
model.where(id: overflowed_record_ids).crono_trigger_unlock_all!
|
@@ -2,12 +2,15 @@ require 'rollbar'
|
|
2
2
|
|
3
3
|
module Rollbar
|
4
4
|
class CronoTrigger
|
5
|
-
def self.handle_exception(ex, record)
|
5
|
+
def self.handle_exception(ex, record = nil)
|
6
6
|
scope = {
|
7
7
|
framework: "CronoTrigger: #{::CronoTrigger::VERSION}",
|
8
|
-
context: "#{record.class}/#{record.id}"
|
9
8
|
}
|
10
9
|
|
10
|
+
if record
|
11
|
+
scope.merge!({context: "#{record.class}/#{record.id}"})
|
12
|
+
end
|
13
|
+
|
11
14
|
Rollbar.scope(scope).error(ex, use_exception_level_filters: true)
|
12
15
|
end
|
13
16
|
end
|
@@ -18,8 +21,11 @@ Rollbar.plugins.define('crono_trigger') do
|
|
18
21
|
|
19
22
|
execute! do
|
20
23
|
CronoTrigger.config.error_handlers << proc do |ex, record|
|
21
|
-
Rollbar.reset_notifier!
|
22
24
|
Rollbar::CronoTrigger.handle_exception(ex, record)
|
23
25
|
end
|
26
|
+
|
27
|
+
CronoTrigger.config.global_error_handlers << proc do |ex|
|
28
|
+
Rollbar::CronoTrigger.handle_exception(ex)
|
29
|
+
end
|
24
30
|
end
|
25
31
|
end
|
@@ -20,9 +20,6 @@ module CronoTrigger
|
|
20
20
|
@included_by
|
21
21
|
end
|
22
22
|
|
23
|
-
class AbortExecution < StandardError; end
|
24
|
-
class RetryExecution < StandardError; end
|
25
|
-
|
26
23
|
extend ActiveSupport::Concern
|
27
24
|
include ActiveSupport::Callbacks
|
28
25
|
|
@@ -59,6 +56,7 @@ module CronoTrigger
|
|
59
56
|
rel
|
60
57
|
end
|
61
58
|
|
59
|
+
before_create :set_current_cyctle_id
|
62
60
|
before_update :update_next_execute_at_if_update_cron
|
63
61
|
|
64
62
|
validate :validate_cron_format
|
@@ -112,37 +110,34 @@ module CronoTrigger
|
|
112
110
|
end
|
113
111
|
|
114
112
|
def do_execute
|
115
|
-
|
116
|
-
|
117
|
-
execution_tracker.track do
|
118
|
-
catch(:ok_without_reset) do
|
119
|
-
catch(:ok) do
|
120
|
-
catch(:retry) do
|
121
|
-
catch(:abort) do
|
122
|
-
execute
|
123
|
-
throw :ok
|
124
|
-
end
|
125
|
-
raise AbortExecution
|
126
|
-
end
|
127
|
-
retry!
|
128
|
-
raise RetryExecution
|
129
|
-
end
|
130
|
-
reset!
|
131
|
-
end
|
132
|
-
end
|
113
|
+
ExecutionTracker.track(self) do
|
114
|
+
do_execute_with_catch
|
133
115
|
end
|
134
|
-
rescue AbortExecution => ex
|
135
|
-
save_last_error_info(ex)
|
136
|
-
reset!(false)
|
137
|
-
|
138
|
-
raise
|
139
|
-
rescue RetryExecution => ex
|
140
|
-
save_last_error_info(ex)
|
141
116
|
rescue Exception => ex
|
117
|
+
logger.error(ex) if logger
|
142
118
|
save_last_error_info(ex)
|
143
119
|
retry_or_reset!(ex)
|
120
|
+
end
|
144
121
|
|
145
|
-
|
122
|
+
private def do_execute_with_catch
|
123
|
+
catch(:ok_without_reset) do
|
124
|
+
catch(:ok) do
|
125
|
+
catch(:retry) do
|
126
|
+
catch(:abort) do
|
127
|
+
run_callbacks :execute do
|
128
|
+
execute
|
129
|
+
end
|
130
|
+
throw :ok
|
131
|
+
end
|
132
|
+
abort_execution!
|
133
|
+
return :abort
|
134
|
+
end
|
135
|
+
retry!
|
136
|
+
return :retry
|
137
|
+
end
|
138
|
+
reset!
|
139
|
+
return :ok
|
140
|
+
end
|
146
141
|
end
|
147
142
|
|
148
143
|
def activate_schedule!(at: Time.current)
|
@@ -213,10 +208,18 @@ module CronoTrigger
|
|
213
208
|
attributes.merge!(retry_count: 0)
|
214
209
|
end
|
215
210
|
|
211
|
+
if self.class.column_names.include?(crono_trigger_column_name(:current_cycle_id))
|
212
|
+
attributes.merge!(crono_trigger_column_name(:current_cycle_id) => SecureRandom.uuid)
|
213
|
+
end
|
214
|
+
|
216
215
|
merge_updated_at_for_crono_trigger!(attributes, now)
|
217
216
|
update_columns(attributes)
|
218
217
|
end
|
219
218
|
|
219
|
+
def abort_execution!
|
220
|
+
reset!(false)
|
221
|
+
end
|
222
|
+
|
220
223
|
def crono_trigger_lock!(**attributes)
|
221
224
|
attributes = {
|
222
225
|
crono_trigger_column_name(:execute_lock) => Time.current.to_i,
|
@@ -259,7 +262,7 @@ module CronoTrigger
|
|
259
262
|
end
|
260
263
|
|
261
264
|
def locking?(at: Time.now)
|
262
|
-
self[crono_trigger_column_name(:execute_lock)] > 0 &&
|
265
|
+
self[crono_trigger_column_name(:execute_lock)] > 0 &&
|
263
266
|
self[crono_trigger_column_name(:execute_lock)] >= at.to_f - self.class.execute_lock_timeout
|
264
267
|
end
|
265
268
|
|
@@ -293,7 +296,19 @@ module CronoTrigger
|
|
293
296
|
tz = self[crono_trigger_column_name(:timezone)].try { |zn| TZInfo::Timezone.get(zn) }
|
294
297
|
base = [now, self[crono_trigger_column_name(:started_at)]].compact.max
|
295
298
|
cron_now = tz ? base.in_time_zone(tz) : base
|
296
|
-
Chrono::NextTime.new(now: cron_now, source: self[crono_trigger_column_name(:cron)]).to_time
|
299
|
+
calculated = Chrono::NextTime.new(now: cron_now, source: self[crono_trigger_column_name(:cron)]).to_time
|
300
|
+
|
301
|
+
return calculated unless self[crono_trigger_column_name(:finished_at)]
|
302
|
+
return if calculated > self[crono_trigger_column_name(:finished_at)]
|
303
|
+
|
304
|
+
calculated
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
def set_current_cyctle_id
|
309
|
+
if self.class.column_names.include?(crono_trigger_column_name(:current_cycle_id)) &&
|
310
|
+
self[crono_trigger_column_name(:current_cycle_id)].nil?
|
311
|
+
self[crono_trigger_column_name(:current_cycle_id)] = SecureRandom.uuid
|
297
312
|
end
|
298
313
|
end
|
299
314
|
|
data/lib/crono_trigger/worker.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require "active_support/core_ext/string"
|
2
2
|
|
3
|
+
require "crono_trigger/global_exception_handler"
|
4
|
+
|
3
5
|
module CronoTrigger
|
4
6
|
module Worker
|
5
7
|
HEARTBEAT_INTERVAL = 60
|
@@ -100,9 +102,9 @@ module CronoTrigger
|
|
100
102
|
worker_record.polling_model_names = @model_names
|
101
103
|
worker_record.last_heartbeated_at = Time.current
|
102
104
|
@logger.info("[worker_id:#{@crono_trigger_worker_id}] Send heartbeat to database")
|
103
|
-
worker_record.save
|
105
|
+
worker_record.save!
|
104
106
|
rescue => ex
|
105
|
-
|
107
|
+
CronoTrigger::GlobalExceptionHandler.handle_global_exception(ex)
|
106
108
|
stop
|
107
109
|
end
|
108
110
|
end
|
@@ -128,6 +130,8 @@ module CronoTrigger
|
|
128
130
|
CronoTrigger::Models::Worker.connection_pool.with_connection do
|
129
131
|
CronoTrigger::Models::Worker.find_by(worker_id: @crono_trigger_worker_id)&.destroy
|
130
132
|
end
|
133
|
+
rescue => ex
|
134
|
+
CronoTrigger::GlobalExceptionHandler.handle_global_exception(ex)
|
131
135
|
end
|
132
136
|
|
133
137
|
def handle_signal_from_rdb
|
@@ -137,6 +141,8 @@ module CronoTrigger
|
|
137
141
|
s.kill_me(to_supervisor: s.signal != "TSTP")
|
138
142
|
end
|
139
143
|
end
|
144
|
+
rescue => ex
|
145
|
+
CronoTrigger::GlobalExceptionHandler.handle_global_exception(ex)
|
140
146
|
end
|
141
147
|
end
|
142
148
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crono_trigger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- joker1007
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chrono
|
@@ -263,6 +263,7 @@ files:
|
|
263
263
|
- lib/crono_trigger/cli.rb
|
264
264
|
- lib/crono_trigger/exception_handler.rb
|
265
265
|
- lib/crono_trigger/execution_tracker.rb
|
266
|
+
- lib/crono_trigger/global_exception_handler.rb
|
266
267
|
- lib/crono_trigger/models/execution.rb
|
267
268
|
- lib/crono_trigger/models/signal.rb
|
268
269
|
- lib/crono_trigger/models/worker.rb
|