exekutor 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/exe/exekutor +2 -2
  4. data/lib/active_job/queue_adapters/exekutor_adapter.rb +2 -1
  5. data/lib/exekutor/asynchronous.rb +143 -75
  6. data/lib/exekutor/cleanup.rb +27 -28
  7. data/lib/exekutor/configuration.rb +48 -25
  8. data/lib/exekutor/hook.rb +15 -11
  9. data/lib/exekutor/info/worker.rb +3 -3
  10. data/lib/exekutor/internal/base_record.rb +2 -1
  11. data/lib/exekutor/internal/callbacks.rb +55 -35
  12. data/lib/exekutor/internal/cli/app.rb +31 -23
  13. data/lib/exekutor/internal/cli/application_loader.rb +17 -6
  14. data/lib/exekutor/internal/cli/cleanup.rb +54 -40
  15. data/lib/exekutor/internal/cli/daemon.rb +9 -11
  16. data/lib/exekutor/internal/cli/default_option_value.rb +3 -1
  17. data/lib/exekutor/internal/cli/info.rb +117 -84
  18. data/lib/exekutor/internal/cli/manager.rb +190 -123
  19. data/lib/exekutor/internal/configuration_builder.rb +40 -27
  20. data/lib/exekutor/internal/database_connection.rb +6 -0
  21. data/lib/exekutor/internal/executable.rb +12 -7
  22. data/lib/exekutor/internal/executor.rb +50 -21
  23. data/lib/exekutor/internal/hooks.rb +11 -8
  24. data/lib/exekutor/internal/listener.rb +66 -39
  25. data/lib/exekutor/internal/logger.rb +28 -10
  26. data/lib/exekutor/internal/provider.rb +93 -74
  27. data/lib/exekutor/internal/reserver.rb +27 -12
  28. data/lib/exekutor/internal/status_server.rb +81 -49
  29. data/lib/exekutor/job.rb +1 -1
  30. data/lib/exekutor/job_error.rb +1 -1
  31. data/lib/exekutor/job_options.rb +22 -13
  32. data/lib/exekutor/plugins/appsignal.rb +7 -5
  33. data/lib/exekutor/plugins.rb +8 -4
  34. data/lib/exekutor/queue.rb +40 -22
  35. data/lib/exekutor/version.rb +1 -1
  36. data/lib/exekutor/worker.rb +88 -47
  37. data/lib/exekutor.rb +2 -2
  38. data/lib/generators/exekutor/configuration_generator.rb +9 -5
  39. data/lib/generators/exekutor/install_generator.rb +26 -15
  40. data/lib/generators/exekutor/templates/install/migrations/create_exekutor_schema.rb.erb +11 -10
  41. data.tar.gz.sig +0 -0
  42. metadata +63 -19
  43. 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['job_class'],
26
+ class: payload["job_class"],
25
27
  method: "perform",
26
28
  params: params,
27
29
  metadata: {
28
- id: payload['job_id'],
29
- queue: payload['queue_name'],
30
- priority: payload.fetch('priority', Exekutor.config.default_queue_priority),
31
- attempts: payload.fetch('attempts', 0)
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
@@ -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
- if File.exist? File.join(__dir__, "plugins/#{name}.rb")
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
@@ -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 "ACTION_NAME"
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? { |job| job.is_a?(ActiveJob::Base) }
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
- scheduled_at = Time.now.to_i
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 = scheduled_at.to_f
66
+ scheduled_at.to_f
53
67
  when Date
54
- scheduled_at = scheduled_at.at_beginning_of_day.to_f
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
- json_serializer = Exekutor.config.load_json_serializer
61
-
62
- Internal::Hooks.run :enqueue, jobs do
63
- if jobs.one?
64
- Exekutor::Job.connection.exec_query <<~SQL, ACTION_NAME, job_sql_binds(jobs.first, scheduled_at, json_serializer), prepare: true
65
- INSERT INTO exekutor_jobs ("queue", "priority", "scheduled_at", "active_job_id", "payload", "options") VALUES ($1, $2, to_timestamp($3), $4, $5, $6) RETURNING id;
66
- SQL
67
- else
68
- insert_statements = jobs.map do |job|
69
- Exekutor::Job.sanitize_sql_for_assignment(
70
- ["(?, ?, to_timestamp(?), ?, ?::jsonb, ?::jsonb)", *job_sql_binds(job, scheduled_at, json_serializer)]
71
- )
72
- end
73
- Exekutor::Job.connection.insert <<~SQL, ACTION_NAME
74
- INSERT INTO exekutor_jobs ("queue", "priority", "scheduled_at", "active_job_id", "payload", "options") VALUES #{insert_statements.join(",")}
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, "The queue name \"#{job.queue_name}\" is too long, the limit is #{Queue::MAX_NAME_LENGTH} characters"
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
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Exekutor
4
4
  # The current version of Exekutor
5
- VERSION = "0.1.0"
5
+ VERSION = "0.1.1"
6
6
  end
@@ -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 stopped
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 before exiting
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 deems it as down
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
- @reserver = Internal::Reserver.new @record.id, config[:queues]
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
- provider_threads = 1
45
- provider_threads += 1 if config.fetch(:enable_listener, true)
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
- listener = Internal::Listener.new worker_id: @record.id, provider: @provider, pool: provider_pool,
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
- set_state :stopped
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: if @executor.running?
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 "Internal"
48
+ Exekutor.private_constant :Internal
49
49
  ActiveSupport.run_load_hooks(:exekutor, Exekutor)
@@ -1,18 +1,22 @@
1
1
  # frozen_string_literal: true
2
- require 'rails/generators'
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 'Create YAML configuration for Exekutor'
8
+ desc "Create YAML configuration for Exekutor"
7
9
 
8
- class_option :identifier, type: :string, aliases: %i(--id), desc: "The worker identifier"
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", { "exekutor" => config.stringify_keys }.to_yaml
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
- require 'rails/generators'
3
- require 'rails/generators/active_record'
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 'Create migrations for Exekutor'
10
+ desc "Create migrations for Exekutor"
9
11
 
10
- TEMPLATE_DIR = File.join(__dir__, 'templates/install')
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 'initializers/exekutor.rb.erb', 'config/initializers/exekutor.rb'
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 'migrations/create_exekutor_schema.rb.erb', File.join(db_migrate_path, 'create_exekutor_schema.rb')
19
- if defined? Fx
20
- %w(job_notifier requeue_orphaned_jobs).each do |function|
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 :created_at, null: false, default: -> { 'now()' }
11
- t.datetime :last_heartbeat_at, null: false, default: -> { 'now()' }
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: 'i'
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: 'default', limit: 200, index: true
21
- t.integer :priority, null: false, default: 16383, limit: 2
22
- t.datetime :enqueued_at, null: false, default: -> { 'now()' }
23
- t.datetime :scheduled_at, null: false, default: -> { 'now()' }
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: 'p'
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: %Q{"status"='p'}, name: :index_exekutor_jobs_on_dequeue_order
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: -> { 'now()' }
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