Skip to content
This repository was archived by the owner on Dec 22, 2021. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
314aef9
PERL-792 Change MongoDB::_ServerSession attribute pod
TBSliver Apr 6, 2018
f94929a
PERL-792 Add transaction_id tracking to server session
TBSliver Apr 6, 2018
98511f7
PERL-792 Added retryWrites option to URL and MongoClient
TBSliver Apr 6, 2018
7c7ee3f
PERL-792 add docs for supported commands in retryable writes
TBSliver Apr 6, 2018
17fc919
PERL-792 first pass of implementation and enabling retryable writes for
TBSliver Apr 18, 2018
4752b88
PERL-792 Added test data
TBSliver Apr 18, 2018
873f043
PERL-792 Adding retryable writes to most commands for testing
TBSliver May 4, 2018
ee8bb24
PERL-792 Add retryable write to command for bulk write
TBSliver May 4, 2018
c618599
minor: devel: add MONGO_PRESERVE env option for tempdir
xdg May 9, 2018
294b318
PERL-792 Extract link object from op helpers
xdg May 9, 2018
918b9da
PERL-792 Pull link object out of bulk write execution loop
xdg May 9, 2018
bce1177
PERL-792 Finish yml tests for bulkWrite retryable writes
TBSliver May 8, 2018
12321eb
Move PERL-792 spec test to normal testing dir and add to seq testrules
TBSliver May 9, 2018
833e674
PERL-792 Split Batch test first mockup
TBSliver May 10, 2018
ab6c13a
PERL-792 Stop undef error on Link test for retryWrites wire version
TBSliver May 11, 2018
58bcf5a
PERL-792 remove no-journal causing issues with changestreams
TBSliver May 14, 2018
66ff378
PERL-792 first pass RS failover test
TBSliver May 14, 2018
ac955aa
PERL-792 Added command monitoring for checking the insert succeeded on
TBSliver May 14, 2018
c765c53
PERL-792 Command Construction test
TBSliver May 16, 2018
b644c6d
PERL-792 updated split batch test
TBSliver May 16, 2018
6f51dfd
PERL-792 update replica set failover test to use orchestrator
TBSliver May 16, 2018
ff04d47
PERL-792 Guard against going off the end of event array in test
TBSliver May 21, 2018
04625a5
PERL-792 remove unneccessary comments and commented code
TBSliver May 21, 2018
efe7894
PERL-792 remove RetryableBulk role
TBSliver May 21, 2018
12519d0
PERL-792 remove ticket prefix from tests
TBSliver May 21, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile.PL
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ requires 'ExtUtils::ParseXS' => 3.21;
requires 'IO::File';
requires 'IO::Socket' => ( $^O eq 'MSWin32' ? '1.31' : '0' );
requires 'List::Util';
requires 'Math::BigInt';
requires 'MIME::Base64';
requires 'Moo' => '2';
requires 'Moo::Role';
Expand Down Expand Up @@ -68,7 +69,6 @@ test_requires 'File::Spec';
test_requires 'File::Temp' => '0.17';
test_requires 'FileHandle';
test_requires 'JSON::MaybeXS' => '1.002005';
test_requires 'Math::BigInt';
test_requires 'Path::Tiny' => '0.054';
test_requires 'Test::Deep' => '0.111';
test_requires 'Test::Fatal';
Expand Down
11 changes: 11 additions & 0 deletions devel/config/replicaset-multi-3.6.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
type: replica
setName: foo
default_args: -v --noprealloc --smallfiles --bind_ip 0.0.0.0 --nssize 6 --quiet
default_version: 3.6
mongod:
- name: host1
- name: host2
- name: host3

# vim: ts=4 sts=4 sw=4 et:
1 change: 1 addition & 0 deletions devel/lib/MongoDBTest/Role/Server.pm
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ sub _build_tempdir {
return Path::Tiny->tempdir(
TEMPLATE => $self->name . "-XXXXXX",
($ENV{DATA_DIR} ? (DIR => $ENV{DATA_DIR}) : ()),
($ENV{MONGO_PRESERVE} ? (CLEANUP => 0) : ()),
);
}

Expand Down
156 changes: 156 additions & 0 deletions devel/t-dynamic/PERL-792-replica-set-failover.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#
# Copyright 2009-2013 MongoDB, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# Test in t-dynamic as not sure if failover should be tested on install?

use strict;
use warnings;
use JSON::MaybeXS;
use Path::Tiny 0.054; # basename with suffix
use Test::More 0.88;
use Test::Fatal;
use boolean;

