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

LockMode::NONE should not set WITH (NOLOCK) #4400

Merged
merged 1 commit into from Nov 12, 2020

Conversation

BenMorel
Copy link
Contributor

@BenMorel BenMorel commented Nov 1, 2020

Q A
Type bug
BC Break no
Fixed issues #4391

Summary

This fixes the issue detailed in #4391, with SQL Server and SQL Anywhere setting WITH (NOLOCK) for LockMode::NONE, which effectively means using a READ UNCOMMITTED isolation level at table level, which is not the contract of LockMode::NONE.

@BenMorel BenMorel mentioned this pull request Nov 1, 2020
@greg0ire
Copy link
Member

greg0ire commented Nov 2, 2020

Please improve your commit message according to the contributing guide.

@BenMorel
Copy link
Contributor Author

BenMorel commented Nov 2, 2020

@greg0ire Done!

SenseException
SenseException previously approved these changes Nov 3, 2020
Copy link
Member

@SenseException SenseException left a comment

Choose a reason for hiding this comment

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

If this is handled as a bugfix, this looks 👍 after reading a bit more about WITH (NOLOCK) behavior.

@BenMorel
Copy link
Contributor Author

BenMorel commented Nov 8, 2020

Does this PR need approval from someone else?

@greg0ire greg0ire requested a review from morozov November 8, 2020 17:25
@morozov
Copy link
Member

morozov commented Nov 8, 2020

