odbc icon indicating copy to clipboard operation
odbc copied to clipboard

Improve interrupt behavior

Open jimhester opened this issue 4 years ago • 12 comments

Right now we check for interrupts on the R side, but don't attempt to do anything to signal to the database that an interrupt occurred.

Either calling SQLCancel() on the statement, or SQLFreeStmt() should do it, though we will need to test to make sure.

If that doesn't work then we may have to switch to using async queries, though I would prefer to avoid that complexity if possible.

jimhester avatar Jan 25 '21 13:01 jimhester

Hey Jim -

Isn't SQLCancel called by nanodbc when some of the resources are destroyed?

If so, I wonder if we can get there by looking into opportunities to release if interrupted.

detule avatar Jan 25 '21 15:01 detule

yeah, I think that would likely work. Rcpp throws a Rcpp::internal::InterruptedException if R is interrupted from Rcpp::checkUserInterrupt(). So if we put a try / catch block around https://github.com/r-dbi/odbc/blob/731b46f00afc654868b61df910a631dabe6b59ef/src/odbc_result.cpp#L158 , release the statement if interrupted and the re-throw the exception I think that might work.

jimhester avatar Jan 25 '21 15:01 jimhester

I don't think the problem is with retrieving the results; it's submitting the query.

hadley avatar Feb 01 '21 15:02 hadley

That makes sense; I think the checkUserInterrupt call that Jim referenced above is also at the point where the query is being submitted for execution, prior to any fetching. He is probably right here, catching the Rcpp exception and making sure we clean up any nanodbc resources may be a good first pass.

Should we try it? Is there a way to test if that helps with the issue. Happy to submit the patch.

detule avatar Feb 01 '21 15:02 detule

@isteves do you happen to have a reprex for a slow query that we could use for testing?

hadley avatar Feb 01 '21 16:02 hadley

Oh here's a good one from @krlmlr: https://github.com/rstudio/rstudio/issues/8865#issue-798588592

hadley avatar Feb 01 '21 19:02 hadley

Hey @hadley, @jimhester:

I don't think anything we do here, short of real async, can make the query in that example interruptible, right ? (just making sure that's not the expectation ) At any rate, in terms of improving interrupt handling, with that example I get:

> dbGetQuery(conn, make_query(0:21))
^C^C
> dbGetQuery(conn, "SELECT GETDATE()")
1 2021-02-02 01:18:44
Warning message:
In new_result(connection@ptr, statement, immediate) :
  Cancelling previous query

I think we can make it so that we release the resources when the interrupt is caught (rather than on subsequent query). Does that sound reasonable?

detule avatar Feb 02 '21 01:02 detule

@detule, that sounds good to me.

@isteves were you using odbc when you had your interruption issues or some other connection package?

jimhester avatar Feb 02 '21 13:02 jimhester

@jimhester in the past I used DBI+RPostgreSQL and now I use odbc. I had issues with both though haven't checked the former in a few months now.

isteves avatar Feb 02 '21 13:02 isteves

@detule just to be clear — what you're saying is that query will continue to run on the database, but control will immediately return to R? How much work would a real async approach be?

hadley avatar Feb 02 '21 13:02 hadley

@hadley - No, I think the goal of wrapping the existing checkUserInterrupt calls (and/or introducing a few more) in try-catch blocks, would be to make sure that we reset the SQL connection in a usable state when control is returned from the I/O call, not immediately. [This is an improvement relative to the current state where no resource cleanup happens when a user interrupt is caught]

I think for control to immediately return to [R] some form of actual async is needed. For that, ODBC has some facilities for asynchronous execution, and these are exposed via nanodbc. But I mean there are so many issues with various drivers when it comes to vanilla execution, let alone async.

Alternatively, one could go the C++ route, i think at this point C++11 is required to build package:odbc. Something like this could work, but of course devil may be in the details, especially when it comes to exception handling in a multi-threaded context!

detule avatar Feb 03 '21 02:02 detule

@detule that sounds like good a thing to do, but it's not the motivation behind this issue (so I don't want it to get lost).

hadley avatar Feb 03 '21 12:02 hadley