Skip to content

Commit a347f55

Browse files
committed
Remove all auto recoonect & SQL retry logic.
This was getting too complicated and I believe it stood in the way of passing a few tests along with the strong likelyhood of causing other errors. For example, I found out that using the super strong method of going into single DB user mode caused potential DB lock outs and other grief when used with our connection pool #reset! hook. I would not be surprised to find out that we played a hand in causing users grief in odd to find places when we tried doing to much for them. Lets just allow ActiveRecord and the connection pool to do what it needs and if we are no longer #active? then so be it. 🏃🔥 So passes Autoreconnectathor, son of Notourproblemalion.
1 parent fa593d2 commit a347f55

File tree

7 files changed

+16
-163
lines changed

7 files changed

+16
-163
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
* Removed tests for old issue #164. Handled by core types now.
4242
* The `activity_stats` method. Please put this in a gem if needed.
4343
* We no longger use regular expressions to fix identity inserts. Use ActiveRecord or public ID insert helper.
44+
* All auto reconnect and SQL retry logic. Got too complicated and stood in the way of AR's pool. Speed boost too.
4445

4546
#### Fixed
4647

README.md

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,6 @@ ActiveRecord::Base.table_name_prefix = 'dbo.'
7777
```
7878

7979

80-
#### Auto Connecting
81-
82-
By default the adapter will auto connect to lost DB connections. For every query it will retry at intervals of 2, 4, 8, 16 and 32 seconds. During each retry it will callback out to ActiveRecord::Base.did_retry_sqlserver_connection(connection,count). When all retries fail, it will callback to ActiveRecord::Base.did_lose_sqlserver_connection(connection). Both implementations of these methods are to write to the rails logger, however, they make great override points for notifications like Hoptoad. If you want to disable automatic reconnections use the following in an initializer.
83-
84-
```ruby
85-
ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect = false
86-
```
87-
88-
8980
#### Configure Connection & App Name
9081

9182
We currently conform to an unpublished and non-standard AbstractAdapter interface to configure connections made to the database. To do so, just override the `configure_connection` method in an initializer like so. In this case below we are setting the `TEXTSIZE` to 64 megabytes. Also, TinyTDS supports an application name when it logs into SQL Server. This can be used to identify the connection in SQL Server's activity monitor. By default it will use the `appname` from your database.yml file or a lowercased version of your Rails::Application name. It is now possible to define a `configure_application_name` method that can give you per instance details. Below shows how you might use this to get the process id and thread id of the current connection.

lib/active_record/connection_adapters/sqlserver/database_statements.rb

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,21 @@ def begin_db_transaction
5353
end
5454

5555
def commit_db_transaction
56-
disable_auto_reconnect { do_execute 'COMMIT TRANSACTION' }
56+
do_execute 'COMMIT TRANSACTION'
5757
end
5858

5959
def rollback_db_transaction
60-
do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION'
60+
do_execute 'ROLLBACK TRANSACTION'
6161
end
6262

6363
include Savepoints
6464

6565
def create_savepoint(name = current_savepoint_name)
66-
disable_auto_reconnect { do_execute "SAVE TRANSACTION #{name}" }
66+
do_execute "SAVE TRANSACTION #{name}"
6767
end
6868

6969
def rollback_to_savepoint(name = current_savepoint_name)
70-
disable_auto_reconnect { do_execute "ROLLBACK TRANSACTION #{name}" }
70+
do_execute "ROLLBACK TRANSACTION #{name}"
7171
end
7272

7373
def release_savepoint(name = current_savepoint_name)
@@ -307,9 +307,7 @@ def valid_isolation_levels
307307
# === SQLServer Specific (Executing) ============================ #
308308

309309
def do_execute(sql, name = 'SQL')
310-
log(sql, name) do
311-
with_sqlserver_error_handling { raw_connection_do(sql) }
312-
end
310+
log(sql, name) { raw_connection_do(sql) }
313311
end
314312

315313
def do_exec_query(sql, name, binds, options = {})
@@ -375,13 +373,11 @@ def _raw_select(sql, options = {})
375373
end
376374

377375
def raw_connection_run(sql)
378-
with_sqlserver_error_handling do
379-
case @connection_options[:mode]
380-
when :dblib
381-
@connection.execute(sql)
382-
when :odbc
383-
block_given? ? @connection.run_block(sql) { |handle| yield(handle) } : @connection.run(sql)
384-
end
376+
case @connection_options[:mode]
377+
when :dblib
378+
@connection.execute(sql)
379+
when :odbc
380+
block_given? ? @connection.run_block(sql) { |handle| yield(handle) } : @connection.run(sql)
385381
end
386382
end
387383

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,7 @@
11
module ActiveRecord
22

3-
class LostConnection < WrappedDatabaseException
4-
end
5-
63
class DeadlockVictim < WrappedDatabaseException
74
end
85

9-
module ConnectionAdapters
10-
module SQLServer
11-
module Errors
12-
13-
LOST_CONNECTION_EXCEPTIONS = {
14-
dblib: ['TinyTds::Error'],
15-
odbc: ['ODBC::Error']
16-
}.freeze
17-
18-
LOST_CONNECTION_MESSAGES = {
19-
dblib: [/closed connection/, /dead or not enabled/, /server failed/i],
20-
odbc: [/link failure/, /server failed/, /connection was already closed/, /invalid handle/i]
21-
}.freeze
22-
23-
def lost_connection_exceptions
24-
exceptions = LOST_CONNECTION_EXCEPTIONS[@connection_options[:mode]]
25-
@lost_connection_exceptions ||= exceptions ? exceptions.map { |e| e.constantize rescue nil }.compact : []
26-
end
27-
28-
def lost_connection_messages
29-
LOST_CONNECTION_MESSAGES[@connection_options[:mode]]
30-
end
31-
32-
end
33-
end
34-
end
356

367
end

lib/active_record/connection_adapters/sqlserver_adapter.rb

Lines changed: 5 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,12 @@ class SQLServerAdapter < AbstractAdapter
3030
SQLServer::DatabaseStatements,
3131
SQLServer::Showplan,
3232
SQLServer::SchemaStatements,
33-
SQLServer::DatabaseLimits,
34-
SQLServer::Errors
33+
SQLServer::DatabaseLimits
3534

3635
ADAPTER_NAME = 'SQLServer'.freeze
3736

3837
attr_reader :spid
3938

40-
cattr_accessor :auto_connect, :auto_connect_duration, instance_accessor: false
4139
cattr_accessor :cs_equality_operator, instance_accessor: false
4240
cattr_accessor :lowercase_schema_reflection, :showplan_option
4341

@@ -115,13 +113,9 @@ def disable_referential_integrity
115113

116114
def active?
117115
return false unless @connection
118-
case @connection_options[:mode]
119-
when :dblib
120-
return @connection.active?
121-
end
122-
raw_connection_do('SELECT 1')
116+
raw_connection_do 'SELECT 1'
123117
true
124-
rescue *lost_connection_exceptions
118+
rescue TinyTds::Error, ODBC::Error
125119
false
126120
end
127121

@@ -144,7 +138,8 @@ def disconnect!
144138
end
145139

146140
def reset!
147-
remove_database_connections_and_rollback {}
141+
reset_transaction
142+
do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION'
148143
end
149144

150145
# === Abstract Adapter (Misc Support) =========================== #
@@ -176,14 +171,6 @@ def inspect
176171
"#<#{self.class} version: #{version}, mode: #{@connection_options[:mode]}, azure: #{sqlserver_azure?.inspect}>"
177172
end
178173

179-
def auto_connect
180-
self.class.auto_connect.is_a?(FalseClass) ? false : true
181-
end
182-
183-
def auto_connect_duration
184-
self.class.auto_connect_duration ||= 10
185-
end
186-
187174

188175
protected
189176

@@ -249,8 +236,6 @@ def translate_exception(e, message)
249236
InvalidForeignKey.new(message, e)
250237
when /has been chosen as the deadlock victim/i
251238
DeadlockVictim.new(message, e)
252-
when *lost_connection_messages
253-
LostConnection.new(message, e)
254239
else
255240
super
256241
end
@@ -268,8 +253,6 @@ def connect
268253
end
269254
@spid = _raw_select('SELECT @@SPID', fetch: :rows).first.first
270255
configure_connection
271-
rescue
272-
raise unless @auto_connecting
273256
end
274257

275258
def dblib_connect(config)
@@ -363,40 +346,6 @@ def remove_database_connections_and_rollback(database = nil)
363346
end if block_given?
364347
end
365348

366-
def with_sqlserver_error_handling
367-
yield
368-
rescue Exception => e
369-
case translate_exception(e, e.message)
370-
when LostConnection then retry if auto_reconnected?
371-
end
372-
raise
373-
end
374-
375-
def disable_auto_reconnect
376-
old_auto_connect, self.class.auto_connect = self.class.auto_connect, false
377-
yield
378-
ensure
379-
self.class.auto_connect = old_auto_connect
380-
end
381-
382-
def auto_reconnected?
383-
return false unless auto_connect
384-
@auto_connecting = true
385-
count = 0
386-
while count <= (auto_connect_duration / 2)
387-
disconnect!
388-
reconnect!
389-
ActiveRecord::Base.did_retry_sqlserver_connection(self, count)
390-
return true if active?
391-
sleep 2**count
392-
count += 1
393-
end
394-
ActiveRecord::Base.did_lose_sqlserver_connection(self)
395-
false
396-
ensure
397-
@auto_connecting = false
398-
end
399-
400349
end
401350
end
402351
end

test/cases/connection_test_sqlserver.rb

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -109,53 +109,6 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase
109109
assert connection.active?
110110
end
111111

112-
it 'auto reconnect when setting is on' do
113-
with_auto_connect(true) do
114-
disconnect_raw_connection!
115-
assert_nothing_raised() { Topic.count }
116-
assert connection.active?
117-
end
118-
end
119-
120-
it 'not auto reconnect when setting is off' do
121-
with_auto_connect(false) do
122-
disconnect_raw_connection!
123-
assert_raise(ActiveRecord::LostConnection) { Topic.count }
124-
end
125-
end
126-
127-
it 'not auto reconnect on commit transaction' do
128-
disconnect_raw_connection!
129-
assert_raise(ActiveRecord::LostConnection) { connection.commit_db_transaction }
130-
end
131-
132-
it 'gracefully ignore lost connections on rollback transaction' do
133-
disconnect_raw_connection!
134-
assert_nothing_raised { connection.rollback_db_transaction }
135-
end
136-
137-
describe 'testing #disable_auto_reconnect' do
138-
139-
it 'when auto reconnect setting is on' do
140-
with_auto_connect(true) do
141-
connection.send(:disable_auto_reconnect) do
142-
assert !connection.class.auto_connect
143-
end
144-
assert connection.class.auto_connect
145-
end
146-
end
147-
148-
it 'when auto reconnect setting is off' do
149-
with_auto_connect(false) do
150-
connection.send(:disable_auto_reconnect) do
151-
assert !connection.class.auto_connect
152-
end
153-
assert !connection.class.auto_connect
154-
end
155-
end
156-
157-
end
158-
159112
describe 'with a deadlock victim exception 1205' do
160113

161114
describe 'outside a transaction' do

test/cases/helper_sqlserver.rb

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,6 @@ def connection_mode_dblib? ; self.class.connection_mode_dblib? ; end
2828
def connection_mode_odbc? ; self.class.connection_mode_odbc? ; end
2929
def sqlserver_azure? ; self.class.sqlserver_azure? ; end
3030

31-
def with_auto_connect(boolean)
32-
existing = connection.auto_connect
33-
connection.class.auto_connect = boolean
34-
yield
35-
ensure
36-
connection.class.auto_connect = existing
37-
end
38-
3931
end
4032
end
4133

0 commit comments

Comments
 (0)