Skip to content
This repository was archived by the owner on Dec 22, 2021. It is now read-only.

Conversation

@TBSliver
Copy link
Contributor

First pass of Transaction Support for MongoDB 4.0

Currently the test (t/transactions-spec.t) has several TODO parts in the test - the majority of these require PERL-918 ticket to be completed, however they should all start working after that point.

The run-command test run command fails with explicit secondary read preference also has a TODO on it. The test yml looks like this:

  - description: run command fails with explicit secondary read preference

    operations:
      - name: startTransaction
        object: session0
      - name: runCommand
        object: database
        command_name: find
        arguments:
          session: session0
          command:
            find: *collection_name
          readPreference:
            mode: Secondary
        result:
          errorContains: read preference in a transaction must be primary

the issue here is, the spec specifically states The transaction's read preference MUST override all other user configurable read preferences. This is obvious for a client, database, or collection level read preference configuration (and is how it is normally inherited), and is arguable that an explicit run_command with a read preference would also count as user configurable. As well as that, there is no way to determine at the op level, without adding a large amount of extra logic, that the read preference was set explicitly instead of inherited - and this seems to also be the only case or way for a read preference to be set outside of a normal config option.

Either way this is now passed forward for initial code review, pending any changes to the spec in the meantime.

Copy link
Contributor

@xdg xdg left a comment

Choose a reason for hiding this comment

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

I haven't gone in detail through the test file, but there's enough for you to start working on changes while I get to that. :-)

Also, please note that server selection is suppose to attach TransientTransactionError or UnknownTransactionCommitResult labels, so you'll need to wrap selection and have some logic to possibly put one or the other error on. It's a little janky, but I think you could toggle the label type depending on the "force" option to send_retryable -- as only commit and abort get forced so those you can set the UnknownTransactionCommitResult label (and then abort throws away any errors).

