Skip to content

Fix for Prepared Statement handle not found intermittent exception#648

Merged
cheenamalhotra merged 18 commits intomicrosoft:devfrom
cheenamalhotra:pstmt_caching
May 29, 2018
Merged

Fix for Prepared Statement handle not found intermittent exception#648
cheenamalhotra merged 18 commits intomicrosoft:devfrom
cheenamalhotra:pstmt_caching

Conversation

@cheenamalhotra
Copy link
Copy Markdown
Member

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

  • Increased creation of handles, as handles created for old type definitions are discarded, reuse of already created handles (with old type def) does not happen.
  • Intermittent failures with exception Could not find prepared statement with handle x. This specifically happens when the handle x created (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.

@codecov-io
Copy link
Copy Markdown

codecov-io commented Mar 8, 2018

Codecov Report

Merging #648 into dev will decrease coverage by <.01%.
The diff coverage is 54.28%.

Impacted file tree graph

@@             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
Flag Coverage Δ Complexity Δ
#JDBC42 47.82% <54.28%> (?) 2576 <10> (?)
#JDBC43 47.88% <54.28%> (-0.08%) 2579 <10> (-1)
Impacted Files Coverage Δ Complexity Δ
...soft/sqlserver/jdbc/SQLServerDatabaseMetaData.java 32.26% <ø> (-0.16%) 58 <0> (ø)
...m/microsoft/sqlserver/jdbc/SQLServerStatement.java 59.44% <0%> (-0.1%) 134 <0> (-1)
.../microsoft/sqlserver/jdbc/SQLServerConnection.java 45.65% <100%> (-0.04%) 287 <0> (-1)
...oft/sqlserver/jdbc/SQLServerPreparedStatement.java 50.65% <53.12%> (-0.04%) 160 <10> (+3)
...m/microsoft/sqlserver/jdbc/SQLServerResultSet.java 32.65% <0%> (-0.48%) 240% <0%> (-4%)
...in/java/com/microsoft/sqlserver/jdbc/IOBuffer.java 54.6% <0%> (-0.04%) 0% <0%> (ø)
.../com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java 40% <0%> (ø) 0% <0%> (?)
...rc/main/java/com/microsoft/sqlserver/jdbc/dtv.java 63.29% <0%> (+0.05%) 0% <0%> (ø) ⬇️
...om/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java 52.67% <0%> (+0.19%) 241% <0%> (+2%) ⬆️
... and 6 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4f1b3e1...f713515. Read the comment docs.

cachedPreparedStatementHandle.removeReference();
cachedPreparedStatementHandle.setIsExplicitlyDiscarded();
}
resetPrepStmtHandle();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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.

@TobiasSQL
Copy link
Copy Markdown
Contributor

TobiasSQL commented Mar 16, 2018

@cheenamalhotra , I took a look at this. Have two questions and a suggestion.

Question 1: I am missing something on the isBatch addition you made. If needsPrepare is true we never want to re-try given that the error cannot be due to missing handle, no? What am I missing here?

Question 2: Why are we adding dbName to the source for the statement handle hash? This seems closely akin to trying to interpret the SET-options. Are we sure that dbName will always have an impact here, and that it can be trusted to be accurate?

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 cachedPreparedStatementHandle with regards to clean-up I thought it may be a reasonable thing to do. Basically my suggestion is to make PreparedStatementHandle.removeReference private given it is all handled by SQLServerConnection.returnCachedPreparedStatementHandle and then on the SQLServerPreparedStatement side of the house move all clean-up logic into resetPrepStmtHandle.

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 closePreparedHandle we will simply do this for statement pooling:

           // 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 reuseCachedHandle we can do:

// 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?

@TobiasSQL
Copy link
Copy Markdown
Contributor

Ping?

@cheenamalhotra
Copy link
Copy Markdown
Member Author

Hi Tobias, I was away last week and looking into the changes now. Will update you soon!

@cheenamalhotra
Copy link
Copy Markdown
Member Author

Hi @TobiasSQL

Sorry it took a while to get this working. Answers to your questions:

  • Question 1: Allowing retry if batch is processed by isBatch check.
    So the reason why I added this check was because we were getting some exceptions when batches were being processed with caching enabled. As you know when we discussed Detect changed context is just ... #610 the reason why we wanted to throw exception if needsPrepare is true is in this test case where exec sp_execute or RAISEERROR is called inside SQL query that should go for retry of handle creation. Except that if a batch call is sent internally, with a prepared handle to statement, during executeBatch call, that still qualifies for retrying handle creation. The use case that fails is very complex but I can say allowing this retry does fix all scenarios where exceptions are thrown in batch processing. I also tested different possibilities where this would fail, but till now I haven't discovered anything like that. Please let me know if you find a scenario that would fail with this change.
  • Question 2: This was added to address exception scenarios, by limiting handle sharing between different databases but since we have a better fix of not discarding handles, I tested with removed 'dbName' and everything looks good.

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 resetPrepStmtHandle was called, but most of it I tried to taken in.

Request you to review the PR changes once more, and share your thoughts.

@cheenamalhotra
Copy link
Copy Markdown
Member Author

@TobiasSQL Please review changes if you find some time!

Thanks, Cheena.

@cheenamalhotra cheenamalhotra added this to the 6.5.3 milestone May 15, 2018
HandleAssociation previous = handleMap.put(request, hassoc);
if (null != previous) {
((SQLServerCallableStatement) previous.stmt).handleDBName = previous.databaseName;
//((SQLServerCallableStatement) previous.stmt).handleDBName = previous.databaseName;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

let's remove these commented lines.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done!

AfsanehR-zz
AfsanehR-zz previously approved these changes May 22, 2018
}
cachedPreparedStatementHandle = null;

// Make sure the cached handle does not get re-used more if it should be discarded
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Formatting issues. Please apply the formatter to the modified/added lines.

@cheenamalhotra cheenamalhotra merged commit a409708 into microsoft:dev May 29, 2018
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.

7 participants