Skip to content

Commit 76a6572

Browse files
authored
Prevent background workers from holding ActiveRecord connections (#1320)
* Use file instead of memory as sqlite database For concurrency testing. * Add failing spec * Improve test cases * Make sure BackgroundWorker returns AR connection * Update changelog
1 parent e8652c6 commit 76a6572

File tree

6 files changed

+83
-4
lines changed

6 files changed

+83
-4
lines changed

sentry-rails/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/doc/
66
/pkg/
77
/spec/reports/
8+
/spec/support/test_rails_app/db
89
/tmp/
910

1011
# rspec failure tracking

sentry-rails/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Unreleased
44

55
- Support performance monitoring on ActiveJob execution [#1304](https://github.com/getsentry/sentry-ruby/pull/1304)
6+
- Prevent background workers from holding ActiveRecord connections [#1320](https://github.com/getsentry/sentry-ruby/pull/1320)
67

78
## 4.2.2
89

sentry-rails/lib/sentry/rails.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require "sentry-ruby"
22
require "sentry/integrable"
3+
require "sentry/rails/background_worker"
34
require "sentry/rails/configuration"
45
require "sentry/rails/engine"
56
require "sentry/rails/railtie"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module Sentry
2+
class BackgroundWorker
3+
alias_method :original_perform, :perform
4+
5+
def perform(&block)
6+
@executor.post do
7+
# make sure the background worker returns AR connection if it accidentally acquire one during serialization
8+
ActiveRecord::Base.connection_pool.with_connection do
9+
block.call
10+
end
11+
end
12+
end
13+
end
14+
end
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
require "spec_helper"
2+
3+
return unless Gem::Version.new(Rails.version) >= Gem::Version.new('5.1.0')
4+
5+
RSpec.describe Sentry::Client, type: :request do
6+
let(:transport) do
7+
Sentry.get_current_client.transport
8+
end
9+
10+
before do
11+
expect(ActiveRecord::Base.connection_pool.stat[:busy]).to eq(1)
12+
end
13+
14+
def send_events
15+
5.times.map do
16+
Thread.new { Sentry::Rails.capture_message("msg") }
17+
end.join
18+
end
19+
20+
context "when serialization triggers ActiveRecord queries" do
21+
before do
22+
make_basic_app do |config|
23+
config.background_worker_threads = 5
24+
# simulate connection being obtained during event serialization
25+
# this could happen when serializing breadcrumbs
26+
config.before_send = lambda do |event, hint|
27+
Post.count
28+
event
29+
end
30+
end
31+
end
32+
33+
it "doesn't hold the ActiveRecord connection after sending the event" do
34+
send_events
35+
36+
sleep(0.5)
37+
38+
expect(transport.events.count).to eq(5)
39+
40+
expect(ActiveRecord::Base.connection_pool.stat[:busy]).to eq(1)
41+
end
42+
end
43+
44+
context "when serialization doesn't trigger ActiveRecord queries" do
45+
before do
46+
make_basic_app do |config|
47+
config.background_worker_threads = 5
48+
end
49+
end
50+
51+
it "doesn't create any extra ActiveRecord connection when sending the event" do
52+
send_events
53+
54+
sleep(0.1)
55+
56+
expect(transport.events.count).to eq(5)
57+
58+
expect(ActiveRecord::Base.connection_pool.stat[:busy]).to eq(1)
59+
end
60+
end
61+
end

sentry-rails/spec/support/test_rails_app/app.rb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010

1111
ActiveSupport::Deprecation.silenced = true
1212

13-
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
13+
# need to init app before establish connection so sqlite can place the database file under the correct project root
14+
class TestApp < Rails::Application
15+
end
16+
17+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: "db")
1418
ActiveRecord::Base.logger = Logger.new(nil)
1519

1620
ActiveRecord::Schema.define do
@@ -30,9 +34,6 @@ class Comment < ActiveRecord::Base
3034
belongs_to :post
3135
end
3236

33-
class TestApp < Rails::Application
34-
end
35-
3637
class PostsController < ActionController::Base
3738
def index
3839
Post.all.to_a

0 commit comments

Comments
 (0)