mongo 2.7.0 → 2.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/README.md +39 -14
- data/Rakefile +1 -0
- data/lib/mongo/auth.rb +4 -1
- data/lib/mongo/client.rb +4 -3
- data/lib/mongo/cluster.rb +1 -1
- data/lib/mongo/collection/view/readable.rb +5 -2
- data/lib/mongo/database.rb +1 -0
- data/lib/mongo/error/invalid_server_preference.rb +1 -0
- data/lib/mongo/error/operation_failure.rb +10 -0
- data/lib/mongo/error/parser.rb +10 -0
- data/lib/mongo/event.rb +1 -2
- data/lib/mongo/operation/result.rb +4 -1
- data/lib/mongo/operation/shared/executable_transaction_label.rb +3 -1
- data/lib/mongo/operation/shared/sessions_supported.rb +1 -1
- data/lib/mongo/protocol/msg.rb +1 -1
- data/lib/mongo/protocol/query.rb +2 -2
- data/lib/mongo/retryable.rb +20 -6
- data/lib/mongo/server.rb +6 -0
- data/lib/mongo/server/connection.rb +4 -4
- data/lib/mongo/server/monitor/connection.rb +9 -5
- data/lib/mongo/server_selector/selectable.rb +86 -32
- data/lib/mongo/session.rb +98 -15
- data/lib/mongo/version.rb +1 -1
- data/spec/README.md +85 -0
- data/spec/integration/bulk_insert_spec.rb +1 -1
- data/spec/integration/change_stream_examples_spec.rb +3 -1
- data/spec/integration/change_stream_spec.rb +10 -14
- data/spec/integration/client_construction_spec.rb +1 -0
- data/spec/integration/command_monitoring_spec.rb +37 -1
- data/spec/integration/command_spec.rb +141 -0
- data/spec/integration/connection_spec.rb +4 -2
- data/spec/integration/cursor_reaping_spec.rb +1 -1
- data/spec/integration/docs_examples_spec.rb +1 -1
- data/spec/integration/retryable_writes_spec.rb +33 -42
- data/spec/integration/server_description_spec.rb +3 -3
- data/spec/integration/server_selector_spec.rb +79 -0
- data/spec/lite_spec_helper.rb +4 -2
- data/spec/mongo/address_spec.rb +8 -0
- data/spec/mongo/auth/cr_spec.rb +5 -2
- data/spec/mongo/auth/invalid_mechanism_spec.rb +11 -0
- data/spec/mongo/auth/scram/conversation_spec.rb +3 -2
- data/spec/mongo/auth/scram/negotiation_spec.rb +1 -2
- data/spec/mongo/auth/scram_spec.rb +11 -6
- data/spec/mongo/auth/user/view_spec.rb +13 -6
- data/spec/mongo/bulk_write_spec.rb +81 -104
- data/spec/mongo/client_construction_spec.rb +18 -7
- data/spec/mongo/client_spec.rb +11 -7
- data/spec/mongo/cluster_spec.rb +30 -1
- data/spec/mongo/collection/view/aggregation_spec.rb +18 -10
- data/spec/mongo/collection/view/change_stream_spec.rb +28 -8
- data/spec/mongo/collection/view/map_reduce_spec.rb +24 -10
- data/spec/mongo/collection/view/readable_spec.rb +37 -19
- data/spec/mongo/collection/view/writable_spec.rb +64 -32
- data/spec/mongo/collection/view_spec.rb +4 -2
- data/spec/mongo/collection_spec.rb +163 -73
- data/spec/mongo/cursor_spec.rb +5 -2
- data/spec/mongo/database_spec.rb +41 -19
- data/spec/mongo/error/no_server_available_spec.rb +1 -1
- data/spec/mongo/error/parser_spec.rb +29 -0
- data/spec/mongo/grid/stream/write_spec.rb +2 -1
- data/spec/mongo/index/view_spec.rb +42 -24
- data/spec/mongo/operation/delete/op_msg_spec.rb +11 -7
- data/spec/mongo/operation/insert/op_msg_spec.rb +10 -6
- data/spec/mongo/operation/update/op_msg_spec.rb +10 -6
- data/spec/mongo/protocol/compressed_spec.rb +1 -1
- data/spec/mongo/protocol/msg_spec.rb +1 -1
- data/spec/mongo/server/app_metadata_spec.rb +2 -1
- data/spec/mongo/server/connection_auth_spec.rb +1 -1
- data/spec/mongo/server/monitor/connection_spec.rb +42 -0
- data/spec/mongo/server_selector_spec.rb +17 -0
- data/spec/mongo/server_spec.rb +110 -0
- data/spec/mongo/session/session_pool_spec.rb +1 -1
- data/spec/mongo/session_spec.rb +1 -1
- data/spec/mongo/session_transaction_spec.rb +162 -1
- data/spec/mongo/socket/ssl_spec.rb +14 -7
- data/spec/mongo/uri/srv_protocol_spec.rb +41 -34
- data/spec/spec_helper.rb +3 -191
- data/spec/spec_tests/change_streams_spec.rb +3 -6
- data/spec/spec_tests/data/transactions/abort.yml +3 -1
- data/spec/spec_tests/data/transactions/commit.yml +4 -3
- data/spec/spec_tests/data/transactions/error-labels.yml +17 -13
- data/spec/spec_tests/data/transactions/read-concern.yml +611 -0
- data/spec/spec_tests/data/transactions/retryable-commit.yml +126 -21
- data/spec/spec_tests/data/transactions_api/callback-aborts.yml +42 -39
- data/spec/spec_tests/data/transactions_api/callback-commits.yml +52 -50
- data/spec/spec_tests/data/transactions_api/callback-retry.yml +33 -31
- data/spec/spec_tests/data/transactions_api/commit-retry.yml +42 -39
- data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror-4.2.yml +13 -12
- data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror.yml +15 -26
- data/spec/spec_tests/data/transactions_api/commit-writeconcernerror.yml +25 -33
- data/spec/spec_tests/data/transactions_api/commit.yml +44 -42
- data/spec/spec_tests/data/transactions_api/transaction-options.yml +25 -23
- data/spec/spec_tests/max_staleness_spec.rb +2 -0
- data/spec/spec_tests/retryable_writes_spec.rb +2 -6
- data/spec/spec_tests/sdam_spec.rb +2 -0
- data/spec/spec_tests/server_selection_spec.rb +3 -0
- data/spec/spec_tests/transactions_api_spec.rb +7 -1
- data/spec/spec_tests/transactions_spec.rb +6 -0
- data/spec/spec_tests/uri_options_spec.rb +4 -26
- data/spec/support/certificates/ca.pem +21 -16
- data/spec/support/certificates/client.pem +90 -90
- data/spec/support/certificates/client_cert.pem +21 -20
- data/spec/support/certificates/client_key.pem +27 -28
- data/spec/support/certificates/client_key_encrypted.pem +26 -26
- data/spec/support/certificates/crl.pem +10 -8
- data/spec/support/certificates/crl_client_revoked.pem +11 -10
- data/spec/support/certificates/server.pem +48 -33
- data/spec/support/change_streams.rb +12 -32
- data/spec/support/client_registry.rb +12 -1
- data/spec/support/cluster_config.rb +48 -2
- data/spec/support/common_shortcuts.rb +73 -7
- data/spec/support/connection_string.rb +0 -3
- data/spec/support/constraints.rb +87 -22
- data/spec/support/crud.rb +2 -1
- data/spec/support/shared/server_selector.rb +0 -28
- data/spec/support/shared/session.rb +25 -14
- data/spec/support/transactions.rb +4 -8
- data/spec/support/transactions/operation.rb +26 -4
- data/spec/support/transactions/verifier.rb +5 -2
- metadata +496 -488
- metadata.gz.sig +5 -2
- data/spec/support/certificates/password_protected.pem +0 -51
@@ -20,6 +20,38 @@ module Mongo
|
|
20
20
|
# @since 2.0.0
|
21
21
|
module Selectable
|
22
22
|
|
23
|
+
# Initialize the server selector.
|
24
|
+
#
|
25
|
+
# @example Initialize the selector.
|
26
|
+
# Mongo::ServerSelector::Secondary.new(:tag_sets => [{'dc' => 'nyc'}])
|
27
|
+
#
|
28
|
+
# @example Initialize the preference with no options.
|
29
|
+
# Mongo::ServerSelector::Secondary.new
|
30
|
+
#
|
31
|
+
# @param [ Hash ] options The server preference options.
|
32
|
+
#
|
33
|
+
# @option options [ Integer ] :local_threshold The local threshold boundary for
|
34
|
+
# nearest selection in seconds.
|
35
|
+
# @option options [ Integer ] max_staleness The maximum replication lag,
|
36
|
+
# in seconds, that a secondary can suffer and still be eligible for a read.
|
37
|
+
# A value of -1 is treated identically to nil, which is to not
|
38
|
+
# have a maximum staleness.
|
39
|
+
#
|
40
|
+
# @raise [ Error::InvalidServerPreference ] If tag sets are specified
|
41
|
+
# but not allowed.
|
42
|
+
#
|
43
|
+
# @since 2.0.0
|
44
|
+
def initialize(options = nil)
|
45
|
+
options = options ? options.dup : {}
|
46
|
+
if options[:max_staleness] == -1
|
47
|
+
options.delete(:max_staleness)
|
48
|
+
end
|
49
|
+
@options = options.freeze
|
50
|
+
@tag_sets = (options[:tag_sets] || []).freeze
|
51
|
+
@max_staleness = options[:max_staleness]
|
52
|
+
validate!
|
53
|
+
end
|
54
|
+
|
23
55
|
# @return [ Hash ] options The options.
|
24
56
|
attr_reader :options
|
25
57
|
|
@@ -48,34 +80,6 @@ module Mongo
|
|
48
80
|
max_staleness == other.max_staleness
|
49
81
|
end
|
50
82
|
|
51
|
-
# Initialize the server selector.
|
52
|
-
#
|
53
|
-
# @example Initialize the selector.
|
54
|
-
# Mongo::ServerSelector::Secondary.new(:tag_sets => [{'dc' => 'nyc'}])
|
55
|
-
#
|
56
|
-
# @example Initialize the preference with no options.
|
57
|
-
# Mongo::ServerSelector::Secondary.new
|
58
|
-
#
|
59
|
-
# @param [ Hash ] options The server preference options.
|
60
|
-
#
|
61
|
-
# @option options [ Integer ] :local_threshold The local threshold boundary for
|
62
|
-
# nearest selection in seconds.
|
63
|
-
# @option options [ Integer ] max_staleness The maximum replication lag,
|
64
|
-
# in seconds, that a secondary can suffer and still be eligible for a read.
|
65
|
-
# A value of -1 is treated identically to nil, which is to not
|
66
|
-
# have a maximum staleness.
|
67
|
-
#
|
68
|
-
# @raise [ Error::InvalidServerPreference ] If tag sets are specified
|
69
|
-
# but not allowed.
|
70
|
-
#
|
71
|
-
# @since 2.0.0
|
72
|
-
def initialize(options = {})
|
73
|
-
@options = (options || {}).freeze
|
74
|
-
@tag_sets = (options[:tag_sets] || []).freeze
|
75
|
-
@max_staleness = options[:max_staleness] unless options[:max_staleness] == -1
|
76
|
-
validate!
|
77
|
-
end
|
78
|
-
|
79
83
|
# Inspect the server selector.
|
80
84
|
#
|
81
85
|
# @example Inspect the server selector.
|
@@ -99,6 +103,24 @@ module Mongo
|
|
99
103
|
#
|
100
104
|
# @since 2.0.0
|
101
105
|
def select_server(cluster, ping = nil)
|
106
|
+
if cluster.replica_set?
|
107
|
+
validate_max_staleness_value_early!
|
108
|
+
end
|
109
|
+
if cluster.addresses.empty?
|
110
|
+
if Lint.enabled?
|
111
|
+
unless cluster.servers.empty?
|
112
|
+
raise Error::LintError, "Cluster has no addresses but has servers: #{cluster.servers.map(&:inspect).join(', ')}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
msg = "Cluster has no addresses, and therefore will never have a server"
|
116
|
+
raise Error::NoServerAvailable.new(self, cluster, msg)
|
117
|
+
end
|
118
|
+
=begin Add this check in version 3.0.0
|
119
|
+
unless cluster.connected?
|
120
|
+
msg = 'Cluster is disconnected'
|
121
|
+
raise Error::NoServerAvailable.new(self, cluster, msg)
|
122
|
+
end
|
123
|
+
=end
|
102
124
|
@local_threshold = cluster.options[:local_threshold] || LOCAL_THRESHOLD
|
103
125
|
@server_selection_timeout = cluster.options[:server_selection_timeout] || SERVER_SELECTION_TIMEOUT
|
104
126
|
deadline = Time.now + server_selection_timeout
|
@@ -134,7 +156,24 @@ module Mongo
|
|
134
156
|
end
|
135
157
|
cluster.scan!(false)
|
136
158
|
end
|
137
|
-
|
159
|
+
|
160
|
+
msg = "No #{name} server is available in cluster: #{cluster.summary} " +
|
161
|
+
"with timeout=#{server_selection_timeout}, " +
|
162
|
+
"LT=#{local_threshold}"
|
163
|
+
dead_monitors = []
|
164
|
+
cluster.servers_list.each do |server|
|
165
|
+
thread = server.monitor.instance_variable_get('@thread')
|
166
|
+
if thread.nil? || !thread.alive?
|
167
|
+
dead_monitors << server
|
168
|
+
end
|
169
|
+
end
|
170
|
+
if dead_monitors.any?
|
171
|
+
msg += ". The following servers have dead monitor threads: #{dead_monitors.map(&:summary).join(', ')}"
|
172
|
+
end
|
173
|
+
unless cluster.connected?
|
174
|
+
msg += ". The cluster is disconnected (client may have been closed)"
|
175
|
+
end
|
176
|
+
raise Error::NoServerAvailable.new(self, cluster, msg)
|
138
177
|
end
|
139
178
|
|
140
179
|
# Get the timeout for server selection.
|
@@ -292,12 +331,27 @@ module Mongo
|
|
292
331
|
end
|
293
332
|
end
|
294
333
|
|
334
|
+
def validate_max_staleness_value_early!
|
335
|
+
if @max_staleness
|
336
|
+
unless @max_staleness >= SMALLEST_MAX_STALENESS_SECONDS
|
337
|
+
msg = "`max_staleness` value (#{@max_staleness}) is too small - it must be at least " +
|
338
|
+
"`Mongo::ServerSelector::SMALLEST_MAX_STALENESS_SECONDS` (#{ServerSelector::SMALLEST_MAX_STALENESS_SECONDS})"
|
339
|
+
raise Error::InvalidServerPreference.new(msg)
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
295
344
|
def validate_max_staleness_value!(cluster)
|
296
345
|
if @max_staleness
|
297
346
|
heartbeat_frequency_seconds = cluster.options[:heartbeat_frequency] || Server::Monitor::HEARTBEAT_FREQUENCY
|
298
|
-
unless @max_staleness >= [
|
299
|
-
|
300
|
-
|
347
|
+
unless @max_staleness >= [
|
348
|
+
SMALLEST_MAX_STALENESS_SECONDS,
|
349
|
+
min_cluster_staleness = heartbeat_frequency_seconds + Cluster::IDLE_WRITE_PERIOD_SECONDS,
|
350
|
+
].max
|
351
|
+
msg = "`max_staleness` value (#{@max_staleness}) is too small - it must be at least " +
|
352
|
+
"`Mongo::ServerSelector::SMALLEST_MAX_STALENESS_SECONDS` (#{ServerSelector::SMALLEST_MAX_STALENESS_SECONDS}) and (the cluster's heartbeat_frequency " +
|
353
|
+
"setting + `Mongo::Cluster::IDLE_WRITE_PERIOD_SECONDS`) (#{min_cluster_staleness})"
|
354
|
+
raise Error::InvalidServerPreference.new(msg)
|
301
355
|
end
|
302
356
|
end
|
303
357
|
end
|
data/lib/mongo/session.rb
CHANGED
@@ -24,6 +24,7 @@ module Mongo
|
|
24
24
|
class Session
|
25
25
|
extend Forwardable
|
26
26
|
include Retryable
|
27
|
+
include Loggable
|
27
28
|
|
28
29
|
# Get the options for this session.
|
29
30
|
#
|
@@ -113,6 +114,13 @@ module Mongo
|
|
113
114
|
# @param [ Client ] client The client through which this session is created.
|
114
115
|
# @param [ Hash ] options The options for this session.
|
115
116
|
#
|
117
|
+
# @option options [ true|false ] :causal_consistency Whether to enable
|
118
|
+
# causal consistency for this session.
|
119
|
+
# @option options [ Hash ] :default_transaction_options Options to pass
|
120
|
+
# to start_transaction by default, can contain any of the options that
|
121
|
+
# start_transaction accepts.
|
122
|
+
# @option options [ true|false ] :implicit For internal driver use only -
|
123
|
+
# specifies whether the session is implicit.
|
116
124
|
# @option options [ Hash ] :read_preference The read preference options hash,
|
117
125
|
# with the following optional keys:
|
118
126
|
# - *:mode* -- the read preference as a string or symbol; valid values are
|
@@ -264,9 +272,28 @@ module Mongo
|
|
264
272
|
end
|
265
273
|
|
266
274
|
# The read concern should be added to any command that starts a transaction.
|
267
|
-
if starting_transaction?
|
268
|
-
|
269
|
-
|
275
|
+
if starting_transaction?
|
276
|
+
# https://jira.mongodb.org/browse/SPEC-1161: transaction's
|
277
|
+
# read concern overrides collection/database/client read concerns,
|
278
|
+
# even if transaction's read concern is not set.
|
279
|
+
# Read concern here is the one sent to the server and may
|
280
|
+
# include afterClusterTime.
|
281
|
+
if rc = c[:readConcern]
|
282
|
+
rc = rc.dup
|
283
|
+
rc.delete(:level)
|
284
|
+
end
|
285
|
+
if txn_read_concern
|
286
|
+
if rc
|
287
|
+
rc.update(txn_read_concern)
|
288
|
+
else
|
289
|
+
rc = txn_read_concern.dup
|
290
|
+
end
|
291
|
+
end
|
292
|
+
if rc.nil? || rc.empty?
|
293
|
+
c.delete(:readConcern)
|
294
|
+
else
|
295
|
+
c[:readConcern ] = rc
|
296
|
+
end
|
270
297
|
end
|
271
298
|
|
272
299
|
# We need to send the read concern level as a string rather than a symbol.
|
@@ -275,8 +302,15 @@ module Mongo
|
|
275
302
|
end
|
276
303
|
|
277
304
|
# The write concern should be added to any abortTransaction or commitTransaction command.
|
278
|
-
if (c[:abortTransaction] || c[:commitTransaction])
|
279
|
-
|
305
|
+
if (c[:abortTransaction] || c[:commitTransaction])
|
306
|
+
if @already_committed
|
307
|
+
wc = BSON::Document.new(c[:writeConcern] || txn_write_concern || {})
|
308
|
+
wc.merge!(w: :majority)
|
309
|
+
wc[:wtimeout] ||= 10000
|
310
|
+
c[:writeConcern] = wc
|
311
|
+
elsif txn_write_concern
|
312
|
+
c[:writeConcern] ||= txn_write_concern
|
313
|
+
end
|
280
314
|
end
|
281
315
|
|
282
316
|
# A non-numeric write concern w value needs to be sent as a string rather than a symbol.
|
@@ -544,6 +578,7 @@ module Mongo
|
|
544
578
|
end
|
545
579
|
|
546
580
|
@state = STARTING_TRANSACTION_STATE
|
581
|
+
@already_committed = false
|
547
582
|
end
|
548
583
|
|
549
584
|
# Commit the currently active transaction on the session.
|
@@ -574,6 +609,7 @@ module Mongo
|
|
574
609
|
# operation again, so we revert the session to the previous state.
|
575
610
|
if within_states?(TRANSACTION_COMMITTED_STATE)
|
576
611
|
@state = @last_commit_skipped ? STARTING_TRANSACTION_STATE : TRANSACTION_IN_PROGRESS_STATE
|
612
|
+
@already_committed = true
|
577
613
|
end
|
578
614
|
|
579
615
|
if starting_transaction?
|
@@ -585,7 +621,16 @@ module Mongo
|
|
585
621
|
if write_concern && !write_concern.is_a?(WriteConcern::Base)
|
586
622
|
write_concern = WriteConcern.get(write_concern)
|
587
623
|
end
|
588
|
-
write_with_retry(self, write_concern, true) do |server, txn_num|
|
624
|
+
write_with_retry(self, write_concern, true) do |server, txn_num, is_retry|
|
625
|
+
if is_retry
|
626
|
+
if write_concern
|
627
|
+
wco = write_concern.options.merge(w: :majority)
|
628
|
+
wco[:wtimeout] ||= 10000
|
629
|
+
write_concern = WriteConcern.get(wco)
|
630
|
+
else
|
631
|
+
write_concern = WriteConcern.get(w: :majority, wtimeout: 10000)
|
632
|
+
end
|
633
|
+
end
|
589
634
|
Operation::Command.new(
|
590
635
|
selector: { commitTransaction: 1 },
|
591
636
|
db_name: 'admin',
|
@@ -652,6 +697,9 @@ module Mongo
|
|
652
697
|
raise
|
653
698
|
rescue Mongo::Error
|
654
699
|
@state = TRANSACTION_ABORTED_STATE
|
700
|
+
rescue Exception
|
701
|
+
@state = TRANSACTION_ABORTED_STATE
|
702
|
+
raise
|
655
703
|
end
|
656
704
|
end
|
657
705
|
|
@@ -687,15 +735,16 @@ module Mongo
|
|
687
735
|
# with_transaction. Exceptions derived from Mongo::Error may be
|
688
736
|
# handled by with_transaction, resulting in retries of the process.
|
689
737
|
#
|
690
|
-
#
|
691
|
-
#
|
692
|
-
#
|
693
|
-
#
|
694
|
-
#
|
695
|
-
#
|
696
|
-
#
|
697
|
-
#
|
698
|
-
#
|
738
|
+
# Currently, with_transaction will retry commits and block invocations
|
739
|
+
# until at least 120 seconds have passed since with_transaction started
|
740
|
+
# executing. This timeout is not configurable and may change in a future
|
741
|
+
# driver version.
|
742
|
+
#
|
743
|
+
# @note with_transaction contains a loop, therefore the if with_transaction
|
744
|
+
# itself is placed in a loop, its block should not call next or break to
|
745
|
+
# control the outer loop because this will instead affect the loop in
|
746
|
+
# with_transaction. The driver will warn and abort the transaction
|
747
|
+
# if it detects this situation.
|
699
748
|
#
|
700
749
|
# @example Execute a statement in a transaction
|
701
750
|
# session.with_transaction(write_concern: {w: :majority}) do
|
@@ -721,17 +770,27 @@ module Mongo
|
|
721
770
|
#
|
722
771
|
# @since 2.7.0
|
723
772
|
def with_transaction(options=nil)
|
773
|
+
# Non-configurable 120 second timeout for the entire operation
|
774
|
+
deadline = Time.now + 120
|
775
|
+
transaction_in_progress = false
|
724
776
|
loop do
|
725
777
|
commit_options = {}
|
726
778
|
if options
|
727
779
|
commit_options[:write_concern] = options[:write_concern]
|
728
780
|
end
|
729
781
|
start_transaction(options)
|
782
|
+
transaction_in_progress = true
|
730
783
|
begin
|
731
784
|
rv = yield self
|
732
785
|
rescue Exception => e
|
733
786
|
if within_states?(STARTING_TRANSACTION_STATE, TRANSACTION_IN_PROGRESS_STATE)
|
734
787
|
abort_transaction
|
788
|
+
transaction_in_progress = false
|
789
|
+
end
|
790
|
+
|
791
|
+
if Time.now >= deadline
|
792
|
+
transaction_in_progress = false
|
793
|
+
raise
|
735
794
|
end
|
736
795
|
|
737
796
|
if e.is_a?(Mongo::Error) && e.label?(Mongo::Error::TRANSIENT_TRANSACTION_ERROR_LABEL)
|
@@ -741,14 +800,24 @@ module Mongo
|
|
741
800
|
raise
|
742
801
|
else
|
743
802
|
if within_states?(TRANSACTION_ABORTED_STATE, NO_TRANSACTION_STATE, TRANSACTION_COMMITTED_STATE)
|
803
|
+
transaction_in_progress = false
|
744
804
|
return rv
|
745
805
|
end
|
746
806
|
|
747
807
|
begin
|
748
808
|
commit_transaction(commit_options)
|
809
|
+
transaction_in_progress = false
|
749
810
|
return rv
|
750
811
|
rescue Mongo::Error => e
|
751
812
|
if e.label?(Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)
|
813
|
+
# WriteConcernFailed
|
814
|
+
if e.is_a?(Mongo::Error::OperationFailure) && e.code == 64 && e.wtimeout?
|
815
|
+
raise
|
816
|
+
end
|
817
|
+
if Time.now >= deadline
|
818
|
+
transaction_in_progress = false
|
819
|
+
raise
|
820
|
+
end
|
752
821
|
wc_options = case v = commit_options[:write_concern]
|
753
822
|
when WriteConcern::Base
|
754
823
|
v.options
|
@@ -760,13 +829,26 @@ module Mongo
|
|
760
829
|
commit_options[:write_concern] = wc_options.merge(w: :majority)
|
761
830
|
retry
|
762
831
|
elsif e.label?(Mongo::Error::TRANSIENT_TRANSACTION_ERROR_LABEL)
|
832
|
+
if Time.now >= deadline
|
833
|
+
transaction_in_progress = false
|
834
|
+
raise
|
835
|
+
end
|
763
836
|
next
|
764
837
|
else
|
838
|
+
transaction_in_progress = false
|
765
839
|
raise
|
766
840
|
end
|
767
841
|
end
|
768
842
|
end
|
769
843
|
end
|
844
|
+
ensure
|
845
|
+
if transaction_in_progress
|
846
|
+
log_warn('with_transaction callback altered with_transaction loop, aborting transaction')
|
847
|
+
begin
|
848
|
+
abort_transaction
|
849
|
+
rescue Error::OperationFailure, Error::InvalidTransactionOperation
|
850
|
+
end
|
851
|
+
end
|
770
852
|
end
|
771
853
|
|
772
854
|
# Get the read preference the session will use in the currently
|
@@ -809,6 +891,7 @@ module Mongo
|
|
809
891
|
end
|
810
892
|
|
811
893
|
def txn_read_concern
|
894
|
+
# Read concern is inherited from client but not db or collection.
|
812
895
|
txn_options && txn_options[:read_concern] || @client.read_concern
|
813
896
|
end
|
814
897
|
|
data/lib/mongo/version.rb
CHANGED
data/spec/README.md
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
The tests run against a MongoDB cluster which is
|
2
|
+
configured and started externally to the test suite. This allows
|
3
|
+
running the entire test suite against, for example, a standalone
|
4
|
+
mongod as well as a replica set. The flip side to this is the
|
5
|
+
test suite will not work without a running mongo cluster, and
|
6
|
+
tests which are not applicable to or cannot be performed on the
|
7
|
+
running mongo cluster are skipped.
|
8
|
+
|
9
|
+
Not only does the test suite require an externally launched cluster,
|
10
|
+
the test suite must also be told how the cluster is configured
|
11
|
+
via MONGODB_URI, TOPOLOGY, MONGODB_ADDRESSES, RS_ENABLED, RS_NAME and/or
|
12
|
+
SHARDED_ENABLED environment variables.
|
13
|
+
|
14
|
+
The test suite attempts to provide diagnostics when it is not able to
|
15
|
+
connect to the cluster it is configured to use.
|
16
|
+
|
17
|
+
Additionally some of the tests assume that the seed list (given in
|
18
|
+
MONGODB_URI or MONGODB_ADDRESSES) encompasses all servers in the cluster,
|
19
|
+
and will fail when MONGODB_URI includes only one host of a replica set.
|
20
|
+
It is best to include all hosts of the cluster in MONGODB_URI and
|
21
|
+
MONGODB_ADDRESSES.
|
22
|
+
|
23
|
+
It is best to have the test suite configured to connect to exactly
|
24
|
+
the hostnames configured in the cluster. If, for example, the test suite
|
25
|
+
is configured to use IP addresses but the cluster is configured with
|
26
|
+
hostnames, the tests should still work (by using SDAM to discover correct
|
27
|
+
cluster configuration) but will spend a significant amount of extra time
|
28
|
+
on server discovery.
|
29
|
+
|
30
|
+
In order to run spec tests, the mongo cluster needs to have fail points
|
31
|
+
enabled. This is accomplished by starting mongod with the following option:
|
32
|
+
--setParameter enableTestCommands=1
|
33
|
+
|
34
|
+
Use the following environment variables to configure the tests:
|
35
|
+
|
36
|
+
CLIENT_DEBUG: Show debug messages from the client.
|
37
|
+
|
38
|
+
CLIENT_DEBUG=1
|
39
|
+
|
40
|
+
MONGODB_URI: Connection string to use. This must be a valid MongoDB URI;
|
41
|
+
mongodb:// and mongodb+srv:// are both supported.
|
42
|
+
RS_ENABLED and SHARDED_ENABLED are NOT honored if using MONGODB_URI -
|
43
|
+
specify replica set name in the URI and to specify a sharded topology
|
44
|
+
set TOPOLOGY=sharded_cluster environment variable.
|
45
|
+
|
46
|
+
MONGODB_URI=mongodb://127.0.0.1:27001/?replicaSet=test
|
47
|
+
MONGODB_URI=mongodb://127.0.0.1:27001,127.0.0.1:27002/ TOPOLOGY=sharded_cluster
|
48
|
+
|
49
|
+
MONGODB_ADDRESSES: Specify addresses to connect to. Use RS_ENABLED,
|
50
|
+
RS_NAME and SHARDED_ENABLED to configure the topology.
|
51
|
+
|
52
|
+
MONGODB_ADDRESSES=127.0.0.1:27017,127.0.0.1:27018
|
53
|
+
MONGODB_ADDRESSES=127.0.0.1:27017,127.0.0.1:27018 RS_ENABLED=1
|
54
|
+
MONGODB_ADDRESSES=127.0.0.1:27017,127.0.0.1:27018 RS_ENABLED=1 RS_NAME=test
|
55
|
+
MONGODB_ADDRESSES=127.0.0.1:27017,127.0.0.1:27018 SHARDED_ENABLED=1
|
56
|
+
|
57
|
+
RS_ENABLED: Instruct the test suite to connect to a replica set.
|
58
|
+
RS_ENABLED is only honored when not using MONGODB_URI; to connect to a
|
59
|
+
replica set with MONGODB_URI, specify the replica set name in the URI
|
60
|
+
(despite the Ruby driver performing topology discovery by default, it
|
61
|
+
doesn't do so in the test suite).
|
62
|
+
RS_NAME can be given to specify the replica set name; the default is
|
63
|
+
ruby-driver-rs.
|
64
|
+
|
65
|
+
RS_ENABLED=1
|
66
|
+
RS_ENABLED=1 RS_NAME=test
|
67
|
+
|
68
|
+
SHARDED_ENABLED: Instruct the test suite to connect to the sharded cluster.
|
69
|
+
Set MONGODB_URI appropriately as well.
|
70
|
+
|
71
|
+
SHARDED_ENABLED=1
|
72
|
+
|
73
|
+
SSL_ENABLED: Instruct the test suite to connect to the cluster via SSL.
|
74
|
+
|
75
|
+
SSL_ENABLED=1
|
76
|
+
# Also acceptable:
|
77
|
+
SSL=ssl
|
78
|
+
|
79
|
+
Note: SSL can also be enabled by giving ssl=true in the MONGODB_URI options.
|
80
|
+
|
81
|
+
EXTERNAL_DISABLED: Run the tests without making any external connections
|
82
|
+
(for example, external connections are required to test DNS seedlists and SRV
|
83
|
+
URIs).
|
84
|
+
|
85
|
+
EXTERNAL_DISABLED=true
|