use lib "t/lib";
use lib "devel/lib";

use MongoDBTest::Orchestrator;

use MongoDBTest qw/
build_client
get_test_db
clear_testdbs
get_unique_collection
server_version
server_type
check_min_server_version
get_feature_compat_version
/;

my $orc =
MongoDBTest::Orchestrator->new(
config_file => "devel/config/replicaset-multi-3.6.yml" );
$orc->start;

$ENV{MONGOD} = $orc->as_uri;

my @events;

sub clear_events { @events = () }
sub event_count { scalar @events }
sub event_cb { push @events, $_[0] }

my $conn = build_client(
retry_writes => 1,
heartbeat_frequency_ms => 60 * 1000,
# build client modifies this so we set it explicitly to the default
server_selection_timeout_ms => 30 * 1000,
server_selection_try_once => 0,
monitoring_callback => \&event_cb,
);
my $testdb = get_test_db($conn);
my $coll = get_unique_collection( $testdb, 'retry_failover' );
my $server_version = server_version($conn);
my $server_type = server_type($conn);
my $feat_compat_ver = get_feature_compat_version($conn);

plan skip_all => "standalone servers dont support retryableWrites"
if $server_type eq 'Standalone';

plan skip_all => "retryableWrites requires featureCompatibilityVersion 3.6 - got $feat_compat_ver"
if ( $feat_compat_ver < 3.6 );

my $primary = $conn->_topology->current_primary;

my $fail_conn = build_client( host => $primary->address );

my $step_down_conn = build_client();

my $ret = $coll->insert_one( { _id => 1, test => 'value' } );

is $ret->inserted_id, 1, 'write succeeded';

my $result = $coll->find_one( { _id => 1 } );

is $result->{test}, 'value', 'Successful write';

$fail_conn->send_admin_command([
configureFailPoint => 'onPrimaryTransactionalWrite',
mode => 'alwaysOn',
]);

# wrapped in eval as this will just drop connection
eval {
$step_down_conn->send_admin_command([
replSetStepDown => 60,
force => true,
]);
};
my $err = $@;
isa_ok( $err, 'MongoDB::NetworkError', 'Step down successfully errored' );

clear_events();

my $post_stepdown_ret = $coll->insert_one( { _id => 2, test => 'again' } );

is $post_stepdown_ret->inserted_id, 2, 'write succeeded';

# All this is to make sure we dont make assumptions on position of the actual
# event, just that the failed one comes first.
my $first_insert_index;

for my $f_idx ( 0 .. $#events - 1 ) {
my $event = $events[ $f_idx ];
if ( $event->{ commandName } eq 'insert'
&& $event->{ type } eq 'command_started' ) {
my $next_event = $events[ $f_idx + 1 ];
is $next_event->{ commandName }, 'insert', 'found insert reply';
is $next_event->{ type }, 'command_failed', 'found failed reply';
$first_insert_index = $f_idx;
last;
}
}

ok defined( $first_insert_index ), 'found first command';

my $second_insert_index;

if ( $first_insert_index + 2 > $#events - 1 ) {
fail 'not enough events captured';
}

for my $s_idx ( $first_insert_index + 2 .. $#events - 1 ) {
my $event = $events[ $s_idx ];
if ( $event->{ commandName } eq 'insert'
&& $event->{ type } eq 'command_started' ) {
my $next_event = $events[ $s_idx + 1 ];
is $next_event->{ commandName }, 'insert', 'found insert reply';
is $next_event->{ type }, 'command_succeeded', 'found success reply';
$second_insert_index = $s_idx;
last;
}
}

ok defined( $second_insert_index ), 'found second command';

$fail_conn->send_admin_command([
configureFailPoint => 'onPrimaryTransactionalWrite',
mode => 'off',
]);

clear_testdbs;

done_testing;
9 changes: 9 additions & 0 deletions lib/MongoDB/BulkWrite.pm
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ sub _build__client {
return $self->_database->_client;
}

has _retryable => (
is => 'rw',
isa => Bool,
default => 1,
);

