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.
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