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

Add logic for transactional commits #2589

Merged

Conversation

alcaeus
Copy link
Member

@alcaeus alcaeus commented Nov 24, 2023

Q A
Type feature
BC Break no
Fixed issues

Summary

This PR contains a commit from #2587, which will be removed when rebasing.

After refactoring commit logic in #2580 to no longer clear changesets and scheduled document actions after executing them, this pull request allows for running all UnitOfWork::commit operations within a multi-document transaction. Note that the feature does not check whether the deployment supports transactions, but it always acts if the configuration setting is enabled.

Included in this PR is a test change that enables transactional commit for all tests if the deployment supports it. This effectively tests transactional commit logic when testing with replica sets (introduced in this PR) or sharded clusters (already present). Standalone deployments continue to test legacy logic.

The commit logic makes use of the convenient transaction API, which takes care of starting the transaction, committing it, and restarting the entire transaction if necessary. The convenient API retries transactions for up to 2 minutes when encountering transient errors, which may be undesirable. As the API does not allow changing this timeout by design, we have to decide whether we want to keep using the convenient API, or rather introduce our own logic for retrying transactions that uses a lower timeout. Note that the 2-minute limit has been set since the introduction of the convenient API, and has not been challenged since. This PR may be a reason to suggest changing the specification to allow for lower timeouts, as a 2-minute timeout makes no sense in a web-based environment where we'd rather show the user a quick error page instead of delaying a page response. Either way, this issue should not come up in practice unless you're dealing with highly contentious parallel workloads, which I'd argue is quite rare.

Documentation for this feature will be introduced in a separate PR.

@alcaeus alcaeus added this to the 2.7.0 milestone Nov 24, 2023
@alcaeus alcaeus self-assigned this Nov 24, 2023
With this commit, all tests using the document manager use transactional flush as long as transactions are supported. Certain tests can use the static $allowsTransactions variable to disable this behaviour.
@alcaeus alcaeus force-pushed the transactions/transactional-commit branch from 9bf352d to 7c9b920 Compare November 27, 2023 12:53
@jmikola
Copy link
Member

jmikola commented Nov 27, 2023

Note that the 2-minute limit has been set since the introduction of the convenient API, and has not been challenged since.

This is where client-side operations timeout comes into play. timeoutMS is intended to supersede the non-configurable, two-minute default for withTransaction(). See CSOT: Convenient Transactions API for more details.

@@ -1562,6 +1563,10 @@ private function getQueryForDocument(object $document): array
*/
private function getWriteOptions(array $options = []): array
{
if ($this->isInTransaction($options)) {
return $options;
Copy link
Member

Choose a reason for hiding this comment

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

Realistically, is writeConcern the only option that would be inherited below? I know specifying per-operation RC/WC options is not permitted, but just want to make sure nothing else is being overlooked from getDefaultCommitOptions() that may be permitted.

If so, this logic should be rewritten so that only the writeConcern gets ignored.

Copy link
Member Author

Choose a reason for hiding this comment

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

Rethinking this, writeConcern is not the only one. Technically speaking, getDefaultCommitOptions may return a host of options that we can't control. Given that, I think it makes most sense to strip transaction options in DocumentPersister as well, after they've been assembled from default commit options, document options, and the specific commit options. This not only ensures that we never pass forbidden options to a write operation within a transaction, but it also future-proofs this code in case other options are eventually added to default commit options or the document commit options.

lib/Doctrine/ODM/MongoDB/UnitOfWork.php Show resolved Hide resolved
lib/Doctrine/ODM/MongoDB/UnitOfWork.php Outdated Show resolved Hide resolved
tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php Outdated Show resolved Hide resolved
tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php Outdated Show resolved Hide resolved
@alcaeus alcaeus requested a review from jmikola November 28, 2023 11:44
Copy link
Member

@jmikola jmikola left a comment

Choose a reason for hiding this comment

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

Suggestion to use "majority" over "2" for the WC test, but LGTM otherwise.

$logger = new CommandLogger();
$logger->register();

$this->uow->commit(['writeConcern' => new WriteConcern(2)]);
Copy link
Member

Choose a reason for hiding this comment

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

"majority" may be safer as that would allow the test to run against a single-member replica set.

@alcaeus
Copy link
Member Author

alcaeus commented Nov 29, 2023

phpstan errors are unrelated and are fixed separately in #2593.

@alcaeus alcaeus merged commit 7c32c01 into doctrine:feature/transactions Nov 29, 2023
19 of 20 checks passed
@alcaeus alcaeus deleted the transactions/transactional-commit branch November 29, 2023 08:59
alcaeus added a commit that referenced this pull request Dec 19, 2023
* Extract commit logic

* Support transactional commit operations

* Always use transactional flush if supported

With this commit, all tests using the document manager use transactional flush as long as transactions are supported. Certain tests can use the static $allowsTransactions variable to disable this behaviour.

* Test with MongoDB 7.0

* Update test names

* Update phpstan baseline

* Fix query selection in shard key tests

* Flip transaction options constant by default

* Use supportsTransaction method when skipping tests

* Apply review feedback to tests

* Add separate test to check write concern in commit options

* Strip write options when in transaction

* Use majority write concern in test
alcaeus added a commit that referenced this pull request Dec 19, 2023
* Extract commit logic

* Support transactional commit operations

* Always use transactional flush if supported

With this commit, all tests using the document manager use transactional flush as long as transactions are supported. Certain tests can use the static $allowsTransactions variable to disable this behaviour.

* Test with MongoDB 7.0

* Update test names

* Update phpstan baseline

* Fix query selection in shard key tests

* Flip transaction options constant by default

* Use supportsTransaction method when skipping tests

* Apply review feedback to tests

* Add separate test to check write concern in commit options

* Strip write options when in transaction

* Use majority write concern in test
alcaeus added a commit that referenced this pull request Jan 8, 2024
* Extract commit logic

* Support transactional commit operations

* Always use transactional flush if supported

With this commit, all tests using the document manager use transactional flush as long as transactions are supported. Certain tests can use the static $allowsTransactions variable to disable this behaviour.

* Test with MongoDB 7.0

* Update test names

* Update phpstan baseline

* Fix query selection in shard key tests

* Flip transaction options constant by default

* Use supportsTransaction method when skipping tests

* Apply review feedback to tests

* Add separate test to check write concern in commit options

* Strip write options when in transaction

* Use majority write concern in test
@alcaeus alcaeus removed this from the 2.7.0 milestone Jan 17, 2024
@alcaeus alcaeus mentioned this pull request Jan 17, 2024
alcaeus added a commit that referenced this pull request Jan 19, 2024
* Run CI workflows on feature branches

* Refactor commit logic (#2580)

* Add tests for commit consistency showing wrong behaviour

* Clear scheduled document changes at the end of a commit operation

* Rename private variables to communicate intent

* Remove obsolete comment

* Use different error code

* Use single mongos in consistency tests

This ensures that the fail points are created on the same server that the write operations take place on, which can't be guaranteed in a sharded cluster with multiple mongoses.

* Rename test methods for clarity

* Explain reasoning for index error

* Extract helper method to create failpoint

* Add configuration setting for transactional flush (#2587)

* Add configuration setting for transactional flush

* Use classic setters for transactional flush setting

* Add logic for transactional commits (#2589)

* Extract commit logic

* Support transactional commit operations

* Always use transactional flush if supported

With this commit, all tests using the document manager use transactional flush as long as transactions are supported. Certain tests can use the static $allowsTransactions variable to disable this behaviour.

* Test with MongoDB 7.0

* Update test names

* Update phpstan baseline

* Fix query selection in shard key tests

* Flip transaction options constant by default

* Use supportsTransaction method when skipping tests

* Apply review feedback to tests

* Add separate test to check write concern in commit options

* Strip write options when in transaction

* Use majority write concern in test

* Update events to play nice with transactions (#2594)

* Pass session and transaction information to event args

* Only dispatch lifecycle events once per commit operation

* Remove isInTransaction property in event args

* Split method signature for readability

* Use property promotion for event args classes

* Extract construction of eventArgs

* Inline spl_object_hash calls

* Avoid injecting test instance

* Add session to commitOptions in persister

* Add session assertions in LifecycleEventManager

* Only retry transaction once (#2604)

* Only retry transaction once

* Rename variable

* Update transaction documentation (#2606)

* Remove references to transactions where not applicable

* Update transaction documentation

* Apply suggestions from code review

Co-authored-by: Jeremy Mikola <jmikola@gmail.com>

* Change title level in event documentation

* Add documentation about transactions to event docs

---------

Co-authored-by: Jeremy Mikola <jmikola@gmail.com>

* Address code review items

* Test highest dependencies on MongoDB 7.0

* Regenerate psalm baseline to silence unrelated errors

---------

Co-authored-by: Jeremy Mikola <jmikola@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants