diff --git a/.vscode/launch.json b/.vscode/launch.json index 83c20cbc..191b6323 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -18,13 +18,6 @@ "request": "launch", "program": "bin/rails server" }, - { - "name": "Solid Queue", - "type": "ruby_lsp", - "request": "launch", - "program": "bin/rails solid_queue:start", - "preLaunchTask": "Sleepdelay" - }, { "name": "Vite", "type": "node-terminal", @@ -38,8 +31,7 @@ "configurations": [ "Vite", "Rails", - "UI", - "Solid Queue" + "UI" ], "stopAll": true, "presentation": { diff --git a/Procfile b/Procfile index 986b8922..d35bf95a 100644 --- a/Procfile +++ b/Procfile @@ -1,2 +1,3 @@ web: bundle exec puma -C config/puma.rb +worker: bin/jobs release: DB_POOL=2 bundle exec rake db:migrate_if_tables diff --git a/app.json b/app.json index 8d1f4687..2b2bae58 100644 --- a/app.json +++ b/app.json @@ -16,6 +16,9 @@ "formation": { "web": { "quantity": 1 + }, + "worker": { + "quantity": 1 } }, "name": "nerdgeschoss_app", diff --git a/bin/jobs b/bin/jobs new file mode 100755 index 00000000..6a637cfd --- /dev/null +++ b/bin/jobs @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require_relative "../config/environment" +require "solid_queue/cli" + +SolidQueue::Cli.start(ARGV) diff --git a/config/application.rb b/config/application.rb index 6499317c..57190df4 100644 --- a/config/application.rb +++ b/config/application.rb @@ -37,7 +37,6 @@ class Application < Rails::Application config.action_mailer.default_url_options ||= {} config.action_mailer.default_url_options[:host] = host Rails.application.routes.default_url_options[:host] = host - config.active_job.queue_adapter = :solid_queue config.autoload_lib(ignore: ["assets", "tasks"]) config.generators.system_tests = nil diff --git a/config/environments/development.rb b/config/environments/development.rb index 36213d0d..2fd13e87 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -52,6 +52,7 @@ # Highlight code that enqueued background job in logs. config.active_job.verbose_enqueue_logs = true + config.active_job.queue_adapter = :solid_queue # Raises error for missing translations. # config.i18n.raise_on_missing_translations = true diff --git a/config/environments/production.rb b/config/environments/production.rb index d708836a..509d390b 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -52,7 +52,7 @@ config.cache_store = :solid_cache_store # Replace the default in-process and non-durable queuing backend for Active Job. - # config.active_job.queue_adapter = :resque + config.active_job.queue_adapter = :solid_queue # Ignore bad email addresses and do not raise email delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors. diff --git a/config/puma.rb b/config/puma.rb index d40e8eb1..c6dd416c 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -36,7 +36,7 @@ plugin :tmp_restart # Run the Solid Queue supervisor inside of Puma for single-server deployments -plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"] +plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"] || Rails.env.development? # Specify the PID file. Defaults to tmp/pids/server.pid in development. # In other environments, only set the PID file if requested. diff --git a/config/queue.yml b/config/queue.yml new file mode 100644 index 00000000..7efb6e91 --- /dev/null +++ b/config/queue.yml @@ -0,0 +1,18 @@ +default: &default + dispatchers: + - polling_interval: 1 + batch_size: 500 + workers: + - queues: '*' + threads: 3 + processes: <%= ENV.fetch("JOB_CONCURRENCY", 1) %> + polling_interval: 0.1 + +development: + <<: *default + +test: + <<: *default + +production: + <<: *default diff --git a/config/recurring.yml b/config/recurring.yml new file mode 100644 index 00000000..d245e09b --- /dev/null +++ b/config/recurring.yml @@ -0,0 +1,25 @@ +default: &default + harvest_import_job: + class: HarvestImportJob + schedule: '*/30 * * * *' # every 30 minutes + github_import_job: + class: GithubImportJob + schedule: '*/30 * * * *' # every 30 minutes + slack_notification_job: + class: SlackNotificationJob + schedule: '0 8 * * * Europe/Berlin' # every day at 8:00, Berlin time + slack_congratulation_job: + class: SlackCongratulationJob + schedule: '0 9 * * * Europe/Berlin' # every day at 9:00, Berlin time + slack_set_status_job: + class: SlackSetStatusJob + schedule: '0 0 * * * Europe/Berlin' # every day at 00:00, Berlin time + +development: + <<: *default + +test: + <<: *default + +production: + <<: *default diff --git a/config/solid_queue.yml b/config/solid_queue.yml deleted file mode 100644 index f17678dc..00000000 --- a/config/solid_queue.yml +++ /dev/null @@ -1,34 +0,0 @@ -default: &default - dispatchers: - - polling_interval: 1 - batch_size: 500 - recurring_tasks: - harvest_import_job: - class: HarvestImportJob - schedule: "*/30 * * * *" # every 30 minutes - github_import_job: - class: GithubImportJob - schedule: "*/30 * * * *" # every 30 minutes - slack_notification_job: - class: SlackNotificationJob - schedule: "0 8 * * * Europe/Berlin" # every day at 8:00, Berlin time - slack_congratulation_job: - class: SlackCongratulationJob - schedule: "0 9 * * * Europe/Berlin" # every day at 9:00, Berlin time - slack_set_status_job: - class: SlackSetStatusJob - schedule: "0 0 * * * Europe/Berlin" # every day at 00:00, Berlin time - workers: - - queues: "*" - threads: 3 - processes: 1 - polling_interval: 0.1 - -development: - <<: *default - -test: - <<: *default - -production: - <<: *default diff --git a/db/migrate/20250114100959_solid_queue_update.rb b/db/migrate/20250114100959_solid_queue_update.rb new file mode 100644 index 00000000..f5b7d59a --- /dev/null +++ b/db/migrate/20250114100959_solid_queue_update.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +class SolidQueueUpdate < ActiveRecord::Migration[8.0] + def change + create_table "solid_queue_blocked_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.string "queue_name", null: false + t.integer "priority", default: 0, null: false + t.string "concurrency_key", null: false + t.datetime "expires_at", null: false + t.datetime "created_at", null: false + t.index ["concurrency_key", "priority", "job_id"], name: "index_solid_queue_blocked_executions_for_release" + t.index ["expires_at", "concurrency_key"], name: "index_solid_queue_blocked_executions_for_maintenance" + t.index ["job_id"], name: "index_solid_queue_blocked_executions_on_job_id", unique: true + end + + create_table "solid_queue_claimed_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.bigint "process_id" + t.datetime "created_at", null: false + t.index ["job_id"], name: "index_solid_queue_claimed_executions_on_job_id", unique: true + t.index ["process_id", "job_id"], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id" + end + + create_table "solid_queue_failed_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.text "error" + t.datetime "created_at", null: false + t.index ["job_id"], name: "index_solid_queue_failed_executions_on_job_id", unique: true + end + + create_table "solid_queue_jobs", force: :cascade do |t| + t.string "queue_name", null: false + t.string "class_name", null: false + t.text "arguments" + t.integer "priority", default: 0, null: false + t.string "active_job_id" + t.datetime "scheduled_at" + t.datetime "finished_at" + t.string "concurrency_key" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["active_job_id"], name: "index_solid_queue_jobs_on_active_job_id" + t.index ["class_name"], name: "index_solid_queue_jobs_on_class_name" + t.index ["finished_at"], name: "index_solid_queue_jobs_on_finished_at" + t.index ["queue_name", "finished_at"], name: "index_solid_queue_jobs_for_filtering" + t.index ["scheduled_at", "finished_at"], name: "index_solid_queue_jobs_for_alerting" + end + + create_table "solid_queue_pauses", force: :cascade do |t| + t.string "queue_name", null: false + t.datetime "created_at", null: false + t.index ["queue_name"], name: "index_solid_queue_pauses_on_queue_name", unique: true + end + + create_table "solid_queue_processes", force: :cascade do |t| + t.string "kind", null: false + t.datetime "last_heartbeat_at", null: false + t.bigint "supervisor_id" + t.integer "pid", null: false + t.string "hostname" + t.text "metadata" + t.datetime "created_at", null: false + t.string "name", null: false + t.index ["last_heartbeat_at"], name: "index_solid_queue_processes_on_last_heartbeat_at" + t.index ["name", "supervisor_id"], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true + t.index ["supervisor_id"], name: "index_solid_queue_processes_on_supervisor_id" + end + + create_table "solid_queue_ready_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.string "queue_name", null: false + t.integer "priority", default: 0, null: false + t.datetime "created_at", null: false + t.index ["job_id"], name: "index_solid_queue_ready_executions_on_job_id", unique: true + t.index ["priority", "job_id"], name: "index_solid_queue_poll_all" + t.index ["queue_name", "priority", "job_id"], name: "index_solid_queue_poll_by_queue" + end + + create_table "solid_queue_recurring_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.string "task_key", null: false + t.datetime "run_at", null: false + t.datetime "created_at", null: false + t.index ["job_id"], name: "index_solid_queue_recurring_executions_on_job_id", unique: true + t.index ["task_key", "run_at"], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true + end + + create_table "solid_queue_recurring_tasks", force: :cascade do |t| + t.string "key", null: false + t.string "schedule", null: false + t.string "command", limit: 2048 + t.string "class_name" + t.text "arguments" + t.string "queue_name" + t.integer "priority", default: 0 + t.boolean "static", default: true, null: false + t.text "description" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["key"], name: "index_solid_queue_recurring_tasks_on_key", unique: true + t.index ["static"], name: "index_solid_queue_recurring_tasks_on_static" + end + + create_table "solid_queue_scheduled_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.string "queue_name", null: false + t.integer "priority", default: 0, null: false + t.datetime "scheduled_at", null: false + t.datetime "created_at", null: false + t.index ["job_id"], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true + t.index ["scheduled_at", "priority", "job_id"], name: "index_solid_queue_dispatch_all" + end + + create_table "solid_queue_semaphores", force: :cascade do |t| + t.string "key", null: false + t.integer "value", default: 1, null: false + t.datetime "expires_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["expires_at"], name: "index_solid_queue_semaphores_on_expires_at" + t.index ["key", "value"], name: "index_solid_queue_semaphores_on_key_and_value" + t.index ["key"], name: "index_solid_queue_semaphores_on_key", unique: true + end + + add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_ready_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_recurring_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_scheduled_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + end +end diff --git a/db/schema.rb b/db/schema.rb index 1c333cb0..405f652c 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[8.0].define(version: 2024_11_23_125437) do +ActiveRecord::Schema[8.0].define(version: 2025_01_14_100959) do create_schema "heroku_ext" # These are extensions that must be enabled in order to support this database @@ -201,7 +201,9 @@ t.string "hostname" t.text "metadata" t.datetime "created_at", null: false + t.string "name", null: false t.index ["last_heartbeat_at"], name: "index_solid_queue_processes_on_last_heartbeat_at" + t.index ["name", "supervisor_id"], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true t.index ["supervisor_id"], name: "index_solid_queue_processes_on_supervisor_id" end @@ -224,6 +226,22 @@ t.index ["task_key", "run_at"], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true end + create_table "solid_queue_recurring_tasks", force: :cascade do |t| + t.string "key", null: false + t.string "schedule", null: false + t.string "command", limit: 2048 + t.string "class_name" + t.text "arguments" + t.string "queue_name" + t.integer "priority", default: 0 + t.boolean "static", default: true, null: false + t.text "description" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["key"], name: "index_solid_queue_recurring_tasks_on_key", unique: true + t.index ["static"], name: "index_solid_queue_recurring_tasks_on_static" + end + create_table "solid_queue_scheduled_executions", force: :cascade do |t| t.bigint "job_id", null: false t.string "queue_name", null: false