Fix for Prepared Statement handle not found intermittent exception#648
Fix for Prepared Statement handle not found intermittent exception#648cheenamalhotra merged 18 commits intomicrosoft:devfrom
Conversation
Codecov Report
@@ Coverage Diff @@
## dev #648 +/- ##
============================================
- Coverage 47.96% 47.95% -0.01%
- Complexity 2580 2582 +2
============================================
Files 113 114 +1
Lines 26596 26592 -4
Branches 4454 4455 +1
============================================
- Hits 12756 12752 -4
- Misses 11703 11706 +3
+ Partials 2137 2134 -3
Continue to review full report at Codecov.
|
| cachedPreparedStatementHandle.removeReference(); | ||
| cachedPreparedStatementHandle.setIsExplicitlyDiscarded(); | ||
| } | ||
| resetPrepStmtHandle(); |
There was a problem hiding this comment.
We do need to keep the previous code that does cachedPreparedStatementHandle.removeReference, in fact maybe code should be refactored so that call does resetPrepStmtHandle. If we don't remove the reference it will leak. It is clear that the main problem with the previous code was the call to cachedPreparedStatementHandle.setIsExplicitlyDiscarded, that should not be called here.
There was a problem hiding this comment.
I added call to removeReference back. This needs to be here. Thanks for catching that!
Also I saw another random issue with executeBatch() calls, that failed with Could not find prepared statement with handle x. We were able to reproduce this issue by load testing Statement tests and saw same pattern in all exceptions. After long investigations, I came up with a fix in my recent push.
I made a change to retryBasedOnFailedReuseOfCachedHandle by passing isBatch parameter, which does not throw exception if a batch is being processed. There are intermittent failures if we do not add this, since if a handle is fetched from cache, and needsPrepare=true as a result of type def change, it throws exception while executing batch.
Let me know your thoughts on this. We have not seen any failure with this fix after running tests in multi-threaded environments with load testing.
|
@cheenamalhotra , I took a look at this. Have two questions and a suggestion. Question 1: I am missing something on the Question 2: Why are we adding On the suggestion side I think we can simplify the remove handle ref. logic. This isn't something new in your pull request but given we are starting to have more and more checks on Something along these lines: private boolean resetPrepStmtHandle(boolean discardCurrentCacheItem) {
PreparedStatementHandle handle = cachedPreparedStatementHandle;
boolean statementPoolingUsed = null != handle;
// Return to pool and decrement reference count
if (statementPoolingUsed) {
// Make sure the cached handle does not get re-used more.
if(discardCurrentCacheItem)
handle.setIsExplicitlyDiscarded();
connection.returnCachedPreparedStatementHandle(handle);
}
// Remove references
cachedPreparedStatementHandle = null;
prepStmtHandle = 0;
return statementPoolingUsed;
}Now in // Handle unprepare actions through statement pooling.
if (resetPrepStmtHandle(false)) {
// Done
}
// If no reference to a statement pool cache item is found handle unprepare actions through batching @ connection level.
else ...And in // New type definitions and existing cached handle reference then deregister cached handle.
// If current cache item should be discarded make sure it is not used again.
if(hasNewTypeDefinitions || discardCurrentCacheItem) {
resetPrepStmtHandle(discardCurrentCacheItem);
if(discardCurrentCacheItem)
return false;
}
// Check for new cache reference.Thoughts? |
|
Ping? |
|
Hi Tobias, I was away last week and looking into the changes now. Will update you soon! |
|
Hi @TobiasSQL Sorry it took a while to get this working. Answers to your questions:
I did push changes as suggested by you recently, I could not incorporate the exact method as you mentioned above due to different conditions where Request you to review the PR changes once more, and share your thoughts. |
|
@TobiasSQL Please review changes if you find some time! Thanks, Cheena. |
| HandleAssociation previous = handleMap.put(request, hassoc); | ||
| if (null != previous) { | ||
| ((SQLServerCallableStatement) previous.stmt).handleDBName = previous.databaseName; | ||
| //((SQLServerCallableStatement) previous.stmt).handleDBName = previous.databaseName; |
There was a problem hiding this comment.
let's remove these commented lines.
| } | ||
| cachedPreparedStatementHandle = null; | ||
|
|
||
| // Make sure the cached handle does not get re-used more if it should be discarded |
There was a problem hiding this comment.
Formatting issues. Please apply the formatter to the modified/added lines.
With current implementation of the driver, if a prepared statement is executed multiple times which changes in type definitions, e.g, calling setBigDecimal() with different precision values, results in creating new handles for every changed precision type def. This results in
Could not find prepared statement with handle x. This specifically happens when the handlexcreated (with new type defs) is the same number as the one just discarded (with old type defs). The database instead of creating a new handle looks for previous one.This fix does not discard existing handle in above scenario (solves both issues), but instead calls resetPrepStmtHandle() when new type definitions are found to reset handle for current query and expect new handle from database.