Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid exception when db-connection is lost #717

Closed
wants to merge 1 commit into from

Conversation

Svelix
Copy link

@Svelix Svelix commented Dec 17, 2019

The tiny_tds lib might return false on execute when the call fails.
This caused exceptions when active? is called after the connection
was lost.

fixes #707

The tiny_tds lib might return false on execute when the call fails.
This caused exceptions when active? is called after the connection
was lost.

fixes rails-sqlserver#707
@@ -281,7 +281,8 @@ def sp_executesql_sql(sql, types, params, name)
def raw_connection_do(sql)
case @connection_options[:mode]
when :dblib
@connection.execute(sql).do
result = @connection.execute(sql)
result == false ? false : result.do

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is essentially the same thing, but

result && result.do

might be more concise.

I did not review the functionality of the change, though.
I am just telling you an idiomatic ruby trick.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's not the same.

result && result.do

will return result if it is any falsy value.
When you look at the tiny_tds lib, you will see that it should either returns false or a result object which then can be called do on it.
In any other case - for example nil - I don't want it to return nil, but cause an exception, so it's easier to figure out what is going wrong.

@@ -152,8 +152,7 @@ def disable_referential_integrity

def active?
return false unless @connection
raw_connection_do 'SELECT 1'
true
raw_connection_do('SELECT 1') == 1

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO the previous one before the change is more robust.
Because the return value is independent of raw_connection_do implementation.

No matter how raw_connection_do changes in the future, as long as still raise expected exceptions.
This function will still work correctly.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with the previous version is that if raw_connection do does not return 1, but for example false, like it currently does when the connection is gone, no exception is raised which caused active? to return true, although the connection is not working.
With this change it will now only return true if the connection actually works, and false if an exception is raised, or anything else causes the connection to not return proper values any more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally I'd like to point out that I did not get rid of the rescue block, so the previous behaviour of returning false if an connection_errors is raised is still the same.

@midnight-wonderer
Copy link
Contributor

midnight-wonderer commented Dec 20, 2019

Could you elaborate more why or how @connection.execute(sql) returns false in the first place? Or how to reproduce the issue.

I am quite interested in this case now. This can potential be a bug of the underlying connection layer.

@Svelix
Copy link
Author

Svelix commented Dec 22, 2019

Could you elaborate more why or how @connection.execute(sql) returns false in the first place? Or how to reproduce the issue.

I am quite interested in this case now. This can potential be a bug of the underlying connection layer.

Its source is in the c-implementaion of the connection adapter: https://github.com/rails-sqlserver/tiny_tds/blob/master/ext/tiny_tds/client.c#L256

The simplest way I found to reproduce this locally is by using a docker-container for the database, open a Rails console and make any db-request, so a connection is opened. Then restart the database container without closing the Rails console and after the database docker container is up again, call the active? method.

@scbabacus-midnight
Copy link

I opened an issue on TinyTds. (rails-sqlserver/tiny_tds#451)

In the meantime, I found out that #active? is part of TinyTds public API.
It used to have a delegation there a347f55
I wonder why it has been removed.

@metaskills
Can we use respond_to? here instead, and delegate active? check to TinyTds?
"ActiveRecord and the connection pool" clearly does not work as intended because of the TinyTds itself.

To summarize the options now are:

  • A) merge this PR
  • B) raise a proper error from TinyTds when the execution failed
  • C) delegate active? to TinyTds

B) and C) are not mutually exclusive, both can be done.

@Svelix
Copy link
Author

Svelix commented Dec 23, 2019

From my experience active? from TinyTds does not return the desired status, but something else. Haven't looked into it in depth, but I remember having tried to use that instead and it didn't work out for me.

@wpolicarpo
Copy link
Member

Hi. I'm jumping into this issue now.

I would prefer to have this fixed at tiny_tds level than the adapter level, but I'm not against this change as a temporary fix for now.

My only questions are

  1. Should we really return false in raw_connection_do if something goes wrong with database connection? Shouldn't we raise an error instead just like tiny_tds would ideally do?
  2. If so, what type of exception should be raised? ActiveRecord::ConnectionTimeoutError? TinyTds::Error?

@Svelix do you mind rebasing your branch so we can check for any failing tests?

wpolicarpo added a commit that referenced this pull request May 15, 2020
TinyTDS returns false instead of raising an exception if connection fails while
executing a query.

Getting around this by raising an exception ourselves while this PR
rails-sqlserver/tiny_tds#469 is not released.

The solution came from #717 and #818, I am just adjusting the error
message to make it the same as that TinyTDS PR.
wpolicarpo added a commit that referenced this pull request May 15, 2020
TinyTDS returns false instead of raising an exception if connection fails while
executing a query.

Getting around this by raising an exception ourselves while this PR
rails-sqlserver/tiny_tds#469 is not released.

The solution came from #717 and #818, I am just adjusting the error
message to make it the same as that TinyTDS PR.
@wpolicarpo
Copy link
Member

Closing this via #818, #825 and rails-sqlserver/tiny_tds/pull/469.

@wpolicarpo wpolicarpo closed this May 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Undefined Method Error Caused By Active?
4 participants