@BenMorel I believe this change cannot be covered with an integration test but it still requires a reproducer that could be run manually across all supported platforms to confirm that the fix is valid. See the testing guidelines (#4356) and #4279 for an example.

@BenMorel
Copy link
Contributor Author

BenMorel commented Nov 8, 2020

@morozov Would it be OK if I add a test that uses 2 separate connections (using TestUtil::getConnection()) to ensure that SQL executed by one uncommitted transaction is not visible by another transaction?

@morozov
Copy link
Member

morozov commented Nov 8, 2020

@BenMorel, absolutely. If the scenario in question can be reproduced using the existing APIs, please add a test for it.

BenMorel added a commit to BenMorel/dbal that referenced this pull request Nov 8, 2020
BenMorel added a commit to BenMorel/dbal that referenced this pull request Nov 8, 2020
BenMorel added a commit to BenMorel/dbal that referenced this pull request Nov 8, 2020
@BenMorel
Copy link
Contributor Author

BenMorel commented Nov 8, 2020

To ensure that we're realling fixing something that's broken, I created a failing test case in #4415; it fails as expected on SQL Server, but does anyone know why it's also failing on Oracle?

Basically, given 2 transactions in READ COMMITTED isolation, T2 is allowed to see a row that's been inserted by T1 but not yet committed. Bug?

BenMorel added a commit to BenMorel/dbal that referenced this pull request Nov 8, 2020
@morozov
Copy link
Member

morozov commented Nov 9, 2020

Bug?

No idea. You can mark this test incomplete on Oracle since it's out of scope.

As far as I understand the code change, it makes the behavior of null and NONE identical. Which raises the question of whether we should support both and whether the call to "append" with "none" makes any sense.

@BenMorel
Copy link
Contributor Author

BenMorel commented Nov 9, 2020

You can mark this test incomplete on Oracle since it's out of scope.

OK, and I'll open a bug to keep track of this, to ensure that we're not overlooking something big here.

As far as I understand the code change, it makes the behavior of null and NONE identical. Which raises the question of whether we should support both and whether the call to "append" with "none" makes any sense.

Yep, that's why I'm advocating to remove support for null, and use LockMode::NONE as the new default. The call to "append" with NONE is fine IMO, as long as the method returns the original string untouched.

@morozov
Copy link
Member

morozov commented Nov 9, 2020

Both points sound good.

@BenMorel
Copy link
Contributor Author

Does anyone have a SQL Server installation at hand?

I'm struggling to create a working test case here. The problem is: by default, in READ COMMITTED isolation, SQL Server blocks all reads on a row that's being updated by another transaction, so my test case blocks indefinitely on the second connection.

I've read here that we can set the following to make SQL Server use row versioning instead of locking:

ALTER DATABASE doctrine SET READ_COMMITTED_SNAPSHOT ON;

This should, in theory, make the query in the second transaction return immediately, but it still blocks. This is contrary to what I can read in the linked article.

I would appreciate a helping hand here!

@morozov
Copy link
Member

morozov commented Nov 11, 2020

Does anyone have a SQL Server installation at hand?

You can spin up a server from the same Docker image as we use on CI:

image: "microsoft/mssql-server-linux:2017-latest"

@morozov
Copy link
Member

morozov commented Nov 12, 2020

@BenMorel please avoid experimentation on CI. Each build job of this PR has locked and timed out after one hour. Given that we run 4 jobs sequentially on AppVeyor, a single build effectively blocked the pipeline for 4 hours.

@BenMorel
Copy link
Contributor Author

@morozov I'm sorry, but I could not repeat the lock locally using the Docker image; at least, not while executing queries manually on the CLI, where it works as expected.

I'll try to do my best to investigate further locally and attempt to not block CI again.

@BenMorel BenMorel force-pushed the with-nolock branch 4 times, most recently from 5f9cbc6 to e9eb3fa Compare November 12, 2020 10:29
BenMorel added a commit to BenMorel/dbal that referenced this pull request Nov 12, 2020
@BenMorel
Copy link
Contributor Author

@morozov, I finally got it to work, with quite a lot of local setup & trial-and-error.

I can't say it's really pretty, but I think it's as pretty as an integration test with 2 connections can be, especially on this quite complex RDBMS.

We now have the same tests failing on SQL server in #4415, and passing here.

Let me know if you need anything else to get this merged! 👍

@BenMorel
Copy link
Contributor Author

@morozov All your feedback has been processed. Please review.

@morozov
Copy link
Member

morozov commented Nov 12, 2020

Looks good! Please squash, and let's get it merged.

This fixes the issue detailed in doctrine#4391, with SQL Server and SQL Anywhere setting WITH (NOLOCK) for LockMode::NONE, which effectively means using a READ UNCOMMITTED isolation level at table level, which is not the contract of LockMode::NONE.
@BenMorel
Copy link
Contributor Author

@morozov Squashed, and I opened #4428 to keep track of #4400 (comment).

@morozov morozov merged commit 74bc147 into doctrine:2.12.x Nov 12, 2020
@morozov
Copy link
Member

morozov commented Nov 12, 2020

Thanks, @BenMorel!

@BenMorel
Copy link
Contributor Author

Thank you, could you please merge 2.12.x into 3.0.x, so that I can take care of #4428?

@morozov
Copy link
Member

morozov commented Nov 13, 2020

@BenMorel I already filed #4429.

@morozov morozov linked an issue Nov 13, 2020 that may be closed by this pull request
rgrellmann added a commit to Rossmann-IT/dbal that referenced this pull request Mar 7, 2021
Release [2.12.1](https://github.com/doctrine/dbal/milestone/84)

2.12.1
======

- Total issues resolved: **2**
- Total pull requests resolved: **11**
- Total contributors: **7**

Documentation,Prepared Statements
---------------------------------

 - [4424: Mark SQLParserUtils internal](doctrine#4424) thanks to @morozov

Packaging
---------

 - [4416: Update .gitattributes](doctrine#4416) thanks to @bytestream

Bug,Cache
---------

 - [4414: ResultCacheStatement::fetchAllAssociative does not store results in cache](doctrine#4414) thanks to @morozov and @dFayet

Deprecation,Prepared Statements
-------------------------------

 - [4411: Deprecate inappropriate usage of prepared statement parameters](doctrine#4411) thanks to @morozov
 - [4407: Deprecate colon prefix for prepared statement parameters](doctrine#4407) thanks to @morozov

Static Analysis
---------------

 - [4403: Remove redundant phpstan param from DriverManager::getConnection()](doctrine#4403) thanks to @simPod

Bug,Locking,Transactions
------------------------

 - [4400: LockMode::NONE should not set WITH (NOLOCK)](doctrine#4400) thanks to @BenMorel

Code Style,PHP
--------------

 - [4398: Update PHP&doctrine#95;CodeSniffer to 3.5.8](doctrine#4398) thanks to @morozov

PDO,PHP,Test Suite
------------------

 - [4396: Fix php8 mysql mariadb](doctrine#4396) thanks to @greg0ire

Documentation
-------------

 - [4390: Fix headline in the upgrade docs](doctrine#4390) thanks to @jdreesen

Documentation,Testing
---------------------

 - [4356: Testing Guidelines](doctrine#4356) thanks to @morozov

# gpg: Signature made Sat Nov 14 21:50:01 2020
# gpg:                using DSA key 1BEDEE0A820BC30D858F9F0C2C3A645671828132
# gpg: Can't check signature: No public key

# Conflicts:
#	README.md
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 26, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Clarify LockMode::NONE
4 participants