exekutor 0.1.0 → 0.1.1
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/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
|