karafka 2.4.8 → 2.4.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.github/workflows/ci.yml +0 -1
- data/CHANGELOG.md +8 -0
- data/Gemfile +8 -5
- data/Gemfile.lock +23 -14
- data/bin/integrations +5 -0
- data/certs/cert.pem +26 -0
- data/config/locales/errors.yml +4 -0
- data/config/locales/pro_errors.yml +17 -0
- data/karafka.gemspec +1 -1
- data/lib/karafka/admin.rb +42 -0
- data/lib/karafka/contracts/config.rb +2 -0
- data/lib/karafka/errors.rb +3 -2
- data/lib/karafka/pro/loader.rb +2 -1
- data/lib/karafka/pro/processing/strategies/dlq/default.rb +16 -1
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_mom.rb +5 -1
- data/lib/karafka/pro/processing/strategies/dlq/ftr_mom.rb +17 -1
- data/lib/karafka/pro/processing/strategies/dlq/lrj_mom.rb +17 -1
- data/lib/karafka/pro/processing/strategies/dlq/mom.rb +22 -6
- data/lib/karafka/pro/recurring_tasks/consumer.rb +105 -0
- data/lib/karafka/pro/recurring_tasks/contracts/config.rb +53 -0
- data/lib/karafka/pro/recurring_tasks/contracts/task.rb +41 -0
- data/lib/karafka/pro/recurring_tasks/deserializer.rb +35 -0
- data/lib/karafka/pro/recurring_tasks/dispatcher.rb +87 -0
- data/lib/karafka/pro/recurring_tasks/errors.rb +34 -0
- data/lib/karafka/pro/recurring_tasks/executor.rb +152 -0
- data/lib/karafka/pro/recurring_tasks/listener.rb +38 -0
- data/lib/karafka/pro/recurring_tasks/matcher.rb +38 -0
- data/lib/karafka/pro/recurring_tasks/schedule.rb +63 -0
- data/lib/karafka/pro/recurring_tasks/serializer.rb +113 -0
- data/lib/karafka/pro/recurring_tasks/setup/config.rb +52 -0
- data/lib/karafka/pro/recurring_tasks/task.rb +151 -0
- data/lib/karafka/pro/recurring_tasks.rb +87 -0
- data/lib/karafka/pro/routing/features/recurring_tasks/builder.rb +130 -0
- data/lib/karafka/pro/routing/features/recurring_tasks/config.rb +28 -0
- data/lib/karafka/pro/routing/features/recurring_tasks/contracts/topic.rb +40 -0
- data/lib/karafka/pro/routing/features/recurring_tasks/proxy.rb +27 -0
- data/lib/karafka/pro/routing/features/recurring_tasks/topic.rb +44 -0
- data/lib/karafka/pro/routing/features/recurring_tasks.rb +25 -0
- data/lib/karafka/processing/strategies/dlq.rb +16 -2
- data/lib/karafka/processing/strategies/dlq_mom.rb +25 -6
- data/lib/karafka/processing/worker.rb +11 -1
- data/lib/karafka/railtie.rb +11 -22
- data/lib/karafka/routing/features/dead_letter_queue/config.rb +3 -0
- data/lib/karafka/routing/features/dead_letter_queue/contracts/topic.rb +1 -0
- data/lib/karafka/routing/features/dead_letter_queue/topic.rb +7 -2
- data/lib/karafka/routing/features/eofed/contracts/topic.rb +12 -0
- data/lib/karafka/routing/topic.rb +14 -0
- data/lib/karafka/setup/config.rb +3 -0
- data/lib/karafka/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +44 -24
- metadata.gz.sig +0 -0
- data/certs/cert_chain.pem +0 -26
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module RecurringTasks
|
17
|
+
# Recurring Tasks related contracts
|
18
|
+
module Contracts
|
19
|
+
# Ensures that task details are as expected
|
20
|
+
class Task < ::Karafka::Contracts::Base
|
21
|
+
configure do |config|
|
22
|
+
config.error_messages = YAML.safe_load(
|
23
|
+
File.read(
|
24
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
25
|
+
)
|
26
|
+
).fetch('en').fetch('validations').fetch('recurring_tasks')
|
27
|
+
end
|
28
|
+
|
29
|
+
# Regexp to ensure all tasks ids are URL safe
|
30
|
+
ID_REGEXP = /\A[a-zA-Z0-9_-]{1,}\z/
|
31
|
+
|
32
|
+
required(:id) { |val| val.is_a?(String) && val.match?(ID_REGEXP) }
|
33
|
+
required(:cron) { |val| val.is_a?(String) && val.length.positive? }
|
34
|
+
required(:enabled) { |val| [true, false].include?(val) }
|
35
|
+
required(:changed) { |val| [true, false].include?(val) }
|
36
|
+
required(:previous_time) { |val| val.is_a?(Numeric) || val.is_a?(Time) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module RecurringTasks
|
17
|
+
# Recurring Tasks data deserializer. We compress data ourselves because we cannot rely on
|
18
|
+
# any external optional features like certain compression types, etc. By doing this that way
|
19
|
+
# we can ensure we have full control over the compression.
|
20
|
+
#
|
21
|
+
# @note We use `symbolize_names` because we want to use the same convention of hash building
|
22
|
+
# for producing, consuming and displaying related data as in other places.
|
23
|
+
class Deserializer
|
24
|
+
# @param message [::Karafka::Messages::Message]
|
25
|
+
# @return [Hash] deserialized data
|
26
|
+
def call(message)
|
27
|
+
::JSON.parse(
|
28
|
+
Zlib::Inflate.inflate(message.raw_payload),
|
29
|
+
symbolize_names: true
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module RecurringTasks
|
17
|
+
# Dispatches appropriate recurring tasks related messages to expected topics
|
18
|
+
class Dispatcher
|
19
|
+
class << self
|
20
|
+
# Snapshots to Kafka current schedule state
|
21
|
+
def schedule
|
22
|
+
produce(
|
23
|
+
topics.schedules,
|
24
|
+
'state:schedule',
|
25
|
+
serializer.schedule(::Karafka::Pro::RecurringTasks.schedule)
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Dispatches the command request
|
30
|
+
#
|
31
|
+
# @param name [String, Symbol] name of the command we want to deal with in the process
|
32
|
+
# @param task_id [String] id of the process. We use name instead of id only
|
33
|
+
# because in the web ui we work with the full name and it is easier. Since
|
34
|
+
def command(name, task_id)
|
35
|
+
produce(
|
36
|
+
topics.schedules,
|
37
|
+
"command:#{name}:#{task_id}",
|
38
|
+
serializer.command(name, task_id)
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Dispatches the task execution log record
|
43
|
+
# @param event [Karafka::Core::Monitoring::Event]
|
44
|
+
def log(event)
|
45
|
+
produce(
|
46
|
+
topics.logs,
|
47
|
+
event[:task].id,
|
48
|
+
serializer.log(event)
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
# @return [::WaterDrop::Producer] web ui producer
|
55
|
+
def producer
|
56
|
+
::Karafka::App.config.recurring_tasks.producer
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [String] consumers commands topic
|
60
|
+
def topics
|
61
|
+
::Karafka::App.config.recurring_tasks.topics
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Serializer]
|
65
|
+
def serializer
|
66
|
+
Serializer.new
|
67
|
+
end
|
68
|
+
|
69
|
+
# Converts payload to json, compresses it and dispatches to Kafka
|
70
|
+
#
|
71
|
+
# @param topic [String] target topic
|
72
|
+
# @param key [String]
|
73
|
+
# @param payload [Hash] hash with payload
|
74
|
+
def produce(topic, key, payload)
|
75
|
+
producer.produce_async(
|
76
|
+
topic: topic,
|
77
|
+
key: key,
|
78
|
+
partition: 0,
|
79
|
+
payload: payload,
|
80
|
+
headers: { 'zlib' => 'true' }
|
81
|
+
)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module RecurringTasks
|
17
|
+
# Recurring Tasks related errors
|
18
|
+
module Errors
|
19
|
+
# Base for all the recurring tasks errors
|
20
|
+
BaseError = Class.new(::Karafka::Errors::BaseError)
|
21
|
+
|
22
|
+
# Raised when older cron manager version is trying to work with newer schema
|
23
|
+
IncompatibleSchemaError = Class.new(BaseError)
|
24
|
+
|
25
|
+
# Raised if you use versioned schedule and an existing schedule from Kafka was picked up
|
26
|
+
# by a process with older schedule version.
|
27
|
+
# This is a safe-guard to protect against a cases where there would be a temporary
|
28
|
+
# reassignment of newer schedule data into older process during deployment. It should go
|
29
|
+
# away once all processes are rolled.
|
30
|
+
IncompatibleScheduleError = Class.new(BaseError)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module RecurringTasks
|
17
|
+
# Executor is responsible for management of the state of recurring tasks schedule and
|
18
|
+
# is the heart of recurring tasks. It coordinates the replaying process as well as
|
19
|
+
# tracking of data on changes.
|
20
|
+
class Executor
|
21
|
+
# Task commands we support and that can be triggered on tasks (if matched)
|
22
|
+
COMMANDS = %w[
|
23
|
+
disable
|
24
|
+
enable
|
25
|
+
trigger
|
26
|
+
].freeze
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
@replaying = true
|
30
|
+
@incompatible = false
|
31
|
+
@catchup_commands = []
|
32
|
+
@catchup_schedule = nil
|
33
|
+
@matcher = Matcher.new
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Boolean] are we in the replaying phase or not (false means, regular operations)
|
37
|
+
def replaying?
|
38
|
+
@replaying
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Boolean] Is the current process schedule incompatible (older) than the one
|
42
|
+
# that we have in memory
|
43
|
+
def incompatible?
|
44
|
+
@incompatible
|
45
|
+
end
|
46
|
+
|
47
|
+
# Applies given command to task (or many tasks) by running the command on tasks that match
|
48
|
+
# @param command_hash [Hash] deserialized command data
|
49
|
+
def apply_command(command_hash)
|
50
|
+
cmd_name = command_hash[:command][:name]
|
51
|
+
|
52
|
+
raise(Karafka::Errors::UnsupportedCaseError, cmd_name) unless COMMANDS.include?(cmd_name)
|
53
|
+
|
54
|
+
schedule.each do |task|
|
55
|
+
next unless @matcher.matches?(task, command_hash)
|
56
|
+
|
57
|
+
task.public_send(cmd_name)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Updates the catchup state
|
62
|
+
# @param schedule_hash [Hash] deserialized schedule hash hash
|
63
|
+
def update_state(schedule_hash)
|
64
|
+
@catchup_schedule = schedule_hash
|
65
|
+
end
|
66
|
+
|
67
|
+
# Once all previous data is accumulated runs the catchup process to establish current
|
68
|
+
# state of the recurring tasks schedule execution.
|
69
|
+
#
|
70
|
+
# It includes applying any requested commands as well as synchronizing execution details
|
71
|
+
# for existing schedule and making sure all is loaded correctly.
|
72
|
+
def replay
|
73
|
+
# Ensure replaying happens only once
|
74
|
+
return unless @replaying
|
75
|
+
|
76
|
+
@replaying = false
|
77
|
+
|
78
|
+
# When there is nothing to replay and synchronize, we can just save the state and
|
79
|
+
# proceed
|
80
|
+
if @catchup_commands.empty? && @catchup_schedule.nil?
|
81
|
+
snapshot
|
82
|
+
|
83
|
+
return
|
84
|
+
end
|
85
|
+
|
86
|
+
# If the schedule version we have in Kafka is higher than ours, we cannot proceed
|
87
|
+
# This prevents us from applying older changes to a new schedule
|
88
|
+
if @catchup_schedule[:schedule_version] > schedule.version
|
89
|
+
@incompatible = true
|
90
|
+
|
91
|
+
return
|
92
|
+
end
|
93
|
+
|
94
|
+
# Now we can synchronize the in-memory state based on the last state stored in Kafka
|
95
|
+
schedule.each do |task|
|
96
|
+
stored_task = @catchup_schedule[:tasks][task.id.to_sym]
|
97
|
+
|
98
|
+
next unless stored_task
|
99
|
+
|
100
|
+
stored_previous_time = stored_task[:previous_time]
|
101
|
+
task.previous_time = stored_previous_time.zero? ? 0 : Time.at(stored_previous_time)
|
102
|
+
|
103
|
+
stored_task[:enabled] ? task.enable : task.disable
|
104
|
+
end
|
105
|
+
|
106
|
+
@catchup_commands.each do |cmd|
|
107
|
+
apply_command(cmd)
|
108
|
+
end
|
109
|
+
|
110
|
+
# We make sure to save in Kafka once more once everything is up to date
|
111
|
+
snapshot
|
112
|
+
|
113
|
+
@catchup_schedule = nil
|
114
|
+
@catchup_commands = []
|
115
|
+
end
|
116
|
+
|
117
|
+
# Run all tasks that should run at a given time and if any tasks were changed in any way
|
118
|
+
# or executed, stores the most recent state back to Kafka
|
119
|
+
def call
|
120
|
+
changed = false
|
121
|
+
|
122
|
+
schedule.each do |task|
|
123
|
+
changed = true if task.changed?
|
124
|
+
|
125
|
+
unless task.call?
|
126
|
+
task.clear
|
127
|
+
|
128
|
+
next
|
129
|
+
end
|
130
|
+
|
131
|
+
changed = true
|
132
|
+
task.call
|
133
|
+
end
|
134
|
+
|
135
|
+
snapshot if changed
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
# @return [Karafka::Pro::RecurringTasks::Schedule] current in-memory schedule
|
141
|
+
def schedule
|
142
|
+
::Karafka::Pro::RecurringTasks.schedule
|
143
|
+
end
|
144
|
+
|
145
|
+
# Dispatches the current schedule state to Kafka
|
146
|
+
def snapshot
|
147
|
+
Dispatcher.schedule
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module RecurringTasks
|
17
|
+
# Listener we use to track execution of recurring tasks and publish those events into the
|
18
|
+
# recurring tasks log table
|
19
|
+
class Listener
|
20
|
+
# @param event [Karafka::Core::Monitoring::Event] task execution event
|
21
|
+
def on_recurring_tasks_task_executed(event)
|
22
|
+
Dispatcher.log(event)
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param event [Karafka::Core::Monitoring::Event] error that occurred
|
26
|
+
# @note We do nothing with other errors. We only want to log and dispatch information about
|
27
|
+
# the recurring tasks errors. The general Web UI error tracking may also work but those
|
28
|
+
# are independent. It is not to replace the Web UI tracking but to just log failed
|
29
|
+
# executions in the same way as successful but just with the failure as an outcome.
|
30
|
+
def on_error_occurred(event)
|
31
|
+
return unless event[:type] == 'recurring_tasks.task.execute.error'
|
32
|
+
|
33
|
+
Dispatcher.log(event)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module RecurringTasks
|
17
|
+
# Matcher used to check if given command can be applied to a given task.
|
18
|
+
class Matcher
|
19
|
+
# @param task [Karafka::Pro::RecurringTasks::Task]
|
20
|
+
# @param payload [Hash] command message payload
|
21
|
+
# @return [Boolean] is this message dedicated to current process and is actionable
|
22
|
+
def matches?(task, payload)
|
23
|
+
# We only match commands
|
24
|
+
return false unless payload[:type] == 'command'
|
25
|
+
|
26
|
+
# * is a wildcard to match all for batch commands
|
27
|
+
return false unless payload[:task][:id] == '*' || payload[:task][:id] == task.id
|
28
|
+
|
29
|
+
# Ignore messages that have different schema. This can happen in the middle of
|
30
|
+
# upgrades of the framework. We ignore this not to risk compatibility issues
|
31
|
+
return false unless payload[:schema_version] == Serializer::SCHEMA_VERSION
|
32
|
+
|
33
|
+
true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module RecurringTasks
|
17
|
+
# Represents the current code-context schedule with defined tasks and their cron execution
|
18
|
+
# details. Single schedule includes all the information about all the tasks that we have
|
19
|
+
# defined and to be executed in a given moment in time.
|
20
|
+
class Schedule
|
21
|
+
# @return [String]
|
22
|
+
attr_reader :version
|
23
|
+
|
24
|
+
# @return [Hash<String, Task>]
|
25
|
+
attr_reader :tasks
|
26
|
+
|
27
|
+
# @param version [String] schedule version. In case of usage of versioning it is used to
|
28
|
+
# ensure, that older still active processes do not intercept the assignment to run older
|
29
|
+
# version of the scheduler. It is important to make sure, that this string is comparable.
|
30
|
+
def initialize(version:)
|
31
|
+
@version = version
|
32
|
+
@tasks = {}
|
33
|
+
end
|
34
|
+
|
35
|
+
# Adds task into the tasks accumulator
|
36
|
+
# @param task [Task]
|
37
|
+
# @note In case of multiple tasks with the same id, it will overwrite
|
38
|
+
def <<(task)
|
39
|
+
@tasks[task.id] = task
|
40
|
+
end
|
41
|
+
|
42
|
+
# Iterates over tasks yielding them one after another
|
43
|
+
# @param block [Proc] block that will be executed with each task
|
44
|
+
def each(&block)
|
45
|
+
@tasks.each_value(&block)
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param id [String] id of a particular recurring task
|
49
|
+
# @return [Task, nil] task with a given id or nil if not found
|
50
|
+
def find(id)
|
51
|
+
@tasks[id]
|
52
|
+
end
|
53
|
+
|
54
|
+
# Allows us to have a nice DSL for defining schedules
|
55
|
+
# @param args [Array] attributes accepted by the task initializer
|
56
|
+
# @param block [Proc] block to execute
|
57
|
+
def schedule(**args, &block)
|
58
|
+
self << Task.new(**args, &block)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module RecurringTasks
|
17
|
+
# Converts schedule command and log details into data we can dispatch to Kafka.
|
18
|
+
class Serializer
|
19
|
+
# Current recurring tasks related schema structure
|
20
|
+
SCHEMA_VERSION = '1.0'
|
21
|
+
|
22
|
+
# @param schedule [Karafka::Pro::RecurringTasks::Schedule] schedule to serialize
|
23
|
+
# @return [String] serialized and compressed current schedule data with its tasks and their
|
24
|
+
# current state.
|
25
|
+
def schedule(schedule)
|
26
|
+
tasks = {}
|
27
|
+
|
28
|
+
schedule.each do |task|
|
29
|
+
tasks[task.id] = {
|
30
|
+
id: task.id,
|
31
|
+
cron: task.cron.original,
|
32
|
+
previous_time: task.previous_time.to_i,
|
33
|
+
next_time: task.next_time.to_i,
|
34
|
+
enabled: task.enabled?
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
data = {
|
39
|
+
schema_version: SCHEMA_VERSION,
|
40
|
+
schedule_version: schedule.version,
|
41
|
+
dispatched_at: Time.now.to_f,
|
42
|
+
type: 'schedule',
|
43
|
+
tasks: tasks
|
44
|
+
}
|
45
|
+
|
46
|
+
compress(
|
47
|
+
serialize(data)
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
# @param command_name [String] command name
|
52
|
+
# @param task_id [String] task id or '*' to match all.
|
53
|
+
# @return [String] serialized and compressed command data
|
54
|
+
def command(command_name, task_id)
|
55
|
+
data = {
|
56
|
+
schema_version: SCHEMA_VERSION,
|
57
|
+
schedule_version: ::Karafka::Pro::RecurringTasks.schedule.version,
|
58
|
+
dispatched_at: Time.now.to_f,
|
59
|
+
type: 'command',
|
60
|
+
command: {
|
61
|
+
name: command_name
|
62
|
+
},
|
63
|
+
task: {
|
64
|
+
id: task_id
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
compress(
|
69
|
+
serialize(data)
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
# @param event [Karafka::Core::Monitoring::Event] recurring task dispatch event
|
74
|
+
# @return [String] serialized and compressed event log data
|
75
|
+
def log(event)
|
76
|
+
task = event[:task]
|
77
|
+
|
78
|
+
data = {
|
79
|
+
schema_version: SCHEMA_VERSION,
|
80
|
+
schedule_version: ::Karafka::Pro::RecurringTasks.schedule.version,
|
81
|
+
dispatched_at: Time.now.to_f,
|
82
|
+
type: 'log',
|
83
|
+
task: {
|
84
|
+
id: task.id,
|
85
|
+
time_taken: event.payload[:time] || -1,
|
86
|
+
result: event.payload.key?(:error) ? 'failure' : 'success'
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
compress(
|
91
|
+
serialize(data)
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# @param hash [Hash] hash to cast to json
|
98
|
+
# @return [String] json hash
|
99
|
+
def serialize(hash)
|
100
|
+
hash.to_json
|
101
|
+
end
|
102
|
+
|
103
|
+
# Compresses the provided data
|
104
|
+
#
|
105
|
+
# @param data [String] data to compress
|
106
|
+
# @return [String] compressed data
|
107
|
+
def compress(data)
|
108
|
+
Zlib::Deflate.deflate(data)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module RecurringTasks
|
17
|
+
# Setup and config related recurring tasks components
|
18
|
+
module Setup
|
19
|
+
# Config for recurring tasks
|
20
|
+
class Config
|
21
|
+
extend ::Karafka::Core::Configurable
|
22
|
+
|
23
|
+
setting(:consumer_class, default: Consumer)
|
24
|
+
setting(:deserializer, default: Deserializer.new)
|
25
|
+
setting(:group_id, default: 'karafka_recurring_tasks')
|
26
|
+
# By default we will run the scheduling every 15 seconds since we provide a minute-based
|
27
|
+
# precision
|
28
|
+
setting(:interval, default: 15_000)
|
29
|
+
# Should we log the executions. If true (default) with each cron execution, there will
|
30
|
+
# be a special message published. Useful for debugging.
|
31
|
+
setting(:logging, default: true)
|
32
|
+
|
33
|
+
# Producer to be used by the recurring tasks.
|
34
|
+
# By default it is a `Karafka.producer`, however it may be overwritten if we want to use
|
35
|
+
# a separate instance in case of heavy usage of the transactional producer, etc.
|
36
|
+
setting(
|
37
|
+
:producer,
|
38
|
+
constructor: -> { ::Karafka.producer },
|
39
|
+
lazy: true
|
40
|
+
)
|
41
|
+
|
42
|
+
setting(:topics) do
|
43
|
+
setting(:schedules, default: 'karafka_recurring_tasks_schedules')
|
44
|
+
setting(:logs, default: 'karafka_recurring_tasks_logs')
|
45
|
+
end
|
46
|
+
|
47
|
+
configure
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|