Skip to content

Commit 0531f06

Browse files
committed
feat(rails): initial support for Solid Queue
1 parent e747740 commit 0531f06

6 files changed

Lines changed: 218 additions & 7 deletions

File tree

sentry-rails/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
/doc/
66
/pkg/
77
/spec/reports/
8-
/spec/dummy/test_rails_app/db*
8+
/spec/dummy/test_rails_app/**/*.sqlite3*
99
/tmp/
1010

1111
# rspec failure tracking

sentry-rails/Gemfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,16 @@ gem "rails", "~> #{rails_version}"
3232

3333
if rails_version >= Gem::Version.new("8.1.0")
3434
gem "rspec-rails", "~> 8.0.0"
35+
gem "solid_queue"
3536
gem "sqlite3", "~> 2.1.1", platform: :ruby
3637
elsif rails_version >= Gem::Version.new("8.0.0")
3738
gem "rspec-rails", "~> 8.0.0"
39+
gem "solid_queue"
3840
gem "sqlite3", "~> 2.1.1", platform: :ruby
3941
elsif rails_version >= Gem::Version.new("7.1.0")
4042
gem "psych", "~> 4.0.0"
4143
gem "rspec-rails", "~> 7.0"
44+
gem "solid_queue"
4245
gem "sqlite3", "~> 1.7.3", platform: :ruby
4346
elsif rails_version >= Gem::Version.new("6.1.0")
4447
gem "rspec-rails", "~> 6.0"
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# frozen_string_literal: true
2+
3+
require "spec_helper"
4+
5+
if Gem::Version.new(Rails.version) >= Gem::Version.new("7.1")
6+
require "solid_queue"
7+
end
8+
9+
RSpec.describe "Sentry + ActiveJob on SolidQueue", skip: Gem::Version.new(Rails.version) < Gem::Version.new("7.1") do
10+
include ActiveSupport::Testing::TimeHelpers
11+
include_context "active_job backend harness", adapter: :solid_queue
12+
13+
def boot_adapter(_adapter)
14+
Sentry::Rails::Test::Application.load_queue_schema
15+
end
16+
17+
def reset_adapter(_adapter)
18+
[
19+
SolidQueue::ReadyExecution,
20+
SolidQueue::ClaimedExecution,
21+
SolidQueue::FailedExecution,
22+
SolidQueue::BlockedExecution,
23+
SolidQueue::ScheduledExecution,
24+
SolidQueue::RecurringExecution,
25+
SolidQueue::Process,
26+
SolidQueue::Job
27+
].each(&:delete_all)
28+
end
29+
30+
def drain(at: nil)
31+
process = SolidQueue::Process.register(
32+
kind: "Worker",
33+
pid: ::Process.pid,
34+
name: "spec-#{SecureRandom.hex(4)}"
35+
)
36+
37+
travel_to(at || Time.current) do
38+
SolidQueue::ScheduledExecution.dispatch_next_batch(100)
39+
SolidQueue::ReadyExecution.claim("*", 100, process.id).each(&:perform)
40+
end
41+
end
42+
43+
it_behaves_like "a Sentry-instrumented ActiveJob backend"
44+
end

sentry-rails/spec/active_job/support/harness.rb

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,27 @@
1919
teardown_sentry_test
2020
end
2121

22-
def boot_adapter(_adapter)
23-
# Per-adapter setup hook. Backends extend this when they need to load
24-
# schemas, start supervisors, or otherwise prepare the environment.
22+
def boot_adapter(adapter)
23+
case adapter
24+
when :solid_queue
25+
Sentry::Rails::Test::Application.load_queue_schema
26+
end
2527
end
2628

27-
def reset_adapter(_adapter)
28-
# Per-adapter teardown hook. Backends extend this to truncate tables
29-
# or otherwise clean up state between examples.
29+
def reset_adapter(adapter)
30+
case adapter
31+
when :solid_queue
32+
[
33+
SolidQueue::ReadyExecution,
34+
SolidQueue::ClaimedExecution,
35+
SolidQueue::FailedExecution,
36+
SolidQueue::BlockedExecution,
37+
SolidQueue::ScheduledExecution,
38+
SolidQueue::RecurringExecution,
39+
SolidQueue::Process,
40+
SolidQueue::Job
41+
].each(&:delete_all)
42+
end
3043
end
3144

3245
def drain(at: nil)
@@ -42,6 +55,14 @@ def drain(at: nil)
4255
kwargs = at ? { at: at } : {}
4356
perform_enqueued_jobs(**kwargs)
4457
end
58+
when :solid_queue
59+
process = SolidQueue::Process.register(
60+
kind: "Worker",
61+
pid: ::Process.pid,
62+
name: "spec-#{SecureRandom.hex(4)}"
63+
)
64+
65+
SolidQueue::ReadyExecution.claim("*", 100, process.id).each(&:perform)
4566
else
4667
raise NotImplementedError, "active_job backend harness has no drain strategy for adapter: #{adapter.inspect}"
4768
end

sentry-rails/spec/dummy/test_rails_app/config/application.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ def self.schema_file
4545
@schema_file ||= root_path.join("db/schema.rb")
4646
end
4747

48+
def self.queue_schema_file
49+
@queue_schema_file ||= root_path.join("db/queue_schema.rb")
50+
end
51+
4852
def self.db_path
4953
@db_path ||= root_path.join("db", "db.sqlite3")
5054
end
@@ -77,6 +81,14 @@ def self.load_test_schema
7781
end
7882
end
7983

84+
def self.load_queue_schema
85+
@__queue_schema_loaded__ ||= begin
86+
load_test_schema
87+
require Test::Application.queue_schema_file
88+
true
89+
end
90+
end
91+
8092
# Configure method that sets up base configuration
8193
# This can be inherited and extended by subclasses
8294
def configure
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# frozen_string_literal: true
2+
3+
ActiveRecord::Schema[7.1].define(version: 1) do
4+
create_table "solid_queue_blocked_executions", force: :cascade do |t|
5+
t.bigint "job_id", null: false
6+
t.string "queue_name", null: false
7+
t.integer "priority", default: 0, null: false
8+
t.string "concurrency_key", null: false
9+
t.datetime "expires_at", null: false
10+
t.datetime "created_at", null: false
11+
t.index [ "concurrency_key", "priority", "job_id" ], name: "index_solid_queue_blocked_executions_for_release"
12+
t.index [ "expires_at", "concurrency_key" ], name: "index_solid_queue_blocked_executions_for_maintenance"
13+
t.index [ "job_id" ], name: "index_solid_queue_blocked_executions_on_job_id", unique: true
14+
end
15+
16+
create_table "solid_queue_claimed_executions", force: :cascade do |t|
17+
t.bigint "job_id", null: false
18+
t.bigint "process_id"
19+
t.datetime "created_at", null: false
20+
t.index [ "job_id" ], name: "index_solid_queue_claimed_executions_on_job_id", unique: true
21+
t.index [ "process_id", "job_id" ], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id"
22+
end
23+
24+
create_table "solid_queue_failed_executions", force: :cascade do |t|
25+
t.bigint "job_id", null: false
26+
t.text "error"
27+
t.datetime "created_at", null: false
28+
t.index [ "job_id" ], name: "index_solid_queue_failed_executions_on_job_id", unique: true
29+
end
30+
31+
create_table "solid_queue_jobs", force: :cascade do |t|
32+
t.string "queue_name", null: false
33+
t.string "class_name", null: false
34+
t.text "arguments"
35+
t.integer "priority", default: 0, null: false
36+
t.string "active_job_id"
37+
t.datetime "scheduled_at"
38+
t.datetime "finished_at"
39+
t.string "concurrency_key"
40+
t.datetime "created_at", null: false
41+
t.datetime "updated_at", null: false
42+
t.index [ "active_job_id" ], name: "index_solid_queue_jobs_on_active_job_id"
43+
t.index [ "class_name" ], name: "index_solid_queue_jobs_on_class_name"
44+
t.index [ "finished_at" ], name: "index_solid_queue_jobs_on_finished_at"
45+
t.index [ "queue_name", "finished_at" ], name: "index_solid_queue_jobs_for_filtering"
46+
t.index [ "scheduled_at", "finished_at" ], name: "index_solid_queue_jobs_for_alerting"
47+
end
48+
49+
create_table "solid_queue_pauses", force: :cascade do |t|
50+
t.string "queue_name", null: false
51+
t.datetime "created_at", null: false
52+
t.index [ "queue_name" ], name: "index_solid_queue_pauses_on_queue_name", unique: true
53+
end
54+
55+
create_table "solid_queue_processes", force: :cascade do |t|
56+
t.string "kind", null: false
57+
t.datetime "last_heartbeat_at", null: false
58+
t.bigint "supervisor_id"
59+
t.integer "pid", null: false
60+
t.string "hostname"
61+
t.text "metadata"
62+
t.datetime "created_at", null: false
63+
t.string "name", null: false
64+
t.index [ "last_heartbeat_at" ], name: "index_solid_queue_processes_on_last_heartbeat_at"
65+
t.index [ "name", "supervisor_id" ], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true
66+
t.index [ "supervisor_id" ], name: "index_solid_queue_processes_on_supervisor_id"
67+
end
68+
69+
create_table "solid_queue_ready_executions", force: :cascade do |t|
70+
t.bigint "job_id", null: false
71+
t.string "queue_name", null: false
72+
t.integer "priority", default: 0, null: false
73+
t.datetime "created_at", null: false
74+
t.index [ "job_id" ], name: "index_solid_queue_ready_executions_on_job_id", unique: true
75+
t.index [ "priority", "job_id" ], name: "index_solid_queue_poll_all"
76+
t.index [ "queue_name", "priority", "job_id" ], name: "index_solid_queue_poll_by_queue"
77+
end
78+
79+
create_table "solid_queue_recurring_executions", force: :cascade do |t|
80+
t.bigint "job_id", null: false
81+
t.string "task_key", null: false
82+
t.datetime "run_at", null: false
83+
t.datetime "created_at", null: false
84+
t.index [ "job_id" ], name: "index_solid_queue_recurring_executions_on_job_id", unique: true
85+
t.index [ "task_key", "run_at" ], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true
86+
end
87+
88+
create_table "solid_queue_recurring_tasks", force: :cascade do |t|
89+
t.string "key", null: false
90+
t.string "schedule", null: false
91+
t.string "command", limit: 2048
92+
t.string "class_name"
93+
t.text "arguments"
94+
t.string "queue_name"
95+
t.integer "priority", default: 0
96+
t.boolean "static", default: true, null: false
97+
t.text "description"
98+
t.datetime "created_at", null: false
99+
t.datetime "updated_at", null: false
100+
t.index [ "key" ], name: "index_solid_queue_recurring_tasks_on_key", unique: true
101+
t.index [ "static" ], name: "index_solid_queue_recurring_tasks_on_static"
102+
end
103+
104+
create_table "solid_queue_scheduled_executions", force: :cascade do |t|
105+
t.bigint "job_id", null: false
106+
t.string "queue_name", null: false
107+
t.integer "priority", default: 0, null: false
108+
t.datetime "scheduled_at", null: false
109+
t.datetime "created_at", null: false
110+
t.index [ "job_id" ], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true
111+
t.index [ "scheduled_at", "priority", "job_id" ], name: "index_solid_queue_dispatch_all"
112+
end
113+
114+
create_table "solid_queue_semaphores", force: :cascade do |t|
115+
t.string "key", null: false
116+
t.integer "value", default: 1, null: false
117+
t.datetime "expires_at", null: false
118+
t.datetime "created_at", null: false
119+
t.datetime "updated_at", null: false
120+
t.index [ "expires_at" ], name: "index_solid_queue_semaphores_on_expires_at"
121+
t.index [ "key", "value" ], name: "index_solid_queue_semaphores_on_key_and_value"
122+
t.index [ "key" ], name: "index_solid_queue_semaphores_on_key", unique: true
123+
end
124+
125+
add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
126+
add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
127+
add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
128+
add_foreign_key "solid_queue_ready_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
129+
add_foreign_key "solid_queue_recurring_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
130+
add_foreign_key "solid_queue_scheduled_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
131+
end

0 commit comments

Comments
 (0)