# applied after to not override the clone with the original
defaultTransactionOptions => {
defined( $_[0] )
&& ref( $_[0] ) eq 'HASH'
Copy link
Contributor

Choose a reason for hiding this comment

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

These guards are necessary for line 96, too, so maybe move up for the whole sub?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah oops thought isa fired before coerce, knew it was one way or the other... heh

%{ $_[0] }
%{ $_[0] },
# applied after to not override the clone with the original
defaultTransactionOptions => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this would be clearer if you cloned the default transaction options in a separate operation rather than nesting it in with the other cloning.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, have done that before building this hash, along with sorting out the guards mentioned below

has _current_transaction_settings => (
is => 'rwp',
isa => HashRef,
init_arg => undef,
Copy link
Contributor

Choose a reason for hiding this comment

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

needs 'init_arg'?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

True, as well as the clearer which I think I was using earlier but became redundant because we just set it completely later.

);

# Flag used to say we are still in a transaction
has _active_transaction => (
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this not redundant with _transaction_state? Can it just be a method that computes a boolean based on _transaction_state?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The problem is that theres no easy logic for whether we are in an active transaction - the state must be changed for committed or aborted before the command is sent (so that on error we are in the correct state), but if we are in one of those states and not in a transaction we need to reset the session state back to none - which would mean needing a flag notifying if we are in an actual commit or abort command - and the active transaction flag seems a lot more useful and can be set correctly in the right spots. Also means that setting the transaction state back to 'none' is an easy logic check!

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok. Keep it, but I'll look closer after the GA release and see what can be done about it.


# Flag used to say whether any operations have been performed on the
# transaction
has _has_transaction_operations => (
Copy link
Contributor

Choose a reason for hiding this comment

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

is this not also something we can derive from _transaction_state?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not fully - the biggest problem is being able to call commit multiple times. Its easy if you are coming from starting or in_progress states, but after that you are in the committed state - at which point, its unknown what state you were in before. I thought about tracking previous state as well, but if you then call commit more than twice (which is allowed AFAICT) it would break - so find just setting a boolean more useful.

Copy link
Contributor

Choose a reason for hiding this comment

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

ok.

UnknownReplWriteConcern
NoSuchTransaction
/ ) {
push @{ $err->error_labels }, 'UnknownTransactionCommitResult';
Copy link
Contributor

Choose a reason for hiding this comment

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

This label also needs to be applied to server selection errors, network errors, network timeout errors, and retryable write errors.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, those are the only occurances for it not being applied - in the latest version have had to put int he actual error code numbers in as some of the spec tests dont actually set the codeName... (error-labels.json in the last two omit tests)

my $server_version = server_version($conn);
my $server_type = server_type($conn);

plan skip_all => "Requires MongoDB 4.0"
Copy link
Contributor

Choose a reason for hiding this comment

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

For the 3.6 tests, I added a test helper function for "skip_unless_sessions". Please make a similar one for "skip_unless _transactions" here.

);

my $dir = path("t/data/transactions");
my $iterator = $dir->iterator; my $index = 0; # TBSLIVER
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't forget to remove this


subtest $path => sub {

for my $test ( @{ $plan->{tests} } ) { # TBSLIVER run specific subtest
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto

# a seperate step.
$test_db->run_command([ create => $test_coll_name ]);

my $test_coll = $test_db->get_collection(
Copy link
Contributor

Choose a reason for hiding this comment

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

Redundant. Just save the first one at line 121 before you call 'drop' on it.

Copy link
Contributor

@xdg xdg left a comment

Choose a reason for hiding this comment

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

Looks really close. Please make these changes and then get started on rebasing and integrating with PERL-918 work.

};
# Will cause the isa requirement to fire
return unless defined( $_[0] ) && ref( $_[0] ) eq 'HASH';
my $dto = $_[0]->{defaultTransactionOptions};
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be making a clone as well?

$dto ||= {};
$_[0] = {
causalConsistency => 1,
%{ $_[0] },
Copy link
Contributor

Choose a reason for hiding this comment

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

I find this confusing because there are only two available options right now and both are also listed in the clone. Maybe just assign directly to the copies?
{ causalConsistency => $_[0]->{causalConsistency}, defaultTransactionOptions => { %{$_[0]->{defaultTransactionOptions} || {} }}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

mm... premature optimisation I think I did there, had mis-remembered the session spec whcih just says 'in a backwards compatible way' for storing sessionOptions. Have changed it to direct assignment anyway.


# increment transaction id before write, but otherwise is the same for both attempts
$op->session->_server_session->_increment_transaction_id;
$op->session->_increment_transaction_id;
Copy link
Contributor

Choose a reason for hiding this comment

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

ok

);
use namespace::clean -except => 'meta';

# Options provided during strart transaction
Copy link
Contributor

Choose a reason for hiding this comment

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

typo. :-)

@TBSliver TBSliver force-pushed the TBSliver/PERL-875 branch from 3e9d68a to 075d97f Compare June 21, 2018 17:39
@coveralls
Copy link

coveralls commented Jun 22, 2018

Coverage Status

Coverage decreased (-1.7%) to 75.246% when pulling 086d2e0 on shadow-dot-cat:TBSliver/PERL-875 into 9687f61 on mongodb:master.

TBSliver added 26 commits June 22, 2018 13:13
one requires PERL-918 to be finished, the other needs some thought
@TBSliver TBSliver force-pushed the TBSliver/PERL-875 branch from 2702d1f to d8bd31f Compare June 22, 2018 12:31
@TBSliver
Copy link
Contributor Author

that should be everything, Have had to update the dependency on Safe::Isa for the $_call_if_can usage - although that could be downgraded again if that code is modified to not use that function.

@xdg
Copy link
Contributor

xdg commented Jun 22, 2018

Rebased, squashed and merged as 9162d1d

@xdg xdg closed this Jun 22, 2018
@TBSliver TBSliver deleted the TBSliver/PERL-875 branch September 14, 2018 14:59
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants