Skip to content

Commit ad38485

Browse files
solnicclaude
andcommitted
test(rails): run distributed_tracing shared example against :solid_queue
Opts the SolidQueue adapter spec into the distributed_tracing meta so all five tracing examples (producer span, trace propagation, messaging span data, user propagation, worker hub isolation) run against :solid_queue alongside the existing :test adapter coverage. Two SolidQueue-specific harness adjustments were needed to make this work on the SQLite-backed test setup: - drain only wraps in travel_to when the caller passes an explicit `at:`. Without this, the inner travel_to(Time.current) collides with the outer travel block in messaging_span_data.rb's latency assertion. - worker_hub_isolation.rb now spawns its threads through a `worker_thread` harness hook (default = `Thread.new`). The :solid_queue spec overrides this hook to allocate an isolated SQLite shard per spawned thread via `connects_to(shards: ...)` + `connected_to(shard:)`. Each worker thread reads/writes its own SolidQueue tables, eliminating the SQLite3::BusyException that two concurrent perform_later/drain pipelines on the shared test DB would otherwise raise. The contract the spec enforces — concurrent jobs in different threads do not cross-pollute Sentry scope — holds the same way it does on :test. Verified GREEN on Rails 6.1 (SQ skipped, :test adapter only), 7.1, and 8.1 via ./bin/test --version. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a157c4f commit ad38485

1 file changed

Lines changed: 60 additions & 1 deletion

File tree

sentry-rails/spec/active_job/solid_queue_spec.rb

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,61 @@
99
include ActiveSupport::Testing::TimeHelpers
1010
include_context "active_job backend harness", adapter: :solid_queue
1111

12+
WORKER_SHARD_COUNT = 4
13+
1214
def boot_adapter(_adapter)
1315
Sentry::Rails::Test::Application.load_queue_schema
16+
17+
install_worker_shards
18+
end
19+
20+
# Sets up `WORKER_SHARD_COUNT` independent SQLite databases as AR
21+
# shards alongside the primary test DB. Each worker thread spawned
22+
# by `worker_thread` claims its own shard, so concurrent perform_later
23+
# / drain calls from different threads never contend on the same
24+
# SQLite file (which would otherwise raise SQLite3::BusyException).
25+
def install_worker_shards
26+
base_dir = Sentry::Rails::Test::Application.root_path.join("db")
27+
worker_paths = (1..WORKER_SHARD_COUNT).map { |i| base_dir.join("queue_worker_#{i}.sqlite3") }
28+
29+
# Wipe any previous run's files so each spec starts fresh.
30+
worker_paths.each { |p| File.unlink(p) if File.exist?(p) }
31+
32+
primary_db = Sentry::Rails::Test::Application.db_path.to_s
33+
configs = { "primary" => { "adapter" => "sqlite3", "database" => primary_db, "timeout" => 5000 } }
34+
worker_paths.each_with_index do |path, i|
35+
configs["worker_#{i + 1}"] = { "adapter" => "sqlite3", "database" => path.to_s, "timeout" => 5000 }
36+
end
37+
38+
ActiveRecord::Base.configurations = { "test" => configs }
39+
40+
shards = { default: { writing: :primary } }
41+
WORKER_SHARD_COUNT.times { |i| shards[:"worker_#{i + 1}"] = { writing: :"worker_#{i + 1}" } }
42+
ActiveRecord::Base.connects_to(shards: shards)
43+
44+
# Load the queue schema into each worker shard so its tables exist.
45+
WORKER_SHARD_COUNT.times do |i|
46+
ActiveRecord::Base.connected_to(shard: :"worker_#{i + 1}") do
47+
load Sentry::Rails::Test::Application.queue_schema_file
48+
end
49+
end
50+
51+
@worker_shard_counter = 0
52+
@worker_shard_mutex = Mutex.new
53+
end
54+
55+
def next_worker_shard
56+
@worker_shard_mutex.synchronize do
57+
@worker_shard_counter = (@worker_shard_counter % WORKER_SHARD_COUNT) + 1
58+
:"worker_#{@worker_shard_counter}"
59+
end
60+
end
61+
62+
def worker_thread(&block)
63+
shard = next_worker_shard
64+
Thread.new do
65+
ActiveRecord::Base.connected_to(shard: shard, &block)
66+
end
1467
end
1568

1669
def reset_adapter(_adapter)
@@ -33,12 +86,18 @@ def drain(at: nil)
3386
name: "spec-#{SecureRandom.hex(4)}"
3487
)
3588

36-
travel_to(at || Time.current) do
89+
run = lambda do
3790
SolidQueue::ScheduledExecution.dispatch_next_batch(100)
3891
SolidQueue::ReadyExecution.claim("*", 100, process.id).each(&:perform)
3992
end
93+
94+
# Only wrap in travel_to when the caller explicitly asks for a future
95+
# time — otherwise nested travel_to (e.g. from a spec that already
96+
# called `travel`) raises.
97+
at ? travel_to(at, &run) : run.call
4098
end
4199

42100
it_behaves_like "a Sentry-instrumented ActiveJob backend"
101+
it_behaves_like "an ActiveJob backend that supports distributed tracing"
43102
end
44103
end

0 commit comments

Comments
 (0)