with $_ for qw(
MongoDB::Role::_DeprecationWarner
);
Expand Down Expand Up @@ -268,6 +274,7 @@ sub execute {
my $session = $self->collection->_get_session_from_hashref( $options );

my $op = MongoDB::Op::_BulkWrite->_new(
client => $self->_client,
db_name => $self->_database->name,
coll_name => $self->collection->name,
full_name => $self->collection->full_name,
Expand All @@ -278,8 +285,10 @@ sub execute {
write_concern => $write_concern,
session => $session,
monitoring_callback => $self->_client->monitoring_callback,
_retryable => $self->_retryable,
);

# Op::_BulkWrite internally does retryable writes
return $self->_client->send_write_op( $op );
}

Expand Down
3 changes: 3 additions & 0 deletions lib/MongoDB/BulkWriteView.pm
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ sub _update {
$doc = Tie::IxHash->new(%$doc);
}

$self->_bulk->_retryable( 0 ) if $method eq 'update_many';
$self->_bulk->_retryable( 0 ) if $method eq 'delete_many';

my $update = {
q => $self->_query,
u => $doc,
Expand Down
21 changes: 15 additions & 6 deletions lib/MongoDB/Collection.pm
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ sub insert_one {
MongoDB::UsageError->throw("document argument must be a reference")
unless ref( $_[1] );

return $_[0]->client->send_write_op(
return $_[0]->client->send_retryable_write_op(
MongoDB::Op::_InsertOne->_new(
session => $_[0]->_get_session_from_hashref( $_[2] ),
( defined $_[2] ? (%{$_[2]}) : () ),
Expand Down Expand Up @@ -422,6 +422,7 @@ sub insert_many {
MongoDB::UsageError->throw("documents argument must be an array reference")
unless ref( $_[1] ) eq 'ARRAY';

# internally ends up performing a retryable write if possible, see OP::_BulkWrite
my $res = $_[0]->client->send_write_op(
MongoDB::Op::_BulkWrite->_new(
# default
Expand All @@ -431,6 +432,8 @@ sub insert_many {
( defined $_[2] ? ( %{ $_[2] } ) : () ),
# un-overridable
queue => [ map { [ insert => $_ ] } @{ $_[1] } ],
# insert_many is specifically retryable (PERL-792)
_retryable => 1,
%{ $_[0]->_op_args },
)
);
Expand Down Expand Up @@ -475,7 +478,7 @@ sub delete_one {
MongoDB::UsageError->throw("filter argument must be a reference")
unless ref( $_[1] );

return $_[0]->client->send_write_op(
return $_[0]->client->send_retryable_write_op(
MongoDB::Op::_Delete->_new(
session => $_[0]->_get_session_from_hashref( $_[2] ),
( defined $_[2] ? (%{$_[2]}) : () ),
Expand Down Expand Up @@ -557,7 +560,7 @@ sub replace_one {
MongoDB::UsageError->throw("filter and replace arguments must be references")
unless ref( $_[1] ) && ref( $_[2] );

return $_[0]->client->send_write_op(
return $_[0]->client->send_retryable_write_op(
MongoDB::Op::_Update->_new(
session => $_[0]->_get_session_from_hashref( $_[3] ),
( defined $_[3] ? (%{$_[3]}) : () ),
Expand Down Expand Up @@ -608,7 +611,7 @@ sub update_one {
MongoDB::UsageError->throw("filter and update arguments must be references")
unless ref( $_[1] ) && ref( $_[2] );

return $_[0]->client->send_write_op(
return $_[0]->client->send_retryable_write_op(
MongoDB::Op::_Update->_new(
session => $_[0]->_get_session_from_hashref( $_[3] ),
( defined $_[3] ? (%{$_[3]}) : () ),
Expand Down Expand Up @@ -940,7 +943,9 @@ sub find_one_and_delete {
session => $session,
);

return $self->client->send_write_op($op);
return $self->write_concern->is_acknowledged
? $self->client->send_retryable_write_op( $op )
: $self->client->send_write_op( $op );
}

=method find_one_and_replace
Expand Down Expand Up @@ -1647,6 +1652,7 @@ sub bulk_write {
}
elsif ( $method eq 'delete_many' ) {
$view->delete_many;
$bulk->_retryable( 0 );
next;
}

Expand All @@ -1662,6 +1668,7 @@ sub bulk_write {
}
elsif ( $method eq 'update_many' ) {
$view->update_many($update_doc);
$bulk->_retryable( 0 );
}
else {
MongoDB::UsageError->throw("unknown bulk operation '$method'");
Expand Down Expand Up @@ -1733,7 +1740,9 @@ sub _find_one_and_update_or_replace {
%{ $self->_op_args },
);

return $self->client->send_write_op($op);
return $self->write_concern->is_acknowledged
? $self->client->send_retryable_write_op( $op )
: $self->client->send_write_op( $op );
}

# Extracts a session from a provided hashref, or returns an implicit session
Expand Down
Loading