exekutor 0.1.0 → 0.1.1
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
- checksums.yaml.gz.sig +0 -0
- data/exe/exekutor +2 -2
- data/lib/active_job/queue_adapters/exekutor_adapter.rb +2 -1
- data/lib/exekutor/asynchronous.rb +143 -75
- data/lib/exekutor/cleanup.rb +27 -28
- data/lib/exekutor/configuration.rb +48 -25
- data/lib/exekutor/hook.rb +15 -11
- data/lib/exekutor/info/worker.rb +3 -3
- data/lib/exekutor/internal/base_record.rb +2 -1
- data/lib/exekutor/internal/callbacks.rb +55 -35
- data/lib/exekutor/internal/cli/app.rb +31 -23
- data/lib/exekutor/internal/cli/application_loader.rb +17 -6
- data/lib/exekutor/internal/cli/cleanup.rb +54 -40
- data/lib/exekutor/internal/cli/daemon.rb +9 -11
- data/lib/exekutor/internal/cli/default_option_value.rb +3 -1
- data/lib/exekutor/internal/cli/info.rb +117 -84
- data/lib/exekutor/internal/cli/manager.rb +190 -123
- data/lib/exekutor/internal/configuration_builder.rb +40 -27
- data/lib/exekutor/internal/database_connection.rb +6 -0
- data/lib/exekutor/internal/executable.rb +12 -7
- data/lib/exekutor/internal/executor.rb +50 -21
- data/lib/exekutor/internal/hooks.rb +11 -8
- data/lib/exekutor/internal/listener.rb +66 -39
- data/lib/exekutor/internal/logger.rb +28 -10
- data/lib/exekutor/internal/provider.rb +93 -74
- data/lib/exekutor/internal/reserver.rb +27 -12
- data/lib/exekutor/internal/status_server.rb +81 -49
- data/lib/exekutor/job.rb +1 -1
- data/lib/exekutor/job_error.rb +1 -1
- data/lib/exekutor/job_options.rb +22 -13
- data/lib/exekutor/plugins/appsignal.rb +7 -5
- data/lib/exekutor/plugins.rb +8 -4
- data/lib/exekutor/queue.rb +40 -22
- data/lib/exekutor/version.rb +1 -1
- data/lib/exekutor/worker.rb +88 -47
- data/lib/exekutor.rb +2 -2
- data/lib/generators/exekutor/configuration_generator.rb +9 -5
- data/lib/generators/exekutor/install_generator.rb +26 -15
- data/lib/generators/exekutor/templates/install/migrations/create_exekutor_schema.rb.erb +11 -10
- data.tar.gz.sig +0 -0
- metadata +63 -19
- metadata.gz.sig +0 -0
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
raise Exekutor::Plugins::LoadError, "Appsignal not found, is the gem loaded?" unless defined? Appsignal
|
2
4
|
|
3
5
|
module Exekutor
|
@@ -21,14 +23,14 @@ module Exekutor
|
|
21
23
|
|
22
24
|
::Appsignal.monitor_transaction(
|
23
25
|
"perform_job.exekutor",
|
24
|
-
class: payload[
|
26
|
+
class: payload["job_class"],
|
25
27
|
method: "perform",
|
26
28
|
params: params,
|
27
29
|
metadata: {
|
28
|
-
id: payload[
|
29
|
-
queue: payload[
|
30
|
-
priority: payload.fetch(
|
31
|
-
attempts: payload.fetch(
|
30
|
+
id: payload["job_id"],
|
31
|
+
queue: payload["queue_name"],
|
32
|
+
priority: payload.fetch("priority", Exekutor.config.default_queue_priority),
|
33
|
+
attempts: payload.fetch("attempts", 0)
|
32
34
|
},
|
33
35
|
queue_start: job[:scheduled_at]
|
34
36
|
) do
|
data/lib/exekutor/plugins.rb
CHANGED
@@ -1,13 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The Exekutor namespace
|
1
4
|
module Exekutor
|
2
5
|
module Plugins
|
6
|
+
# Raised when a plugin cannot be loaded
|
3
7
|
class LoadError < ::LoadError; end
|
4
8
|
end
|
5
9
|
|
6
10
|
def self.load_plugin(name)
|
7
|
-
|
8
|
-
require_relative "plugins/#{name}"
|
9
|
-
else
|
11
|
+
unless File.exist? File.join(__dir__, "plugins/#{name}.rb")
|
10
12
|
raise Plugins::LoadError, "The #{name} plugin does not exist. Have you spelled it correctly?"
|
11
13
|
end
|
14
|
+
|
15
|
+
require_relative "plugins/#{name}"
|
12
16
|
end
|
13
|
-
end
|
17
|
+
end
|
data/lib/exekutor/queue.rb
CHANGED
@@ -6,7 +6,7 @@ module Exekutor
|
|
6
6
|
# Used when logging the SQL queries
|
7
7
|
# @private
|
8
8
|
ACTION_NAME = "Exekutor::Enqueue"
|
9
|
-
private_constant
|
9
|
+
private_constant :ACTION_NAME
|
10
10
|
|
11
11
|
# Valid range for job priority
|
12
12
|
# @private
|
@@ -38,42 +38,59 @@ module Exekutor
|
|
38
38
|
# @param scheduled_at [Time,Date,Integer,Float] when the job should be performed
|
39
39
|
# @return [void]
|
40
40
|
def create_records(jobs, scheduled_at: nil)
|
41
|
-
unless jobs.is_a?(Array) && jobs.all?
|
41
|
+
unless jobs.is_a?(Array) && jobs.all?(ActiveJob::Base)
|
42
42
|
raise ArgumentError, "jobs must be an array with ActiveJob items"
|
43
43
|
end
|
44
44
|
|
45
|
+
scheduled_at = parse_scheduled_at(scheduled_at)
|
46
|
+
json_serializer = Exekutor.config.load_json_serializer
|
47
|
+
|
48
|
+
Internal::Hooks.run :enqueue, jobs do
|
49
|
+
insert_job_records(jobs, scheduled_at, json_serializer)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Converts the given value to an epoch timestamp. Returns the current epoch timestamp if the given value is nil
|
54
|
+
# @param scheduled_at [nil,Numeric,Time,Date] The timestamp to convert to an epoch timestamp
|
55
|
+
# @return [Float,Integer] The epoch equivalent of +scheduled_at+
|
56
|
+
def parse_scheduled_at(scheduled_at)
|
45
57
|
if scheduled_at.nil?
|
46
|
-
|
58
|
+
Time.now.to_i
|
47
59
|
else
|
48
60
|
case scheduled_at
|
49
61
|
when Integer, Float
|
50
62
|
raise ArgumentError, "scheduled_at must be a valid epoch" unless scheduled_at.positive?
|
63
|
+
|
64
|
+
scheduled_at
|
51
65
|
when Time
|
52
|
-
scheduled_at
|
66
|
+
scheduled_at.to_f
|
53
67
|
when Date
|
54
|
-
scheduled_at
|
68
|
+
scheduled_at.at_beginning_of_day.to_f
|
55
69
|
else
|
56
70
|
raise ArgumentError, "scheduled_at must be an epoch, time, or date"
|
57
71
|
end
|
58
72
|
end
|
73
|
+
end
|
59
74
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
SQL
|
75
|
+
# Fires off an INSERT INTO query for the given jobs
|
76
|
+
# @param jobs [Array<ActiveJob::Base>] the jobs to insert
|
77
|
+
# @param scheduled_at [Integer,Float] the scheduled execution time for the jobs as an epoch timestamp
|
78
|
+
# @param json_serializer [#dump] the serializer to use to convert hashes into JSON
|
79
|
+
def insert_job_records(jobs, scheduled_at, json_serializer)
|
80
|
+
if jobs.one?
|
81
|
+
sql_binds = job_sql_binds(jobs.first, scheduled_at, json_serializer)
|
82
|
+
Exekutor::Job.connection.exec_query <<~SQL, ACTION_NAME, sql_binds, prepare: true
|
83
|
+
INSERT INTO exekutor_jobs ("queue", "priority", "scheduled_at", "active_job_id", "payload", "options") VALUES ($1, $2, to_timestamp($3), $4, $5, $6) RETURNING id;
|
84
|
+
SQL
|
85
|
+
else
|
86
|
+
insert_statements = jobs.map do |job|
|
87
|
+
Exekutor::Job.sanitize_sql_for_assignment(
|
88
|
+
["(?, ?, to_timestamp(?), ?, ?::jsonb, ?::jsonb)", *job_sql_binds(job, scheduled_at, json_serializer)]
|
89
|
+
)
|
76
90
|
end
|
91
|
+
Exekutor::Job.connection.insert <<~SQL, ACTION_NAME
|
92
|
+
INSERT INTO exekutor_jobs ("queue", "priority", "scheduled_at", "active_job_id", "payload", "options") VALUES #{insert_statements.join(",")}
|
93
|
+
SQL
|
77
94
|
end
|
78
95
|
end
|
79
96
|
|
@@ -86,7 +103,8 @@ module Exekutor
|
|
86
103
|
if job.queue_name.blank?
|
87
104
|
raise Error, "The queue must be set"
|
88
105
|
elsif job.queue_name && job.queue_name.length > Queue::MAX_NAME_LENGTH
|
89
|
-
raise Error,
|
106
|
+
raise Error,
|
107
|
+
"The queue name \"#{job.queue_name}\" is too long, the limit is #{Queue::MAX_NAME_LENGTH} characters"
|
90
108
|
end
|
91
109
|
|
92
110
|
options = exekutor_options job
|
data/lib/exekutor/version.rb
CHANGED
data/lib/exekutor/worker.rb
CHANGED
@@ -23,64 +23,43 @@ module Exekutor
|
|
23
23
|
# @option config [Array<String>] :queues the queues to work on
|
24
24
|
# @option config [Integer] :min_threads the minimum number of execution threads that should be active
|
25
25
|
# @option config [Integer] :max_threads the maximum number of execution threads that may be active
|
26
|
-
# @option config [Integer] :max_thread_idletime the maximum number of seconds a thread may be idle before being
|
26
|
+
# @option config [Integer] :max_thread_idletime the maximum number of seconds a thread may be idle before being
|
27
|
+
# stopped
|
27
28
|
# @option config [Integer] :polling_interval the polling interval in seconds
|
28
29
|
# @option config [Float] :poling_jitter the polling jitter
|
29
30
|
# @option config [Boolean] :set_db_connection_name whether the DB connection name should be set
|
30
|
-
# @option config [Integer,Boolean] :wait_for_termination how long the worker should wait on jobs to be completed
|
31
|
+
# @option config [Integer,Boolean] :wait_for_termination how long the worker should wait on jobs to be completed
|
32
|
+
# before exiting
|
31
33
|
# @option config [Integer] :status_server_port the port to run the status server on
|
32
34
|
# @option config [String] :status_server_handler The name of the rack handler to use for the status server
|
33
|
-
# @option config [Integer] :healthcheck_timeout The timeout of a worker in minutes before the healthcheck server
|
35
|
+
# @option config [Integer] :healthcheck_timeout The timeout of a worker in minutes before the healthcheck server
|
36
|
+
# deems it as down
|
34
37
|
def initialize(config = {})
|
35
38
|
super()
|
36
39
|
@config = config
|
37
40
|
@record = create_record!
|
38
41
|
|
39
|
-
|
40
|
-
@executor = Internal::Executor.new(**config.slice(:min_threads, :max_threads, :max_thread_idletime,
|
41
|
-
:delete_completed_jobs, :delete_discarded_jobs,
|
42
|
-
:delete_failed_jobs))
|
42
|
+
provider_pool = create_provider_pool(config)
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
provider_threads += 1 if config[:status_server_port].to_i > 0
|
47
|
-
|
48
|
-
provider_pool = Concurrent::FixedThreadPool.new provider_threads, max_queue: provider_threads,
|
49
|
-
name: "exekutor-provider"
|
50
|
-
|
51
|
-
@provider = Internal::Provider.new reserver: @reserver, executor: @executor, pool: provider_pool,
|
52
|
-
**provider_options(config)
|
44
|
+
@executor = create_executor(config)
|
45
|
+
@provider = create_provider(config, @executor, provider_pool)
|
53
46
|
|
54
47
|
@executables = [@executor, @provider]
|
55
|
-
if config.fetch(:enable_listener, true)
|
56
|
-
|
57
|
-
**listener_options(config)
|
58
|
-
@executables << listener
|
59
|
-
end
|
60
|
-
if config[:status_server_port].to_i > 0
|
61
|
-
server = Internal::StatusServer.new worker: self, pool: provider_pool, **status_server_options(config)
|
62
|
-
@executables << server
|
63
|
-
end
|
48
|
+
create_listener(provider_pool, config) if config.fetch(:enable_listener, true)
|
49
|
+
create_status_server(provider_pool, config) if config[:status_server_port].to_i.positive?
|
64
50
|
@executables.freeze
|
65
|
-
|
66
|
-
@executor.after_execute(@record) do |_job, worker_info|
|
67
|
-
worker_info.heartbeat! rescue nil
|
68
|
-
@provider.poll if @provider.running?
|
69
|
-
end
|
70
|
-
@provider.on_queue_empty(@record) do |worker_info|
|
71
|
-
worker_info.heartbeat! rescue nil
|
72
|
-
@executor.prune_pool
|
73
|
-
end
|
74
51
|
end
|
75
52
|
|
76
53
|
# Starts the worker. Does nothing if the worker has already started.
|
77
54
|
# @return [Boolean] whether the worker was started
|
78
55
|
def start
|
79
56
|
return false unless compare_and_set_state(:pending, :started)
|
57
|
+
|
80
58
|
Internal::Hooks.run :startup, self do
|
81
59
|
@executables.each(&:start)
|
82
60
|
@record.update(status: "r")
|
83
61
|
end
|
62
|
+
|
84
63
|
true
|
85
64
|
end
|
86
65
|
|
@@ -89,22 +68,21 @@ module Exekutor
|
|
89
68
|
# @return true
|
90
69
|
def stop
|
91
70
|
Internal::Hooks.run :shutdown, self do
|
92
|
-
|
71
|
+
self.state = :stopped
|
93
72
|
unless @record.destroyed?
|
94
73
|
begin
|
95
74
|
@record.update(status: "s")
|
96
|
-
rescue
|
97
|
-
#ignored
|
75
|
+
rescue StandardError
|
76
|
+
# ignored
|
98
77
|
end
|
99
78
|
end
|
100
79
|
@executables.reverse_each(&:stop)
|
101
|
-
|
102
|
-
wait_for_termination @config[:wait_for_termination] if @config[:wait_for_termination]
|
80
|
+
wait_for_termination @config[:wait_for_termination]
|
103
81
|
|
104
82
|
begin
|
105
83
|
@record.destroy
|
106
|
-
rescue
|
107
|
-
#ignored
|
84
|
+
rescue StandardError
|
85
|
+
# ignored
|
108
86
|
end
|
109
87
|
@stop_event&.set if defined?(@stop_event)
|
110
88
|
end
|
@@ -121,8 +99,8 @@ module Exekutor
|
|
121
99
|
@executor.kill
|
122
100
|
begin
|
123
101
|
@record.destroy
|
124
|
-
rescue
|
125
|
-
#ignored
|
102
|
+
rescue StandardError
|
103
|
+
# ignored
|
126
104
|
end
|
127
105
|
true
|
128
106
|
end
|
@@ -138,7 +116,7 @@ module Exekutor
|
|
138
116
|
|
139
117
|
# Reserves and executes jobs.
|
140
118
|
def reserve_jobs
|
141
|
-
@provider.poll
|
119
|
+
@provider.poll if @provider&.running?
|
142
120
|
end
|
143
121
|
|
144
122
|
# The worker ID.
|
@@ -146,24 +124,76 @@ module Exekutor
|
|
146
124
|
@record.id
|
147
125
|
end
|
148
126
|
|
127
|
+
# @return [Time,nil] The timestamp of the last heartbeat. The timestamp is truncated to whole minutes.
|
149
128
|
def last_heartbeat
|
150
129
|
@record.last_heartbeat_at
|
151
130
|
end
|
152
131
|
|
132
|
+
# Returns the thread usage for this worker. The resulting hash will contain the following key-value pairs:
|
133
|
+
# - +:minimum+, (Integer) the minimum number of threads that should be active;
|
134
|
+
# - +:maximum+, (Integer) the maximum number of threads may should be active;
|
135
|
+
# - +:available+, (Integer) the number of threads that are available to execute new jobs;
|
136
|
+
# - +:usage_percent+, (Float, 0-100) the percentage of workers that are currently busy executing jobs.
|
137
|
+
# @return [Hash] the thread usage
|
153
138
|
def thread_stats
|
154
139
|
available = @executor.available_threads
|
140
|
+
usage_percent = (((100 - (available * 100.0 / @executor.maximum_threads))).round(2) if @executor.running?)
|
155
141
|
{
|
156
142
|
minimum: @executor.minimum_threads,
|
157
143
|
maximum: @executor.maximum_threads,
|
158
144
|
available: available,
|
159
|
-
usage_percent:
|
160
|
-
((1 - (available.to_f / @executor.maximum_threads)) * 100).round(2)
|
161
|
-
end
|
145
|
+
usage_percent: usage_percent
|
162
146
|
}
|
163
147
|
end
|
164
148
|
|
165
149
|
private
|
166
150
|
|
151
|
+
def create_provider_pool(config)
|
152
|
+
provider_threads = 1
|
153
|
+
provider_threads += 1 if config.fetch(:enable_listener, true)
|
154
|
+
provider_threads += 1 if config[:status_server_port].to_i.positive?
|
155
|
+
|
156
|
+
Concurrent::FixedThreadPool.new provider_threads, max_queue: provider_threads, name: "exekutor-provider"
|
157
|
+
end
|
158
|
+
|
159
|
+
def create_executor(worker_options)
|
160
|
+
executor = Internal::Executor.new(**executor_options(worker_options))
|
161
|
+
|
162
|
+
executor.after_execute(@record) do |_job, worker_info|
|
163
|
+
begin
|
164
|
+
worker_info.heartbeat!
|
165
|
+
rescue StandardError
|
166
|
+
# ignored
|
167
|
+
end
|
168
|
+
reserve_jobs
|
169
|
+
end
|
170
|
+
|
171
|
+
executor
|
172
|
+
end
|
173
|
+
|
174
|
+
def executor_options(worker_options)
|
175
|
+
worker_options.slice(:min_threads, :max_threads, :max_thread_idletime,
|
176
|
+
:delete_completed_jobs, :delete_discarded_jobs,
|
177
|
+
:delete_failed_jobs)
|
178
|
+
end
|
179
|
+
|
180
|
+
def create_provider(worker_options, executor, thread_pool)
|
181
|
+
@reserver = Internal::Reserver.new @record.id, worker_options[:queues]
|
182
|
+
provider = Internal::Provider.new reserver: @reserver, executor: executor, pool: thread_pool,
|
183
|
+
**provider_options(worker_options)
|
184
|
+
|
185
|
+
provider.on_queue_empty(@record, executor) do |worker_info, thr_executor|
|
186
|
+
begin
|
187
|
+
worker_info.heartbeat!
|
188
|
+
rescue StandardError
|
189
|
+
# ignored
|
190
|
+
end
|
191
|
+
thr_executor.prune_pool
|
192
|
+
end
|
193
|
+
|
194
|
+
provider
|
195
|
+
end
|
196
|
+
|
167
197
|
def provider_options(worker_options)
|
168
198
|
worker_options.slice(:polling_interval, :polling_jitter).transform_keys do |key|
|
169
199
|
case key
|
@@ -175,10 +205,21 @@ module Exekutor
|
|
175
205
|
end
|
176
206
|
end
|
177
207
|
|
208
|
+
def create_listener(provider_pool, config)
|
209
|
+
listener = Internal::Listener.new worker_id: @record.id, provider: @provider, pool: provider_pool,
|
210
|
+
**listener_options(config)
|
211
|
+
@executables << listener
|
212
|
+
end
|
213
|
+
|
178
214
|
def listener_options(worker_options)
|
179
215
|
worker_options.slice(:queues, :set_db_connection_name)
|
180
216
|
end
|
181
217
|
|
218
|
+
def create_status_server(provider_pool, config)
|
219
|
+
server = Internal::StatusServer.new worker: self, pool: provider_pool, **status_server_options(config)
|
220
|
+
@executables << server
|
221
|
+
end
|
222
|
+
|
182
223
|
def status_server_options(worker_options)
|
183
224
|
worker_options.slice(:status_server_port, :status_server_handler, :healthcheck_timeout).transform_keys do |key|
|
184
225
|
case key
|
data/lib/exekutor.rb
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
require_relative "exekutor/version"
|
4
4
|
|
5
|
+
# The Exekutor namespace
|
5
6
|
module Exekutor
|
6
|
-
|
7
7
|
# Base error class
|
8
8
|
class Error < StandardError; end
|
9
9
|
|
@@ -45,5 +45,5 @@ ActiveSupport.on_load(:active_record) do
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
Exekutor.private_constant
|
48
|
+
Exekutor.private_constant :Internal
|
49
49
|
ActiveSupport.run_load_hooks(:exekutor, Exekutor)
|
@@ -1,18 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
|
+
require "rails/generators"
|
3
4
|
|
4
5
|
module Exekutor
|
6
|
+
# Generates a YAML configuration file
|
5
7
|
class ConfigurationGenerator < Rails::Generators::Base
|
6
|
-
desc
|
8
|
+
desc "Create YAML configuration for Exekutor"
|
7
9
|
|
8
|
-
class_option :identifier, type: :string, aliases: %i
|
10
|
+
class_option :identifier, type: :string, aliases: %i[--id], desc: "The worker identifier"
|
9
11
|
|
12
|
+
# Creates the configuration file at +config/exekutor.yml+. Uses the current worker configuration as the base.
|
10
13
|
def create_configuration_file
|
11
14
|
config = { queues: %w[queues to watch] }.merge(Exekutor.config.worker_options)
|
12
15
|
config[:status_port] = 8765
|
13
16
|
config[:set_db_connection_name] = true
|
14
17
|
config[:wait_for_termination] = 120
|
15
|
-
create_file "config/exekutor#{".#{options[:identifier]}" if options[:identifier]}.yml",
|
18
|
+
create_file "config/exekutor#{".#{options[:identifier]}" if options[:identifier]}.yml",
|
19
|
+
{ "exekutor" => config.stringify_keys }.to_yaml
|
16
20
|
end
|
17
21
|
end
|
18
|
-
end
|
22
|
+
end
|
@@ -1,29 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require
|
2
|
+
|
3
|
+
require "rails/generators"
|
4
|
+
require "rails/generators/active_record"
|
4
5
|
|
5
6
|
module Exekutor
|
7
|
+
# Generates the initializer and migrations
|
6
8
|
class InstallGenerator < Rails::Generators::Base
|
7
9
|
include ActiveRecord::Generators::Migration
|
8
|
-
desc
|
10
|
+
desc "Create migrations for Exekutor"
|
9
11
|
|
10
|
-
TEMPLATE_DIR = File.join(__dir__,
|
12
|
+
TEMPLATE_DIR = File.join(__dir__, "templates/install")
|
11
13
|
source_paths << TEMPLATE_DIR
|
12
14
|
|
15
|
+
# Creates the initializer file at +config/initializers/exekutor.rb+
|
13
16
|
def create_initializer_file
|
14
|
-
template
|
17
|
+
template "initializers/exekutor.rb.erb", "config/initializers/exekutor.rb"
|
15
18
|
end
|
16
19
|
|
20
|
+
# Creates the migration file in the migrations folder
|
17
21
|
def create_migration_file
|
18
|
-
migration_template
|
19
|
-
|
20
|
-
|
21
|
-
copy_file "functions/#{function}.sql", Fx::Definition.new(name: function, version: 1).full_path
|
22
|
-
end
|
23
|
-
%w(notify_workers requeue_orphaned_jobs).each do |trigger|
|
24
|
-
copy_file "triggers/#{trigger}.sql", Fx::Definition.new(name: trigger, version: 1, type: "trigger").full_path
|
25
|
-
end
|
26
|
-
end
|
22
|
+
migration_template "migrations/create_exekutor_schema.rb.erb",
|
23
|
+
File.join(db_migrate_path, "create_exekutor_schema.rb")
|
24
|
+
create_fx_files
|
27
25
|
end
|
28
26
|
|
29
27
|
protected
|
@@ -39,5 +37,18 @@ module Exekutor
|
|
39
37
|
def trigger_sql(name)
|
40
38
|
File.read File.join(TEMPLATE_DIR, "triggers/#{name}.sql")
|
41
39
|
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def create_fx_files
|
44
|
+
return unless defined?(Fx)
|
45
|
+
|
46
|
+
%w[job_notifier requeue_orphaned_jobs].each do |function|
|
47
|
+
copy_file "functions/#{function}.sql", Fx::Definition.new(name: function, version: 1).full_path
|
48
|
+
end
|
49
|
+
%w[notify_workers requeue_orphaned_jobs].each do |trigger|
|
50
|
+
copy_file "triggers/#{trigger}.sql", Fx::Definition.new(name: trigger, version: 1, type: "trigger").full_path
|
51
|
+
end
|
52
|
+
end
|
42
53
|
end
|
43
|
-
end
|
54
|
+
end
|
@@ -7,20 +7,20 @@ class CreateExekutorSchema < ActiveRecord::Migration[<%= migration_version %>]
|
|
7
7
|
|
8
8
|
t.jsonb :info, null: false
|
9
9
|
|
10
|
-
t.datetime :
|
11
|
-
t.datetime :last_heartbeat_at, null: false, default: -> {
|
10
|
+
t.datetime :started_at, null: false, default: -> { "now()" }
|
11
|
+
t.datetime :last_heartbeat_at, null: false, default: -> { "now()" }
|
12
12
|
|
13
|
-
t.column :status, :char, null: false, default:
|
13
|
+
t.column :status, :char, null: false, default: "i"
|
14
14
|
|
15
15
|
t.index [:hostname, :pid], unique: true
|
16
16
|
end
|
17
17
|
|
18
18
|
create_table :exekutor_jobs, id: :uuid do |t|
|
19
19
|
# Worker options
|
20
|
-
t.string :queue, null: false, default:
|
21
|
-
t.integer :priority, null: false, default:
|
22
|
-
t.datetime :enqueued_at, null: false, default: -> {
|
23
|
-
t.datetime :scheduled_at, null: false, default: -> {
|
20
|
+
t.string :queue, null: false, default: "default", limit: 200, index: true
|
21
|
+
t.integer :priority, null: false, default: 16_383, limit: 2
|
22
|
+
t.datetime :enqueued_at, null: false, default: -> { "now()" }
|
23
|
+
t.datetime :scheduled_at, null: false, default: -> { "now()" }
|
24
24
|
|
25
25
|
# Job options
|
26
26
|
t.uuid :active_job_id, null: false, index: true
|
@@ -28,16 +28,17 @@ class CreateExekutorSchema < ActiveRecord::Migration[<%= migration_version %>]
|
|
28
28
|
t.jsonb :options
|
29
29
|
|
30
30
|
# Execution options
|
31
|
-
t.column :status, :char, index: true, null: false, default:
|
31
|
+
t.column :status, :char, index: true, null: false, default: "p"
|
32
32
|
t.float :runtime
|
33
33
|
t.references :worker, type: :uuid, foreign_key: { to_table: :exekutor_workers, on_delete: :nullify }
|
34
34
|
|
35
|
-
t.index [:priority, :scheduled_at, :enqueued_at], where: %
|
35
|
+
t.index [:priority, :scheduled_at, :enqueued_at], where: %q("status"='p'),
|
36
|
+
name: :index_exekutor_jobs_on_dequeue_order
|
36
37
|
end
|
37
38
|
|
38
39
|
create_table :exekutor_job_errors, id: :uuid do |t|
|
39
40
|
t.references :job, type: :uuid, null: false, foreign_key: { to_table: :exekutor_jobs, on_delete: :cascade }
|
40
|
-
t.datetime :created_at, null: false, default: -> {
|
41
|
+
t.datetime :created_at, null: false, default: -> { "now()" }
|
41
42
|
t.jsonb :error, null: false
|
42
43
|
end
|
43
44
|
<% if defined? Fx %>
|
data.tar.gz.sig
CHANGED
Binary file
|