diff --git a/db/migrate/20240827075246_create_good_job_settings.rb b/db/migrate/20240827075246_create_good_job_settings.rb new file mode 100644 index 000000000..b8a9b7338 --- /dev/null +++ b/db/migrate/20240827075246_create_good_job_settings.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class CreateGoodJobSettings < ActiveRecord::Migration[7.2] + def change + reversible do |dir| + dir.up do + # Ensure this incremental update migration is idempotent + # with monolithic install migration. + return if connection.table_exists?(:good_job_settings) + end + end + + create_table :good_job_settings, id: :uuid do |t| + t.timestamps + t.text :key + t.jsonb :value + t.index :key, unique: true + end + end +end diff --git a/db/migrate/20240827075247_create_index_good_jobs_jobs_on_priority_created_at_when_unfinished.rb b/db/migrate/20240827075247_create_index_good_jobs_jobs_on_priority_created_at_when_unfinished.rb new file mode 100644 index 000000000..3e518f201 --- /dev/null +++ b/db/migrate/20240827075247_create_index_good_jobs_jobs_on_priority_created_at_when_unfinished.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class CreateIndexGoodJobsJobsOnPriorityCreatedAtWhenUnfinished < ActiveRecord::Migration[7.2] + disable_ddl_transaction! + + def change + reversible do |dir| + dir.up do + # Ensure this incremental update migration is idempotent + # with monolithic install migration. + return if connection.index_name_exists?(:good_jobs, :index_good_jobs_jobs_on_priority_created_at_when_unfinished) + end + end + + add_index :good_jobs, [:priority, :created_at], order: { priority: "DESC NULLS LAST", created_at: :asc }, + where: "finished_at IS NULL", name: :index_good_jobs_jobs_on_priority_created_at_when_unfinished, + algorithm: :concurrently + end +end diff --git a/db/migrate/20240827075248_create_good_job_batches.rb b/db/migrate/20240827075248_create_good_job_batches.rb new file mode 100644 index 000000000..f61f575c8 --- /dev/null +++ b/db/migrate/20240827075248_create_good_job_batches.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +class CreateGoodJobBatches < ActiveRecord::Migration[7.2] + def change + reversible do |dir| + dir.up do + # Ensure this incremental update migration is idempotent + # with monolithic install migration. + return if connection.table_exists?(:good_job_batches) + end + end + + create_table :good_job_batches, id: :uuid do |t| + t.timestamps + t.text :description + t.jsonb :serialized_properties + t.text :on_finish + t.text :on_success + t.text :on_discard + t.text :callback_queue_name + t.integer :callback_priority + t.datetime :enqueued_at + t.datetime :discarded_at + t.datetime :finished_at + end + + change_table :good_jobs do |t| + t.uuid :batch_id + t.uuid :batch_callback_id + + t.index :batch_id, where: "batch_id IS NOT NULL" + t.index :batch_callback_id, where: "batch_callback_id IS NOT NULL" + end + end +end diff --git a/db/migrate/20240827075249_create_good_job_executions.rb b/db/migrate/20240827075249_create_good_job_executions.rb new file mode 100644 index 000000000..08d13f4ac --- /dev/null +++ b/db/migrate/20240827075249_create_good_job_executions.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +class CreateGoodJobExecutions < ActiveRecord::Migration[7.2] + def change + reversible do |dir| + dir.up do + # Ensure this incremental update migration is idempotent + # with monolithic install migration. + return if connection.table_exists?(:good_job_executions) + end + end + + create_table :good_job_executions, id: :uuid do |t| + t.timestamps + + t.uuid :active_job_id, null: false + t.text :job_class + t.text :queue_name + t.jsonb :serialized_params + t.datetime :scheduled_at + t.datetime :finished_at + t.text :error + + t.index [:active_job_id, :created_at], name: :index_good_job_executions_on_active_job_id_and_created_at + end + + change_table :good_jobs do |t| + t.boolean :is_discrete + t.integer :executions_count + t.text :job_class + end + end +end diff --git a/db/migrate/20240827075250_create_good_jobs_error_event.rb b/db/migrate/20240827075250_create_good_jobs_error_event.rb new file mode 100644 index 000000000..366ab7286 --- /dev/null +++ b/db/migrate/20240827075250_create_good_jobs_error_event.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class CreateGoodJobsErrorEvent < ActiveRecord::Migration[7.2] + def change + reversible do |dir| + dir.up do + # Ensure this incremental update migration is idempotent + # with monolithic install migration. + return if connection.column_exists?(:good_jobs, :error_event) + end + end + + add_column :good_jobs, :error_event, :integer, limit: 2 + add_column :good_job_executions, :error_event, :integer, limit: 2 + end +end diff --git a/db/migrate/20240827075251_recreate_good_job_cron_indexes_with_conditional.rb b/db/migrate/20240827075251_recreate_good_job_cron_indexes_with_conditional.rb new file mode 100644 index 000000000..c7b9d2e7a --- /dev/null +++ b/db/migrate/20240827075251_recreate_good_job_cron_indexes_with_conditional.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +class RecreateGoodJobCronIndexesWithConditional < ActiveRecord::Migration[7.2] + disable_ddl_transaction! + + def change + reversible do |dir| + dir.up do + unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_created_at_cond) + add_index :good_jobs, [:cron_key, :created_at], where: "(cron_key IS NOT NULL)", + name: :index_good_jobs_on_cron_key_and_created_at_cond, algorithm: :concurrently + end + unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_cron_at_cond) + add_index :good_jobs, [:cron_key, :cron_at], where: "(cron_key IS NOT NULL)", unique: true, + name: :index_good_jobs_on_cron_key_and_cron_at_cond, algorithm: :concurrently + end + + if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_created_at) + remove_index :good_jobs, name: :index_good_jobs_on_cron_key_and_created_at + end + if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_cron_at) + remove_index :good_jobs, name: :index_good_jobs_on_cron_key_and_cron_at + end + end + + dir.down do + unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_created_at) + add_index :good_jobs, [:cron_key, :created_at], + name: :index_good_jobs_on_cron_key_and_created_at, algorithm: :concurrently + end + unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_cron_at) + add_index :good_jobs, [:cron_key, :cron_at], unique: true, + name: :index_good_jobs_on_cron_key_and_cron_at, algorithm: :concurrently + end + + if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_created_at_cond) + remove_index :good_jobs, name: :index_good_jobs_on_cron_key_and_created_at_cond + end + if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_cron_at_cond) + remove_index :good_jobs, name: :index_good_jobs_on_cron_key_and_cron_at_cond + end + end + end + end +end diff --git a/db/migrate/20240827075252_create_good_job_labels.rb b/db/migrate/20240827075252_create_good_job_labels.rb new file mode 100644 index 000000000..0a6b5b33b --- /dev/null +++ b/db/migrate/20240827075252_create_good_job_labels.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class CreateGoodJobLabels < ActiveRecord::Migration[7.2] + def change + reversible do |dir| + dir.up do + # Ensure this incremental update migration is idempotent + # with monolithic install migration. + return if connection.column_exists?(:good_jobs, :labels) + end + end + + add_column :good_jobs, :labels, :text, array: true + end +end diff --git a/db/migrate/20240827075253_create_good_job_labels_index.rb b/db/migrate/20240827075253_create_good_job_labels_index.rb new file mode 100644 index 000000000..bb30e501f --- /dev/null +++ b/db/migrate/20240827075253_create_good_job_labels_index.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class CreateGoodJobLabelsIndex < ActiveRecord::Migration[7.2] + disable_ddl_transaction! + + def change + reversible do |dir| + dir.up do + unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_labels) + add_index :good_jobs, :labels, using: :gin, where: "(labels IS NOT NULL)", + name: :index_good_jobs_on_labels, algorithm: :concurrently + end + end + + dir.down do + if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_labels) + remove_index :good_jobs, name: :index_good_jobs_on_labels + end + end + end + end +end diff --git a/db/migrate/20240827075254_remove_good_job_active_id_index.rb b/db/migrate/20240827075254_remove_good_job_active_id_index.rb new file mode 100644 index 000000000..f7c48486d --- /dev/null +++ b/db/migrate/20240827075254_remove_good_job_active_id_index.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class RemoveGoodJobActiveIdIndex < ActiveRecord::Migration[7.2] + disable_ddl_transaction! + + def change + reversible do |dir| + dir.up do + if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_active_job_id) + remove_index :good_jobs, name: :index_good_jobs_on_active_job_id + end + end + + dir.down do + unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_active_job_id) + add_index :good_jobs, :active_job_id, name: :index_good_jobs_on_active_job_id + end + end + end + end +end diff --git a/db/migrate/20240827075255_create_index_good_job_jobs_for_candidate_lookup.rb b/db/migrate/20240827075255_create_index_good_job_jobs_for_candidate_lookup.rb new file mode 100644 index 000000000..cf7d4225e --- /dev/null +++ b/db/migrate/20240827075255_create_index_good_job_jobs_for_candidate_lookup.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class CreateIndexGoodJobJobsForCandidateLookup < ActiveRecord::Migration[7.2] + disable_ddl_transaction! + + def change + reversible do |dir| + dir.up do + # Ensure this incremental update migration is idempotent + # with monolithic install migration. + return if connection.index_name_exists?(:good_jobs, :index_good_job_jobs_for_candidate_lookup) + end + end + + add_index :good_jobs, [:priority, :created_at], order: { priority: "ASC NULLS LAST", created_at: :asc }, + where: "finished_at IS NULL", name: :index_good_job_jobs_for_candidate_lookup, + algorithm: :concurrently + end +end diff --git a/db/migrate/20240827075256_create_good_job_execution_error_backtrace.rb b/db/migrate/20240827075256_create_good_job_execution_error_backtrace.rb new file mode 100644 index 000000000..c6d3f64c7 --- /dev/null +++ b/db/migrate/20240827075256_create_good_job_execution_error_backtrace.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class CreateGoodJobExecutionErrorBacktrace < ActiveRecord::Migration[7.2] + def change + reversible do |dir| + dir.up do + # Ensure this incremental update migration is idempotent + # with monolithic install migration. + return if connection.column_exists?(:good_job_executions, :error_backtrace) + end + end + + add_column :good_job_executions, :error_backtrace, :text, array: true + end +end diff --git a/db/migrate/20240827075257_create_good_job_process_lock_ids.rb b/db/migrate/20240827075257_create_good_job_process_lock_ids.rb new file mode 100644 index 000000000..94b8b674e --- /dev/null +++ b/db/migrate/20240827075257_create_good_job_process_lock_ids.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class CreateGoodJobProcessLockIds < ActiveRecord::Migration[7.2] + def change + reversible do |dir| + dir.up do + # Ensure this incremental update migration is idempotent + # with monolithic install migration. + return if connection.column_exists?(:good_jobs, :locked_by_id) + end + end + + add_column :good_jobs, :locked_by_id, :uuid + add_column :good_jobs, :locked_at, :datetime + add_column :good_job_executions, :process_id, :uuid + add_column :good_job_processes, :lock_type, :integer, limit: 2 + end +end diff --git a/db/migrate/20240827075258_create_good_job_process_lock_indexes.rb b/db/migrate/20240827075258_create_good_job_process_lock_indexes.rb new file mode 100644 index 000000000..11b045385 --- /dev/null +++ b/db/migrate/20240827075258_create_good_job_process_lock_indexes.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +class CreateGoodJobProcessLockIndexes < ActiveRecord::Migration[7.2] + disable_ddl_transaction! + + def change + reversible do |dir| + dir.up do + unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_priority_scheduled_at_unfinished_unlocked) + add_index :good_jobs, [:priority, :scheduled_at], + order: { priority: "ASC NULLS LAST", scheduled_at: :asc }, + where: "finished_at IS NULL AND locked_by_id IS NULL", + name: :index_good_jobs_on_priority_scheduled_at_unfinished_unlocked, + algorithm: :concurrently + end + + unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_locked_by_id) + add_index :good_jobs, :locked_by_id, + where: "locked_by_id IS NOT NULL", + name: :index_good_jobs_on_locked_by_id, + algorithm: :concurrently + end + + unless connection.index_name_exists?(:good_job_executions, :index_good_job_executions_on_process_id_and_created_at) + add_index :good_job_executions, [:process_id, :created_at], + name: :index_good_job_executions_on_process_id_and_created_at, + algorithm: :concurrently + end + end + + dir.down do + remove_index(:good_jobs, name: :index_good_jobs_on_priority_scheduled_at_unfinished_unlocked) if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_priority_scheduled_at_unfinished_unlocked) + remove_index(:good_jobs, name: :index_good_jobs_on_locked_by_id) if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_locked_by_id) + remove_index(:good_job_executions, name: :index_good_job_executions_on_process_id_and_created_at) if connection.index_name_exists?(:good_job_executions, :index_good_job_executions_on_process_id_and_created_at) + end + end + end +end diff --git a/db/migrate/20240827075259_create_good_job_execution_duration.rb b/db/migrate/20240827075259_create_good_job_execution_duration.rb new file mode 100644 index 000000000..4b17b33dd --- /dev/null +++ b/db/migrate/20240827075259_create_good_job_execution_duration.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class CreateGoodJobExecutionDuration < ActiveRecord::Migration[7.2] + def change + reversible do |dir| + dir.up do + # Ensure this incremental update migration is idempotent + # with monolithic install migration. + return if connection.column_exists?(:good_job_executions, :duration) + end + end + + add_column :good_job_executions, :duration, :interval + end +end diff --git a/db/schema.rb b/db/schema.rb index 0f9c5aaf8..603940117 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2023_12_14_133032) do +ActiveRecord::Schema[7.2].define(version: 2024_08_27_075259) do # These are extensions that must be enabled in order to support this database enable_extension "btree_gin" enable_extension "pgcrypto" @@ -63,13 +63,18 @@ t.datetime "finished_at" t.text "error" t.integer "error_event", limit: 2 + t.text "error_backtrace", array: true + t.uuid "process_id" + t.interval "duration" t.index ["active_job_id", "created_at"], name: "index_good_job_executions_on_active_job_id_and_created_at" + t.index ["process_id", "created_at"], name: "index_good_job_executions_on_process_id_and_created_at" end create_table "good_job_processes", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| t.datetime "created_at", null: false t.datetime "updated_at", null: false t.jsonb "state" + t.integer "lock_type", limit: 2 end create_table "good_job_settings", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| @@ -101,15 +106,21 @@ t.integer "executions_count" t.text "job_class" t.integer "error_event", limit: 2 + t.text "labels", array: true + t.uuid "locked_by_id" + t.datetime "locked_at" t.index ["active_job_id", "created_at"], name: "index_good_jobs_on_active_job_id_and_created_at" - t.index ["active_job_id"], name: "index_good_jobs_on_active_job_id" t.index ["batch_callback_id"], name: "index_good_jobs_on_batch_callback_id", where: "(batch_callback_id IS NOT NULL)" t.index ["batch_id"], name: "index_good_jobs_on_batch_id", where: "(batch_id IS NOT NULL)" t.index ["concurrency_key"], name: "index_good_jobs_on_concurrency_key_when_unfinished", where: "(finished_at IS NULL)" - t.index ["cron_key", "created_at"], name: "index_good_jobs_on_cron_key_and_created_at" - t.index ["cron_key", "cron_at"], name: "index_good_jobs_on_cron_key_and_cron_at", unique: true + t.index ["cron_key", "created_at"], name: "index_good_jobs_on_cron_key_and_created_at_cond", where: "(cron_key IS NOT NULL)" + t.index ["cron_key", "cron_at"], name: "index_good_jobs_on_cron_key_and_cron_at_cond", unique: true, where: "(cron_key IS NOT NULL)" t.index ["finished_at"], name: "index_good_jobs_jobs_on_finished_at", where: "((retried_good_job_id IS NULL) AND (finished_at IS NOT NULL))" + t.index ["labels"], name: "index_good_jobs_on_labels", where: "(labels IS NOT NULL)", using: :gin + t.index ["locked_by_id"], name: "index_good_jobs_on_locked_by_id", where: "(locked_by_id IS NOT NULL)" + t.index ["priority", "created_at"], name: "index_good_job_jobs_for_candidate_lookup", where: "(finished_at IS NULL)" t.index ["priority", "created_at"], name: "index_good_jobs_jobs_on_priority_created_at_when_unfinished", order: { priority: "DESC NULLS LAST" }, where: "(finished_at IS NULL)" + t.index ["priority", "scheduled_at"], name: "index_good_jobs_on_priority_scheduled_at_unfinished_unlocked", where: "((finished_at IS NULL) AND (locked_by_id IS NULL))" t.index ["queue_name", "scheduled_at"], name: "index_good_jobs_on_queue_name_and_scheduled_at", where: "(finished_at IS NULL)" t.index ["scheduled_at"], name: "index_good_jobs_on_scheduled_at", where: "(finished_at IS NULL)" end