mongo 2.7.0 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|