mongo 2.9.2 → 2.10.0.rc0
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/lib/mongo.rb +1 -0
- data/lib/mongo/auth/user/view.rb +4 -4
- data/lib/mongo/bulk_write.rb +14 -8
- data/lib/mongo/bulk_write/result.rb +1 -1
- data/lib/mongo/bulk_write/result_combiner.rb +2 -2
- data/lib/mongo/bulk_write/transformable.rb +17 -9
- data/lib/mongo/client.rb +107 -16
- data/lib/mongo/cluster.rb +47 -25
- data/lib/mongo/cluster/topology/replica_set_no_primary.rb +1 -1
- data/lib/mongo/cluster_time.rb +139 -0
- data/lib/mongo/collection.rb +84 -25
- data/lib/mongo/collection/view.rb +7 -3
- data/lib/mongo/collection/view/aggregation.rb +4 -4
- data/lib/mongo/collection/view/builder/aggregation.rb +31 -6
- data/lib/mongo/collection/view/builder/find_command.rb +4 -1
- data/lib/mongo/collection/view/builder/map_reduce.rb +4 -1
- data/lib/mongo/collection/view/change_stream.rb +54 -66
- data/lib/mongo/collection/view/iterable.rb +2 -2
- data/lib/mongo/collection/view/map_reduce.rb +6 -4
- data/lib/mongo/collection/view/readable.rb +36 -16
- data/lib/mongo/collection/view/writable.rb +68 -22
- data/lib/mongo/cursor.rb +87 -20
- data/lib/mongo/database.rb +47 -43
- data/lib/mongo/database/view.rb +54 -11
- data/lib/mongo/error.rb +13 -4
- data/lib/mongo/error/invalid_write_concern.rb +2 -2
- data/lib/mongo/error/operation_failure.rb +65 -11
- data/lib/mongo/error/parser.rb +41 -8
- data/lib/mongo/grid/fs_bucket.rb +26 -6
- data/lib/mongo/grid/stream/read.rb +9 -2
- data/lib/mongo/grid/stream/write.rb +21 -5
- data/lib/mongo/index/view.rb +3 -3
- data/lib/mongo/lint.rb +10 -3
- data/lib/mongo/operation.rb +2 -0
- data/lib/mongo/operation/aggregate/result.rb +19 -6
- data/lib/mongo/operation/collections_info.rb +1 -1
- data/lib/mongo/operation/get_more/result.rb +9 -0
- data/lib/mongo/operation/list_collections/command.rb +1 -3
- data/lib/mongo/operation/list_collections/op_msg.rb +1 -2
- data/lib/mongo/operation/parallel_scan/command.rb +4 -1
- data/lib/mongo/operation/parallel_scan/op_msg.rb +4 -1
- data/lib/mongo/operation/result.rb +27 -4
- data/lib/mongo/operation/shared/executable.rb +19 -5
- data/lib/mongo/operation/shared/executable_no_validate.rb +1 -2
- data/lib/mongo/operation/shared/executable_transaction_label.rb +0 -9
- data/lib/mongo/operation/shared/polymorphic_result.rb +9 -1
- data/lib/mongo/operation/shared/result/aggregatable.rb +2 -2
- data/lib/mongo/operation/shared/sessions_supported.rb +42 -32
- data/lib/mongo/operation/shared/specifiable.rb +40 -0
- data/lib/mongo/operation/shared/unpinnable.rb +39 -0
- data/lib/mongo/operation/shared/write.rb +1 -1
- data/lib/mongo/protocol/update.rb +6 -2
- data/lib/mongo/retryable.rb +79 -39
- data/lib/mongo/server/connection.rb +10 -3
- data/lib/mongo/server/description.rb +25 -1
- data/lib/mongo/server/monitor/connection.rb +1 -1
- data/lib/mongo/server_selector.rb +10 -0
- data/lib/mongo/server_selector/selectable.rb +172 -32
- data/lib/mongo/session.rb +654 -581
- data/lib/mongo/session/session_pool.rb +1 -1
- data/lib/mongo/socket.rb +7 -28
- data/lib/mongo/socket/ssl.rb +26 -1
- data/lib/mongo/socket/tcp.rb +3 -0
- data/lib/mongo/socket/unix.rb +3 -0
- data/lib/mongo/uri.rb +112 -265
- data/lib/mongo/uri/srv_protocol.rb +4 -1
- data/lib/mongo/version.rb +1 -1
- data/lib/mongo/write_concern.rb +10 -29
- data/lib/mongo/write_concern/acknowledged.rb +12 -0
- data/lib/mongo/write_concern/base.rb +17 -13
- data/lib/mongo/write_concern/unacknowledged.rb +12 -0
- data/spec/atlas/atlas_connectivity_spec.rb +7 -37
- data/spec/atlas/operations_spec.rb +25 -0
- data/spec/integration/change_stream_examples_spec.rb +45 -31
- data/spec/integration/change_stream_spec.rb +305 -5
- data/spec/integration/client_spec.rb +44 -0
- data/spec/integration/command_monitoring_spec.rb +1 -0
- data/spec/integration/command_spec.rb +7 -1
- data/spec/integration/mmapv1_spec.rb +28 -0
- data/spec/integration/mongos_pinning_spec.rb +34 -0
- data/spec/integration/operation_failure_code_spec.rb +2 -2
- data/spec/integration/{read_concern.rb → read_concern_spec.rb} +7 -1
- data/spec/integration/read_preference_spec.rb +485 -0
- data/spec/integration/retryable_writes_spec.rb +8 -19
- data/spec/integration/sdam_error_handling_spec.rb +1 -1
- data/spec/integration/sdam_events_spec.rb +2 -2
- data/spec/integration/server_description_spec.rb +14 -17
- data/spec/integration/server_selector_spec.rb +7 -3
- data/spec/integration/server_spec.rb +48 -0
- data/spec/integration/ssl_uri_options_spec.rb +1 -1
- data/spec/integration/step_down_spec.rb +10 -4
- data/spec/integration/transactions_examples_spec.rb +11 -10
- data/spec/lite_spec_helper.rb +19 -16
- data/spec/mongo/auth/scram/negotiation_spec.rb +11 -8
- data/spec/mongo/bulk_write/ordered_combiner_spec.rb +6 -6
- data/spec/mongo/bulk_write/unordered_combiner_spec.rb +4 -4
- data/spec/mongo/bulk_write_spec.rb +12 -2
- data/spec/mongo/client_construction_spec.rb +160 -8
- data/spec/mongo/client_spec.rb +5 -4
- data/spec/mongo/cluster_spec.rb +6 -6
- data/spec/mongo/cluster_time_spec.rb +148 -0
- data/spec/mongo/collection/view/aggregation_spec.rb +34 -15
- data/spec/mongo/collection/view/change_stream_spec.rb +62 -3
- data/spec/mongo/collection/view/map_reduce_spec.rb +7 -5
- data/spec/mongo/collection/view/readable_spec.rb +4 -4
- data/spec/mongo/collection_spec.rb +331 -14
- data/spec/mongo/cursor_spec.rb +117 -5
- data/spec/mongo/database_spec.rb +240 -8
- data/spec/mongo/error/operation_failure_spec.rb +47 -1
- data/spec/mongo/error/parser_spec.rb +160 -23
- data/spec/mongo/operation/insert/bulk_spec.rb +2 -1
- data/spec/mongo/operation/result_spec.rb +27 -0
- data/spec/mongo/operation/update/bulk_spec.rb +1 -0
- data/spec/mongo/retryable_spec.rb +2 -0
- data/spec/mongo/server/app_metadata_spec.rb +2 -2
- data/spec/mongo/server/connection_spec.rb +13 -17
- data/spec/mongo/server/monitor/connection_spec.rb +13 -10
- data/spec/mongo/server_selector_spec.rb +34 -2
- data/spec/mongo/session/session_pool_spec.rb +14 -3
- data/spec/mongo/session_spec.rb +3 -3
- data/spec/mongo/session_transaction_spec.rb +4 -3
- data/spec/mongo/socket/ssl_spec.rb +19 -5
- data/spec/mongo/socket_spec.rb +1 -62
- data/spec/mongo/uri/srv_protocol_spec.rb +14 -20
- data/spec/mongo/uri_option_parsing_spec.rb +94 -8
- data/spec/mongo/uri_spec.rb +23 -10
- data/spec/mongo/write_concern_spec.rb +56 -3
- data/spec/spec_tests/change_streams_spec.rb +2 -1
- data/spec/spec_tests/cmap_spec.rb +1 -1
- data/spec/spec_tests/crud_spec.rb +12 -2
- data/spec/spec_tests/data/change_streams/change-streams-errors.yml +24 -1
- data/spec/spec_tests/data/change_streams/change-streams.yml +172 -3
- data/spec/spec_tests/data/command_monitoring/bulkWrite.yml +1 -1
- data/spec/spec_tests/data/command_monitoring/updateMany.yml +0 -2
- data/spec/spec_tests/data/command_monitoring/updateOne.yml +0 -5
- data/spec/spec_tests/data/crud/read/aggregate-out.yml +0 -6
- data/spec/spec_tests/data/crud/read/count-empty.yml +29 -0
- data/spec/spec_tests/data/crud/write/bulkWrite-arrayFilters.yml +1 -0
- data/spec/spec_tests/data/crud/write/bulkWrite-collation.yml +101 -0
- data/spec/spec_tests/data/crud/write/bulkWrite.yml +401 -0
- data/spec/spec_tests/data/crud/write/insertMany.yml +58 -2
- data/spec/spec_tests/data/crud/write/updateMany-arrayFilters.yml +3 -0
- data/spec/spec_tests/data/crud/write/updateOne-arrayFilters.yml +6 -1
- data/spec/spec_tests/data/crud_v2/aggregate-merge.yml +103 -0
- data/spec/spec_tests/data/crud_v2/aggregate-out-readConcern.yml +110 -0
- data/spec/spec_tests/data/crud_v2/bulkWrite-arrayFilters.yml +81 -0
- data/spec/spec_tests/data/crud_v2/db-aggregate.yml +38 -0
- data/spec/spec_tests/data/crud_v2/updateWithPipelines.yml +92 -0
- data/spec/spec_tests/data/retryable_writes/insertOne-serverErrors.yml +2 -2
- data/spec/spec_tests/data/transactions/abort.yml +3 -0
- data/spec/spec_tests/data/transactions/bulk.yml +3 -8
- data/spec/spec_tests/data/transactions/causal-consistency.yml +3 -8
- data/spec/spec_tests/data/transactions/commit.yml +3 -1
- data/spec/spec_tests/data/transactions/count.yml +3 -0
- data/spec/spec_tests/data/transactions/delete.yml +3 -0
- data/spec/spec_tests/data/transactions/error-labels.yml +4 -1
- data/spec/spec_tests/data/transactions/errors-client.yml +56 -0
- data/spec/spec_tests/data/transactions/errors.yml +3 -0
- data/spec/spec_tests/data/transactions/findOneAndDelete.yml +3 -0
- data/spec/spec_tests/data/transactions/findOneAndReplace.yml +3 -0
- data/spec/spec_tests/data/transactions/findOneAndUpdate.yml +3 -0
- data/spec/spec_tests/data/transactions/insert.yml +3 -0
- data/spec/spec_tests/data/transactions/isolation.yml +3 -0
- data/spec/spec_tests/data/transactions/mongos-pin-auto.yml +1671 -0
- data/spec/spec_tests/data/transactions/mongos-recovery-token.yml +347 -0
- data/spec/spec_tests/data/transactions/pin-mongos.yml +557 -0
- data/spec/spec_tests/data/transactions/read-concern.yml +3 -0
- data/spec/spec_tests/data/transactions/read-pref.yml +3 -0
- data/spec/spec_tests/data/transactions/reads.yml +3 -0
- data/spec/spec_tests/data/transactions/retryable-abort.yml +5 -2
- data/spec/spec_tests/data/transactions/retryable-commit.yml +4 -1
- data/spec/spec_tests/data/transactions/retryable-writes.yml +3 -0
- data/spec/spec_tests/data/transactions/run-command.yml +3 -0
- data/spec/spec_tests/data/transactions/transaction-options.yml +6 -0
- data/spec/spec_tests/data/transactions/update.yml +3 -8
- data/spec/spec_tests/data/transactions/write-concern.yml +348 -38
- data/spec/spec_tests/data/transactions_api/callback-aborts.yml +6 -0
- data/spec/spec_tests/data/transactions_api/callback-commits.yml +5 -0
- data/spec/spec_tests/data/transactions_api/callback-retry.yml +7 -2
- data/spec/spec_tests/data/transactions_api/commit-retry.yml +70 -15
- data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror-4.2.yml +3 -0
- data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror.yml +3 -0
- data/spec/spec_tests/data/transactions_api/commit-writeconcernerror.yml +59 -109
- data/spec/spec_tests/data/transactions_api/commit.yml +5 -0
- data/spec/spec_tests/data/transactions_api/transaction-options.yml +10 -0
- data/spec/spec_tests/retryable_reads_spec.rb +5 -2
- data/spec/spec_tests/retryable_writes_spec.rb +5 -2
- data/spec/spec_tests/sdam_monitoring_spec.rb +3 -3
- data/spec/spec_tests/sdam_spec.rb +2 -2
- data/spec/spec_tests/transactions_api_spec.rb +1 -67
- data/spec/spec_tests/transactions_spec.rb +2 -66
- data/spec/support/authorization.rb +4 -0
- data/spec/support/change_streams.rb +30 -10
- data/spec/support/change_streams/operation.rb +27 -0
- data/spec/support/client_registry.rb +44 -25
- data/spec/support/cluster_config.rb +25 -14
- data/spec/support/cluster_tools.rb +32 -10
- data/spec/support/command_monitoring.rb +1 -1
- data/spec/support/common_shortcuts.rb +30 -0
- data/spec/support/connection_string.rb +8 -3
- data/spec/support/constraints.rb +34 -0
- data/spec/support/crud.rb +31 -16
- data/spec/support/crud/context.rb +23 -0
- data/spec/support/crud/operation.rb +311 -14
- data/spec/support/crud/spec.rb +2 -1
- data/spec/support/crud/test.rb +24 -27
- data/spec/support/crud/test_base.rb +22 -0
- data/spec/support/crud/verifier.rb +15 -1
- data/spec/support/event_subscriber.rb +12 -0
- data/spec/support/sdam_formatter_integration.rb +12 -6
- data/spec/support/shared/server_selector.rb +10 -0
- data/spec/support/shared/session.rb +13 -12
- data/spec/support/spec_config.rb +32 -22
- data/spec/support/spec_setup.rb +2 -2
- data/spec/support/transactions.rb +87 -0
- data/spec/support/transactions/context.rb +33 -0
- data/spec/support/transactions/operation.rb +99 -349
- data/spec/support/transactions/spec.rb +1 -3
- data/spec/support/transactions/test.rb +110 -49
- data/spec/support/utils.rb +74 -1
- metadata +52 -10
- metadata.gz.sig +0 -0
- data/spec/support/crud/read.rb +0 -265
- data/spec/support/crud/write.rb +0 -284
data/lib/mongo/session.rb
CHANGED
@@ -20,36 +20,196 @@ module Mongo
|
|
20
20
|
# A logical session representing a set of sequential operations executed
|
21
21
|
# by an application that are related in some way.
|
22
22
|
#
|
23
|
+
# @note Session objects are not thread-safe. An application may use a session
|
24
|
+
# from only one thread or process at a time.
|
25
|
+
#
|
23
26
|
# @since 2.5.0
|
24
27
|
class Session
|
25
28
|
extend Forwardable
|
26
29
|
include Retryable
|
27
30
|
include Loggable
|
31
|
+
include ClusterTime::Consumer
|
32
|
+
|
33
|
+
# Initialize a Session.
|
34
|
+
#
|
35
|
+
# @note Applications should use Client#start_session to begin a session.
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# Session.new(server_session, client, options)
|
39
|
+
#
|
40
|
+
# @param [ ServerSession ] server_session The server session this session is associated with.
|
41
|
+
# @param [ Client ] client The client through which this session is created.
|
42
|
+
# @param [ Hash ] options The options for this session.
|
43
|
+
#
|
44
|
+
# @option options [ true|false ] :causal_consistency Whether to enable
|
45
|
+
# causal consistency for this session.
|
46
|
+
# @option options [ Hash ] :default_transaction_options Options to pass
|
47
|
+
# to start_transaction by default, can contain any of the options that
|
48
|
+
# start_transaction accepts.
|
49
|
+
# @option options [ true|false ] :implicit For internal driver use only -
|
50
|
+
# specifies whether the session is implicit.
|
51
|
+
# @option options [ Hash ] :read_preference The read preference options hash,
|
52
|
+
# with the following optional keys:
|
53
|
+
# - *:mode* -- the read preference as a string or symbol; valid values are
|
54
|
+
# *:primary*, *:primary_preferred*, *:secondary*, *:secondary_preferred*
|
55
|
+
# and *:nearest*.
|
56
|
+
#
|
57
|
+
# @since 2.5.0
|
58
|
+
# @api private
|
59
|
+
def initialize(server_session, client, options = {})
|
60
|
+
@server_session = server_session
|
61
|
+
options = options.dup
|
62
|
+
|
63
|
+
@client = client.use(:admin)
|
64
|
+
@options = options.freeze
|
65
|
+
@cluster_time = nil
|
66
|
+
@state = NO_TRANSACTION_STATE
|
67
|
+
end
|
28
68
|
|
29
|
-
#
|
69
|
+
# @return [ Hash ] The options for this session.
|
30
70
|
#
|
31
71
|
# @since 2.5.0
|
32
72
|
attr_reader :options
|
33
73
|
|
34
|
-
#
|
74
|
+
# @return [ Client ] The client through which this session was created.
|
35
75
|
#
|
36
76
|
# @since 2.5.1
|
37
77
|
attr_reader :client
|
38
78
|
|
39
|
-
|
79
|
+
def cluster
|
80
|
+
@client.cluster
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [ BSON::Timestamp ] The latest seen operation time for this session.
|
40
84
|
#
|
41
85
|
# @since 2.5.0
|
42
|
-
attr_reader :
|
86
|
+
attr_reader :operation_time
|
87
|
+
|
88
|
+
# @return [ Hash ] The options for the transaction currently being executed
|
89
|
+
# on this session.
|
90
|
+
#
|
91
|
+
# @since 2.6.0
|
92
|
+
def txn_options
|
93
|
+
@txn_options or raise ArgumentError, "There is no active transaction"
|
94
|
+
end
|
95
|
+
|
96
|
+
# Is this session an implicit one (not user-created).
|
97
|
+
#
|
98
|
+
# @example Is the session implicit?
|
99
|
+
# session.implicit?
|
100
|
+
#
|
101
|
+
# @return [ true, false ] Whether this session is implicit.
|
102
|
+
#
|
103
|
+
# @since 2.5.1
|
104
|
+
def implicit?
|
105
|
+
@implicit ||= !!(@options.key?(:implicit) && @options[:implicit] == true)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Is this session an explicit one (i.e. user-created).
|
109
|
+
#
|
110
|
+
# @example Is the session explicit?
|
111
|
+
# session.explicit?
|
112
|
+
#
|
113
|
+
# @return [ true, false ] Whether this session is explicit.
|
114
|
+
#
|
115
|
+
# @since 2.5.2
|
116
|
+
def explicit?
|
117
|
+
!implicit?
|
118
|
+
end
|
119
|
+
|
120
|
+
# Whether reads executed with this session can be retried according to
|
121
|
+
# the modern retryable reads specification.
|
122
|
+
#
|
123
|
+
# If this method returns true, the modern retryable reads have been
|
124
|
+
# requested by the application. If the server selected for a read operation
|
125
|
+
# supports modern retryable reads, they will be used for that particular
|
126
|
+
# operation. If the server selected for a read operation does not support
|
127
|
+
# modern retryable reads, the read will not be retried.
|
128
|
+
#
|
129
|
+
# If this method returns false, legacy retryable reads have been requested
|
130
|
+
# by the application. Legacy retryable read logic will be used regardless
|
131
|
+
# of server version of the server(s) that the client is connected to.
|
132
|
+
# The number of read retries is given by :max_read_retries client option,
|
133
|
+
# which is 1 by default and can be set to 0 to disable legacy read retries.
|
134
|
+
#
|
135
|
+
# @api private
|
136
|
+
def retry_reads?
|
137
|
+
client.options[:retry_reads] != false
|
138
|
+
end
|
43
139
|
|
44
|
-
#
|
140
|
+
# Will writes executed with this session be retried.
|
141
|
+
#
|
142
|
+
# @example Will writes be retried.
|
143
|
+
# session.retry_writes?
|
144
|
+
#
|
145
|
+
# @return [ true, false ] If writes will be retried.
|
146
|
+
#
|
147
|
+
# @note Retryable writes are only available on server versions at least 3.6
|
148
|
+
# and with sharded clusters or replica sets.
|
45
149
|
#
|
46
150
|
# @since 2.5.0
|
47
|
-
|
151
|
+
def retry_writes?
|
152
|
+
!!client.options[:retry_writes] && (cluster.replica_set? || cluster.sharded?)
|
153
|
+
end
|
48
154
|
|
49
|
-
#
|
155
|
+
# Get the read preference the session will use in the currently
|
156
|
+
# active transaction.
|
157
|
+
#
|
158
|
+
# This is a driver style hash with underscore keys.
|
159
|
+
#
|
160
|
+
# @example Get the transaction's read preference
|
161
|
+
# session.txn_read_preference
|
162
|
+
#
|
163
|
+
# @return [ Hash ] The read preference of the transaction.
|
50
164
|
#
|
51
165
|
# @since 2.6.0
|
52
|
-
|
166
|
+
def txn_read_preference
|
167
|
+
rp = txn_options[:read] ||
|
168
|
+
@client.read_preference
|
169
|
+
Mongo::Lint.validate_underscore_read_preference(rp)
|
170
|
+
rp
|
171
|
+
end
|
172
|
+
|
173
|
+
# Whether this session has ended.
|
174
|
+
#
|
175
|
+
# @example
|
176
|
+
# session.ended?
|
177
|
+
#
|
178
|
+
# @return [ true, false ] Whether the session has ended.
|
179
|
+
#
|
180
|
+
# @since 2.5.0
|
181
|
+
def ended?
|
182
|
+
@server_session.nil?
|
183
|
+
end
|
184
|
+
|
185
|
+
# Get the server session id of this session, if the session was not ended.
|
186
|
+
# If the session was ended, returns nil.
|
187
|
+
#
|
188
|
+
# @example Get the session id.
|
189
|
+
# session.session_id
|
190
|
+
#
|
191
|
+
# @return [ BSON::Document ] The server session id.
|
192
|
+
#
|
193
|
+
# @since 2.5.0
|
194
|
+
def session_id
|
195
|
+
if ended?
|
196
|
+
raise Error::SessionEnded
|
197
|
+
end
|
198
|
+
|
199
|
+
@server_session.session_id
|
200
|
+
end
|
201
|
+
|
202
|
+
# @return [ Server | nil ] The server (which should be a mongos) that this
|
203
|
+
# session is pinned to, if any.
|
204
|
+
#
|
205
|
+
# @api private
|
206
|
+
attr_reader :pinned_server
|
207
|
+
|
208
|
+
# @return [ BSON::Document | nil ] Recovery token for the sharded
|
209
|
+
# transaction being executed on this session, if any.
|
210
|
+
#
|
211
|
+
# @api private
|
212
|
+
attr_accessor :recovery_token
|
53
213
|
|
54
214
|
# Error message indicating that the session was retrieved from a client with a different cluster than that of the
|
55
215
|
# client through which it is currently being used.
|
@@ -98,53 +258,12 @@ module Mongo
|
|
98
258
|
# @since 2.6.0
|
99
259
|
TRANSACTION_ABORTED_STATE = :transaction_aborted
|
100
260
|
|
261
|
+
# @api private
|
101
262
|
UNLABELED_WRITE_CONCERN_CODES = [
|
102
263
|
79, # UnknownReplWriteConcern
|
103
264
|
100, # CannotSatisfyWriteConcern,
|
104
265
|
].freeze
|
105
266
|
|
106
|
-
# Initialize a Session.
|
107
|
-
#
|
108
|
-
# @note Applications should use Client#start_session to begin a session.
|
109
|
-
#
|
110
|
-
# @example
|
111
|
-
# Session.new(server_session, client, options)
|
112
|
-
#
|
113
|
-
# @param [ ServerSession ] server_session The server session this session is associated with.
|
114
|
-
# @param [ Client ] client The client through which this session is created.
|
115
|
-
# @param [ Hash ] options The options for this session.
|
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.
|
124
|
-
# @option options [ Hash ] :read_preference The read preference options hash,
|
125
|
-
# with the following optional keys:
|
126
|
-
# - *:mode* -- the read preference as a string or symbol; valid values are
|
127
|
-
# *:primary*, *:primary_preferred*, *:secondary*, *:secondary_preferred*
|
128
|
-
# and *:nearest*.
|
129
|
-
#
|
130
|
-
# @since 2.5.0
|
131
|
-
# @api private
|
132
|
-
def initialize(server_session, client, options = {})
|
133
|
-
@server_session = server_session
|
134
|
-
options = options.dup
|
135
|
-
|
136
|
-
# Because the read preference will need to be inserted into a command as a string, we convert
|
137
|
-
# it from a symbol immediately upon receiving it.
|
138
|
-
if options[:read_preference] && options[:read_preference][:mode]
|
139
|
-
options[:read_preference][:mode] = options[:read_preference][:mode].to_s
|
140
|
-
end
|
141
|
-
|
142
|
-
@client = client.use(:admin)
|
143
|
-
@options = options.freeze
|
144
|
-
@cluster_time = nil
|
145
|
-
@state = NO_TRANSACTION_STATE
|
146
|
-
end
|
147
|
-
|
148
267
|
# Get a formatted string for use in inspection.
|
149
268
|
#
|
150
269
|
# @example Inspect the session object.
|
@@ -159,6 +278,16 @@ module Mongo
|
|
159
278
|
|
160
279
|
# End this session.
|
161
280
|
#
|
281
|
+
# If there is an in-progress transaction on this session, the transaction
|
282
|
+
# is aborted. The server session associated with this session is returned
|
283
|
+
# to the server session pool. Finally, this session is marked ended and
|
284
|
+
# is no longer usable.
|
285
|
+
#
|
286
|
+
# If this session is already ended, this method does nothing.
|
287
|
+
#
|
288
|
+
# Note that this method does not directly issue an endSessions command
|
289
|
+
# to this server, contrary to what its name might suggest.
|
290
|
+
#
|
162
291
|
# @example
|
163
292
|
# session.end_session
|
164
293
|
#
|
@@ -179,377 +308,142 @@ module Mongo
|
|
179
308
|
@server_session = nil
|
180
309
|
end
|
181
310
|
|
182
|
-
#
|
183
|
-
#
|
184
|
-
# @example
|
185
|
-
# session.ended?
|
311
|
+
# Executes the provided block in a transaction, retrying as necessary.
|
186
312
|
#
|
187
|
-
#
|
313
|
+
# Returns the return value of the block.
|
188
314
|
#
|
189
|
-
#
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
#
|
315
|
+
# Exact number of retries and when they are performed are implementation
|
316
|
+
# details of the driver; the provided block should be idempotent, and
|
317
|
+
# should be prepared to be called more than once. The driver may retry
|
318
|
+
# the commit command within an active transaction or it may repeat the
|
319
|
+
# transaction and invoke the block again, depending on the error
|
320
|
+
# encountered if any. Note also that the retries may be executed against
|
321
|
+
# different servers.
|
195
322
|
#
|
196
|
-
#
|
197
|
-
#
|
323
|
+
# Transactions cannot be nested - InvalidTransactionOperation will be raised
|
324
|
+
# if this method is called when the session already has an active transaction.
|
198
325
|
#
|
199
|
-
#
|
326
|
+
# Exceptions raised by the block which are not derived from Mongo::Error
|
327
|
+
# stop processing, abort the transaction and are propagated out of
|
328
|
+
# with_transaction. Exceptions derived from Mongo::Error may be
|
329
|
+
# handled by with_transaction, resulting in retries of the process.
|
200
330
|
#
|
201
|
-
#
|
202
|
-
#
|
203
|
-
|
204
|
-
|
205
|
-
c[:autocommit] = false if in_transaction?
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
# Add this session's id to a command document.
|
331
|
+
# Currently, with_transaction will retry commits and block invocations
|
332
|
+
# until at least 120 seconds have passed since with_transaction started
|
333
|
+
# executing. This timeout is not configurable and may change in a future
|
334
|
+
# driver version.
|
210
335
|
#
|
211
|
-
# @
|
212
|
-
#
|
336
|
+
# @note with_transaction contains a loop, therefore the if with_transaction
|
337
|
+
# itself is placed in a loop, its block should not call next or break to
|
338
|
+
# control the outer loop because this will instead affect the loop in
|
339
|
+
# with_transaction. The driver will warn and abort the transaction
|
340
|
+
# if it detects this situation.
|
213
341
|
#
|
214
|
-
# @
|
342
|
+
# @example Execute a statement in a transaction
|
343
|
+
# session.with_transaction(write_concern: {w: :majority}) do
|
344
|
+
# collection.update_one({ id: 3 }, { '$set' => { status: 'Inactive'} },
|
345
|
+
# session: session)
|
215
346
|
#
|
216
|
-
#
|
217
|
-
# @api private
|
218
|
-
def add_id!(command)
|
219
|
-
command.merge!(lsid: session_id)
|
220
|
-
end
|
221
|
-
|
222
|
-
# Add the startTransaction field to a command document if applicable.
|
347
|
+
# end
|
223
348
|
#
|
224
|
-
# @example
|
225
|
-
#
|
226
|
-
#
|
227
|
-
#
|
228
|
-
#
|
229
|
-
# @since 2.6.0
|
230
|
-
# @api private
|
231
|
-
def add_start_transaction!(command)
|
232
|
-
command.tap do |c|
|
233
|
-
if starting_transaction?
|
234
|
-
c[:startTransaction] = true
|
235
|
-
end
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
# Add the transaction number to a command document if applicable.
|
240
|
-
#
|
241
|
-
# @example
|
242
|
-
# session.add_txn_num!(cmd)
|
243
|
-
#
|
244
|
-
# @return [ Hash, BSON::Document ] The command document.
|
349
|
+
# @example Execute a statement in a transaction, limiting total time consumed
|
350
|
+
# Timeout.timeout(5) do
|
351
|
+
# session.with_transaction(write_concern: {w: :majority}) do
|
352
|
+
# collection.update_one({ id: 3 }, { '$set' => { status: 'Inactive'} },
|
353
|
+
# session: session)
|
245
354
|
#
|
246
|
-
#
|
247
|
-
#
|
248
|
-
def add_txn_num!(command)
|
249
|
-
command.tap do |c|
|
250
|
-
c[:txnNumber] = BSON::Int64.new(@server_session.txn_num) if in_transaction?
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
# Add the transactions options if applicable.
|
355
|
+
# end
|
356
|
+
# end
|
255
357
|
#
|
256
|
-
# @
|
257
|
-
#
|
358
|
+
# @param [ Hash ] options The options for the transaction being started.
|
359
|
+
# These are the same options that start_transaction accepts.
|
258
360
|
#
|
259
|
-
# @
|
361
|
+
# @raise [ Error::InvalidTransactionOperation ] If a transaction is already in
|
362
|
+
# progress or if the write concern is unacknowledged.
|
260
363
|
#
|
261
|
-
# @since 2.
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
Mongo::Lint.validate_camel_case_read_preference(txn_read_pref)
|
271
|
-
c['$readPreference'] = txn_read_pref
|
272
|
-
end
|
273
|
-
|
274
|
-
# The read concern should be added to any command that starts a transaction.
|
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
|
297
|
-
end
|
298
|
-
|
299
|
-
# We need to send the read concern level as a string rather than a symbol.
|
300
|
-
if c[:readConcern] && c[:readConcern][:level]
|
301
|
-
c[:readConcern][:level] = c[:readConcern][:level].to_s
|
364
|
+
# @since 2.7.0
|
365
|
+
def with_transaction(options=nil)
|
366
|
+
# Non-configurable 120 second timeout for the entire operation
|
367
|
+
deadline = Time.now + 120
|
368
|
+
transaction_in_progress = false
|
369
|
+
loop do
|
370
|
+
commit_options = {}
|
371
|
+
if options
|
372
|
+
commit_options[:write_concern] = options[:write_concern]
|
302
373
|
end
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
elsif txn_write_concern
|
312
|
-
c[:writeConcern] ||= txn_write_concern
|
374
|
+
start_transaction(options)
|
375
|
+
transaction_in_progress = true
|
376
|
+
begin
|
377
|
+
rv = yield self
|
378
|
+
rescue Exception => e
|
379
|
+
if within_states?(STARTING_TRANSACTION_STATE, TRANSACTION_IN_PROGRESS_STATE)
|
380
|
+
abort_transaction
|
381
|
+
transaction_in_progress = false
|
313
382
|
end
|
314
|
-
end
|
315
|
-
|
316
|
-
# A non-numeric write concern w value needs to be sent as a string rather than a symbol.
|
317
|
-
if c[:writeConcern] && c[:writeConcern][:w] && c[:writeConcern][:w].is_a?(Symbol)
|
318
|
-
c[:writeConcern][:w] = c[:writeConcern][:w].to_s
|
319
|
-
end
|
320
|
-
end
|
321
|
-
end
|
322
|
-
|
323
|
-
# Remove the read concern and/or write concern from the command if not applicable.
|
324
|
-
#
|
325
|
-
# @example
|
326
|
-
# session.suppress_read_write_concern!(cmd)
|
327
|
-
#
|
328
|
-
# @return [ Hash, BSON::Document ] The command document.
|
329
|
-
#
|
330
|
-
# @since 2.6.0
|
331
|
-
# @api private
|
332
|
-
def suppress_read_write_concern!(command)
|
333
|
-
command.tap do |c|
|
334
|
-
next unless in_transaction?
|
335
|
-
|
336
|
-
c.delete(:readConcern) unless starting_transaction?
|
337
|
-
c.delete(:writeConcern) unless c[:commitTransaction] || c[:abortTransaction]
|
338
|
-
end
|
339
|
-
end
|
340
|
-
|
341
|
-
# Ensure that the read preference of a command primary.
|
342
|
-
#
|
343
|
-
# @example
|
344
|
-
# session.validate_read_preference!(command)
|
345
|
-
#
|
346
|
-
# @raise [ Mongo::Error::InvalidTransactionOperation ] If the read preference of the command is
|
347
|
-
# not primary.
|
348
|
-
#
|
349
|
-
# @since 2.6.0
|
350
|
-
# @api private
|
351
|
-
def validate_read_preference!(command)
|
352
|
-
return unless in_transaction? && non_primary_read_preference_mode?(command)
|
353
|
-
|
354
|
-
raise Mongo::Error::InvalidTransactionOperation.new(
|
355
|
-
Mongo::Error::InvalidTransactionOperation::INVALID_READ_PREFERENCE)
|
356
|
-
end
|
357
|
-
|
358
|
-
# Update the state of the session due to a (non-commit and non-abort) operation being run.
|
359
|
-
#
|
360
|
-
# @since 2.6.0
|
361
|
-
# @api private
|
362
|
-
def update_state!
|
363
|
-
case @state
|
364
|
-
when STARTING_TRANSACTION_STATE
|
365
|
-
@state = TRANSACTION_IN_PROGRESS_STATE
|
366
|
-
when TRANSACTION_COMMITTED_STATE, TRANSACTION_ABORTED_STATE
|
367
|
-
@state = NO_TRANSACTION_STATE
|
368
|
-
end
|
369
|
-
end
|
370
|
-
|
371
|
-
# Validate the session.
|
372
|
-
#
|
373
|
-
# @example
|
374
|
-
# session.validate!(cluster)
|
375
|
-
#
|
376
|
-
# @param [ Cluster ] cluster The cluster the session is attempted to be used with.
|
377
|
-
#
|
378
|
-
# @return [ nil ] nil if the session is valid.
|
379
|
-
#
|
380
|
-
# @raise [ Mongo::Error::InvalidSession ] Raise error if the session is not valid.
|
381
|
-
#
|
382
|
-
# @since 2.5.0
|
383
|
-
# @api private
|
384
|
-
def validate!(cluster)
|
385
|
-
check_matching_cluster!(cluster)
|
386
|
-
check_if_ended!
|
387
|
-
self
|
388
|
-
end
|
389
|
-
|
390
|
-
# Process a response from the server that used this session.
|
391
|
-
#
|
392
|
-
# @example Process a response from the server.
|
393
|
-
# session.process(result)
|
394
|
-
#
|
395
|
-
# @param [ Operation::Result ] result The result from the operation.
|
396
|
-
#
|
397
|
-
# @return [ Operation::Result ] The result.
|
398
|
-
#
|
399
|
-
# @since 2.5.0
|
400
|
-
# @api private
|
401
|
-
def process(result)
|
402
|
-
unless implicit?
|
403
|
-
set_operation_time(result)
|
404
|
-
set_cluster_time(result)
|
405
|
-
end
|
406
|
-
@server_session.set_last_use!
|
407
|
-
result
|
408
|
-
end
|
409
|
-
|
410
|
-
# Advance the cached cluster time document for this session.
|
411
|
-
#
|
412
|
-
# @example Advance the cluster time.
|
413
|
-
# session.advance_cluster_time(doc)
|
414
|
-
#
|
415
|
-
# @param [ BSON::Document, Hash ] new_cluster_time The new cluster time.
|
416
|
-
#
|
417
|
-
# @return [ BSON::Document, Hash ] The new cluster time.
|
418
|
-
#
|
419
|
-
# @since 2.5.0
|
420
|
-
def advance_cluster_time(new_cluster_time)
|
421
|
-
if @cluster_time
|
422
|
-
@cluster_time = [ @cluster_time, new_cluster_time ].max_by { |doc| doc[Cluster::CLUSTER_TIME] }
|
423
|
-
else
|
424
|
-
@cluster_time = new_cluster_time
|
425
|
-
end
|
426
|
-
end
|
427
|
-
|
428
|
-
# Advance the cached operation time for this session.
|
429
|
-
#
|
430
|
-
# @example Advance the operation time.
|
431
|
-
# session.advance_operation_time(timestamp)
|
432
|
-
#
|
433
|
-
# @param [ BSON::Timestamp ] new_operation_time The new operation time.
|
434
|
-
#
|
435
|
-
# @return [ BSON::Timestamp ] The max operation time, considering the current and new times.
|
436
|
-
#
|
437
|
-
# @since 2.5.0
|
438
|
-
def advance_operation_time(new_operation_time)
|
439
|
-
if @operation_time
|
440
|
-
@operation_time = [ @operation_time, new_operation_time ].max
|
441
|
-
else
|
442
|
-
@operation_time = new_operation_time
|
443
|
-
end
|
444
|
-
end
|
445
|
-
|
446
|
-
# Whether reads executed with this session can be retried according to
|
447
|
-
# the modern retryable reads specification.
|
448
|
-
#
|
449
|
-
# If this method returns true, the modern retryable reads have been
|
450
|
-
# requested by the application. If the server selected for a read operation
|
451
|
-
# supports modern retryable reads, they will be used for that particular
|
452
|
-
# operation. If the server selected for a read operation does not support
|
453
|
-
# modern retryable reads, the read will not be retried.
|
454
|
-
#
|
455
|
-
# If this method returns false, legacy retryable reads have been requested
|
456
|
-
# by the application. Legacy retryable read logic will be used regardless
|
457
|
-
# of server version of the server(s) that the client is connected to.
|
458
|
-
# The number of read retries is given by :max_read_retries client option,
|
459
|
-
# which is 1 by default and can be set to 0 to disable legacy read retries.
|
460
|
-
#
|
461
|
-
# @api private
|
462
|
-
def retry_reads?
|
463
|
-
client.options[:retry_reads] != false
|
464
|
-
end
|
465
|
-
|
466
|
-
# Will writes executed with this session be retried.
|
467
|
-
#
|
468
|
-
# @example Will writes be retried.
|
469
|
-
# session.retry_writes?
|
470
|
-
#
|
471
|
-
# @return [ true, false ] If writes will be retried.
|
472
|
-
#
|
473
|
-
# @note Retryable writes are only available on server versions at least 3.6
|
474
|
-
# and with sharded clusters or replica sets.
|
475
|
-
#
|
476
|
-
# @since 2.5.0
|
477
|
-
def retry_writes?
|
478
|
-
!!client.options[:retry_writes] && (cluster.replica_set? || cluster.sharded?)
|
479
|
-
end
|
480
|
-
|
481
|
-
# Get the server session id of this session, if the session was not ended.
|
482
|
-
# If the session was ended, returns nil.
|
483
|
-
#
|
484
|
-
# @example Get the session id.
|
485
|
-
# session.session_id
|
486
|
-
#
|
487
|
-
# @return [ BSON::Document ] The server session id.
|
488
|
-
#
|
489
|
-
# @since 2.5.0
|
490
|
-
def session_id
|
491
|
-
if ended?
|
492
|
-
raise Error::SessionEnded
|
493
|
-
end
|
494
|
-
|
495
|
-
@server_session.session_id
|
496
|
-
end
|
497
|
-
|
498
|
-
# Increment and return the next transaction number.
|
499
|
-
#
|
500
|
-
# @example Get the next transaction number.
|
501
|
-
# session.next_txn_num
|
502
|
-
#
|
503
|
-
# @return [ Integer ] The next transaction number.
|
504
|
-
#
|
505
|
-
# @since 2.5.0
|
506
|
-
# @api private
|
507
|
-
def next_txn_num
|
508
|
-
if ended?
|
509
|
-
raise Error::SessionEnded
|
510
|
-
end
|
511
|
-
|
512
|
-
@server_session.next_txn_num
|
513
|
-
end
|
514
383
|
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
#
|
520
|
-
# @return [ Integer ] The current transaction number.
|
521
|
-
#
|
522
|
-
# @since 2.6.0
|
523
|
-
def txn_num
|
524
|
-
if ended?
|
525
|
-
raise Error::SessionEnded
|
526
|
-
end
|
384
|
+
if Time.now >= deadline
|
385
|
+
transaction_in_progress = false
|
386
|
+
raise
|
387
|
+
end
|
527
388
|
|
528
|
-
|
529
|
-
|
389
|
+
if e.is_a?(Mongo::Error) && e.label?('TransientTransactionError')
|
390
|
+
next
|
391
|
+
end
|
530
392
|
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
#
|
538
|
-
# @since 2.5.1
|
539
|
-
def implicit?
|
540
|
-
@implicit ||= !!(@options.key?(:implicit) && @options[:implicit] == true)
|
541
|
-
end
|
393
|
+
raise
|
394
|
+
else
|
395
|
+
if within_states?(TRANSACTION_ABORTED_STATE, NO_TRANSACTION_STATE, TRANSACTION_COMMITTED_STATE)
|
396
|
+
transaction_in_progress = false
|
397
|
+
return rv
|
398
|
+
end
|
542
399
|
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
400
|
+
begin
|
401
|
+
commit_transaction(commit_options)
|
402
|
+
transaction_in_progress = false
|
403
|
+
return rv
|
404
|
+
rescue Mongo::Error => e
|
405
|
+
if e.label?('UnknownTransactionCommitResult')
|
406
|
+
if Time.now >= deadline ||
|
407
|
+
e.is_a?(Error::OperationFailure) && e.max_time_ms_expired?
|
408
|
+
then
|
409
|
+
transaction_in_progress = false
|
410
|
+
raise
|
411
|
+
end
|
412
|
+
wc_options = case v = commit_options[:write_concern]
|
413
|
+
when WriteConcern::Base
|
414
|
+
v.options
|
415
|
+
when nil
|
416
|
+
{}
|
417
|
+
else
|
418
|
+
v
|
419
|
+
end
|
420
|
+
commit_options[:write_concern] = wc_options.merge(w: :majority)
|
421
|
+
retry
|
422
|
+
elsif e.label?('TransientTransactionError')
|
423
|
+
if Time.now >= deadline
|
424
|
+
transaction_in_progress = false
|
425
|
+
raise
|
426
|
+
end
|
427
|
+
next
|
428
|
+
else
|
429
|
+
transaction_in_progress = false
|
430
|
+
raise
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
# No official return value, but return true so that in interactive
|
437
|
+
# use the method hints that it succeeded.
|
438
|
+
true
|
439
|
+
ensure
|
440
|
+
if transaction_in_progress
|
441
|
+
log_warn('with_transaction callback altered with_transaction loop, aborting transaction')
|
442
|
+
begin
|
443
|
+
abort_transaction
|
444
|
+
rescue Error::OperationFailure, Error::InvalidTransactionOperation
|
445
|
+
end
|
446
|
+
end
|
553
447
|
end
|
554
448
|
|
555
449
|
# Places subsequent operations in this session into a new transaction.
|
@@ -562,6 +456,8 @@ module Mongo
|
|
562
456
|
#
|
563
457
|
# @param [ Hash ] options The options for the transaction being started.
|
564
458
|
#
|
459
|
+
# @option options [ Integer ] :max_commit_time_ms The maximum amount of
|
460
|
+
# time to allow a single commitTransaction command to run, in milliseconds.
|
565
461
|
# @option options [ Hash ] read_concern The read concern options hash,
|
566
462
|
# with the following optional keys:
|
567
463
|
# - *:level* -- the read preference level as a symbol; valid values
|
@@ -580,6 +476,18 @@ module Mongo
|
|
580
476
|
def start_transaction(options = nil)
|
581
477
|
if options
|
582
478
|
Lint.validate_read_concern_option(options[:read_concern])
|
479
|
+
|
480
|
+
=begin
|
481
|
+
# It would be handy to detect invalid read preferences here, but
|
482
|
+
# some of the spec tests require later detection of invalid read prefs.
|
483
|
+
# Maybe we can do this when lint mode is on.
|
484
|
+
mode = options[:read] && options[:read][:mode].to_s
|
485
|
+
if mode && mode != 'primary'
|
486
|
+
raise Mongo::Error::InvalidTransactionOperation.new(
|
487
|
+
"read preference in a transaction must be primary (requested: #{mode})"
|
488
|
+
)
|
489
|
+
end
|
490
|
+
=end
|
583
491
|
end
|
584
492
|
|
585
493
|
check_if_ended!
|
@@ -589,16 +497,24 @@ module Mongo
|
|
589
497
|
Mongo::Error::InvalidTransactionOperation::TRANSACTION_ALREADY_IN_PROGRESS)
|
590
498
|
end
|
591
499
|
|
500
|
+
unpin
|
501
|
+
|
592
502
|
next_txn_num
|
593
|
-
@txn_options =
|
503
|
+
@txn_options = (@options[:default_transaction_options] || {}).merge(options || {})
|
594
504
|
|
595
|
-
if txn_write_concern && WriteConcern.
|
505
|
+
if txn_write_concern && !WriteConcern.get(txn_write_concern).acknowledged?
|
596
506
|
raise Mongo::Error::InvalidTransactionOperation.new(
|
597
507
|
Mongo::Error::InvalidTransactionOperation::UNACKNOWLEDGED_WRITE_CONCERN)
|
598
508
|
end
|
599
509
|
|
600
510
|
@state = STARTING_TRANSACTION_STATE
|
601
511
|
@already_committed = false
|
512
|
+
|
513
|
+
# This method has no explicit return value.
|
514
|
+
# We could return nil here but true indicates to the user that the
|
515
|
+
# operation succeeded. This is intended for interactive use.
|
516
|
+
# Note that the return value is not documented.
|
517
|
+
true
|
602
518
|
end
|
603
519
|
|
604
520
|
# Commit the currently active transaction on the session.
|
@@ -636,6 +552,7 @@ module Mongo
|
|
636
552
|
@last_commit_skipped = true
|
637
553
|
else
|
638
554
|
@last_commit_skipped = false
|
555
|
+
@committing_transaction = true
|
639
556
|
|
640
557
|
write_concern = options[:write_concern] || txn_options[:write_concern]
|
641
558
|
if write_concern && !write_concern.is_a?(WriteConcern::Base)
|
@@ -651,30 +568,24 @@ module Mongo
|
|
651
568
|
write_concern = WriteConcern.get(w: :majority, wtimeout: 10000)
|
652
569
|
end
|
653
570
|
end
|
654
|
-
|
571
|
+
spec = {
|
655
572
|
selector: { commitTransaction: 1 },
|
656
573
|
db_name: 'admin',
|
657
574
|
session: self,
|
658
575
|
txn_num: txn_num,
|
659
576
|
write_concern: write_concern,
|
660
|
-
|
577
|
+
}
|
578
|
+
Operation::Command.new(spec).execute(server)
|
661
579
|
end
|
662
580
|
end
|
663
|
-
rescue Mongo::Error::NoServerAvailable, Mongo::Error::SocketError => e
|
664
|
-
e.send(:add_label, Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)
|
665
|
-
raise e
|
666
|
-
rescue Mongo::Error::OperationFailure => e
|
667
|
-
err_doc = e.instance_variable_get(:@result).send(:first_document)
|
668
|
-
|
669
|
-
if e.write_retryable? || (err_doc['writeConcernError'] &&
|
670
|
-
!UNLABELED_WRITE_CONCERN_CODES.include?(err_doc['writeConcernError']['code']))
|
671
|
-
e.send(:add_label, Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)
|
672
|
-
end
|
673
|
-
|
674
|
-
raise e
|
675
581
|
ensure
|
676
582
|
@state = TRANSACTION_COMMITTED_STATE
|
583
|
+
@committing_transaction = false
|
677
584
|
end
|
585
|
+
|
586
|
+
# No official return value, but return true so that in interactive
|
587
|
+
# use the method hints that it succeeded.
|
588
|
+
true
|
678
589
|
end
|
679
590
|
|
680
591
|
# Abort the currently active transaction without making any changes to the database.
|
@@ -721,180 +632,365 @@ module Mongo
|
|
721
632
|
@state = TRANSACTION_ABORTED_STATE
|
722
633
|
raise
|
723
634
|
end
|
635
|
+
|
636
|
+
# No official return value, but return true so that in interactive
|
637
|
+
# use the method hints that it succeeded.
|
638
|
+
true
|
639
|
+
end
|
640
|
+
|
641
|
+
# @api private
|
642
|
+
def starting_transaction?
|
643
|
+
within_states?(STARTING_TRANSACTION_STATE)
|
644
|
+
end
|
645
|
+
|
646
|
+
# Whether or not the session is currently in a transaction.
|
647
|
+
#
|
648
|
+
# @example Is the session in a transaction?
|
649
|
+
# session.in_transaction?
|
650
|
+
#
|
651
|
+
# @return [ true | false ] Whether or not the session in a transaction.
|
652
|
+
#
|
653
|
+
# @since 2.6.0
|
654
|
+
def in_transaction?
|
655
|
+
within_states?(STARTING_TRANSACTION_STATE, TRANSACTION_IN_PROGRESS_STATE)
|
656
|
+
end
|
657
|
+
|
658
|
+
# @return [ true | false ] Whether the session is currently committing a
|
659
|
+
# transaction.
|
660
|
+
#
|
661
|
+
# @api private
|
662
|
+
def committing_transaction?
|
663
|
+
!!@committing_transaction
|
664
|
+
end
|
665
|
+
|
666
|
+
# Pins this session to the specified server, which should be a mongos.
|
667
|
+
#
|
668
|
+
# @param [ Server ] server The server to pin this session to.
|
669
|
+
#
|
670
|
+
# @api private
|
671
|
+
def pin(server)
|
672
|
+
if server.nil?
|
673
|
+
raise ArgumentError, 'Cannot pin to a nil server'
|
674
|
+
end
|
675
|
+
if Lint.enabled?
|
676
|
+
unless server.mongos?
|
677
|
+
raise Error::LintError, "Attempted to pin the session to server #{server.summary} which is not a mongos"
|
678
|
+
end
|
679
|
+
end
|
680
|
+
@pinned_server = server
|
681
|
+
end
|
682
|
+
|
683
|
+
# Unpins this session from the pinned server, if the session was pinned.
|
684
|
+
#
|
685
|
+
# @api private
|
686
|
+
def unpin
|
687
|
+
@pinned_server = nil
|
688
|
+
end
|
689
|
+
|
690
|
+
# Unpins this session from the pinned server, if the session was pinned
|
691
|
+
# and the specified exception instance and the session's transaction state
|
692
|
+
# require it to be unpinned.
|
693
|
+
#
|
694
|
+
# The exception instance should already have all of the labels set on it
|
695
|
+
# (both client- and server-side generated ones).
|
696
|
+
#
|
697
|
+
# @param [ Error ] The exception instance to process.
|
698
|
+
#
|
699
|
+
# @api private
|
700
|
+
def unpin_maybe(error)
|
701
|
+
if !within_states?(Session::NO_TRANSACTION_STATE) &&
|
702
|
+
error.label?('TransientTransactionError')
|
703
|
+
then
|
704
|
+
unpin
|
705
|
+
end
|
706
|
+
|
707
|
+
if committing_transaction? &&
|
708
|
+
error.label?('UnknownTransactionCommitResult')
|
709
|
+
then
|
710
|
+
unpin
|
711
|
+
end
|
712
|
+
end
|
713
|
+
|
714
|
+
# Add the autocommit field to a command document if applicable.
|
715
|
+
#
|
716
|
+
# @example
|
717
|
+
# session.add_autocommit!(cmd)
|
718
|
+
#
|
719
|
+
# @return [ Hash, BSON::Document ] The command document.
|
720
|
+
#
|
721
|
+
# @since 2.6.0
|
722
|
+
# @api private
|
723
|
+
def add_autocommit!(command)
|
724
|
+
command.tap do |c|
|
725
|
+
c[:autocommit] = false if in_transaction?
|
726
|
+
end
|
727
|
+
end
|
728
|
+
|
729
|
+
# Add this session's id to a command document.
|
730
|
+
#
|
731
|
+
# @example
|
732
|
+
# session.add_id!(cmd)
|
733
|
+
#
|
734
|
+
# @return [ Hash, BSON::Document ] The command document.
|
735
|
+
#
|
736
|
+
# @since 2.5.0
|
737
|
+
# @api private
|
738
|
+
def add_id!(command)
|
739
|
+
command.merge!(lsid: session_id)
|
740
|
+
end
|
741
|
+
|
742
|
+
# Add the startTransaction field to a command document if applicable.
|
743
|
+
#
|
744
|
+
# @example
|
745
|
+
# session.add_start_transaction!(cmd)
|
746
|
+
#
|
747
|
+
# @return [ Hash, BSON::Document ] The command document.
|
748
|
+
#
|
749
|
+
# @since 2.6.0
|
750
|
+
# @api private
|
751
|
+
def add_start_transaction!(command)
|
752
|
+
command.tap do |c|
|
753
|
+
if starting_transaction?
|
754
|
+
c[:startTransaction] = true
|
755
|
+
end
|
756
|
+
end
|
757
|
+
end
|
758
|
+
|
759
|
+
# Add the transaction number to a command document if applicable.
|
760
|
+
#
|
761
|
+
# @example
|
762
|
+
# session.add_txn_num!(cmd)
|
763
|
+
#
|
764
|
+
# @return [ Hash, BSON::Document ] The command document.
|
765
|
+
#
|
766
|
+
# @since 2.6.0
|
767
|
+
# @api private
|
768
|
+
def add_txn_num!(command)
|
769
|
+
command.tap do |c|
|
770
|
+
c[:txnNumber] = BSON::Int64.new(@server_session.txn_num) if in_transaction?
|
771
|
+
end
|
772
|
+
end
|
773
|
+
|
774
|
+
# Add the transactions options if applicable.
|
775
|
+
#
|
776
|
+
# @example
|
777
|
+
# session.add_txn_opts!(cmd)
|
778
|
+
#
|
779
|
+
# @return [ Hash, BSON::Document ] The command document.
|
780
|
+
#
|
781
|
+
# @since 2.6.0
|
782
|
+
# @api private
|
783
|
+
def add_txn_opts!(command, read)
|
784
|
+
command.tap do |c|
|
785
|
+
# The read concern should be added to any command that starts a transaction.
|
786
|
+
if starting_transaction?
|
787
|
+
# https://jira.mongodb.org/browse/SPEC-1161: transaction's
|
788
|
+
# read concern overrides collection/database/client read concerns,
|
789
|
+
# even if transaction's read concern is not set.
|
790
|
+
# Read concern here is the one sent to the server and may
|
791
|
+
# include afterClusterTime.
|
792
|
+
if rc = c[:readConcern]
|
793
|
+
rc = rc.dup
|
794
|
+
rc.delete(:level)
|
795
|
+
end
|
796
|
+
if txn_read_concern
|
797
|
+
if rc
|
798
|
+
rc.update(txn_read_concern)
|
799
|
+
else
|
800
|
+
rc = txn_read_concern.dup
|
801
|
+
end
|
802
|
+
end
|
803
|
+
if rc.nil? || rc.empty?
|
804
|
+
c.delete(:readConcern)
|
805
|
+
else
|
806
|
+
c[:readConcern ] = Options::Mapper.transform_values_to_strings(rc)
|
807
|
+
end
|
808
|
+
end
|
809
|
+
|
810
|
+
# We need to send the read concern level as a string rather than a symbol.
|
811
|
+
if c[:readConcern]
|
812
|
+
c[:readConcern] = Options::Mapper.transform_values_to_strings(c[:readConcern])
|
813
|
+
end
|
814
|
+
|
815
|
+
if c[:commitTransaction]
|
816
|
+
if max_time_ms = txn_options[:max_commit_time_ms]
|
817
|
+
c[:maxTimeMS] = max_time_ms
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
# The write concern should be added to any abortTransaction or commitTransaction command.
|
822
|
+
if (c[:abortTransaction] || c[:commitTransaction])
|
823
|
+
if @already_committed
|
824
|
+
wc = BSON::Document.new(c[:writeConcern] || txn_write_concern || {})
|
825
|
+
wc.merge!(w: :majority)
|
826
|
+
wc[:wtimeout] ||= 10000
|
827
|
+
c[:writeConcern] = wc
|
828
|
+
elsif txn_write_concern
|
829
|
+
c[:writeConcern] ||= txn_write_concern
|
830
|
+
end
|
831
|
+
end
|
832
|
+
|
833
|
+
# A non-numeric write concern w value needs to be sent as a string rather than a symbol.
|
834
|
+
if c[:writeConcern] && c[:writeConcern][:w] && c[:writeConcern][:w].is_a?(Symbol)
|
835
|
+
c[:writeConcern][:w] = c[:writeConcern][:w].to_s
|
836
|
+
end
|
837
|
+
end
|
724
838
|
end
|
725
839
|
|
726
|
-
#
|
840
|
+
# Remove the read concern and/or write concern from the command if not applicable.
|
727
841
|
#
|
728
|
-
# @example
|
729
|
-
# session.
|
842
|
+
# @example
|
843
|
+
# session.suppress_read_write_concern!(cmd)
|
730
844
|
#
|
731
|
-
# @return [
|
845
|
+
# @return [ Hash, BSON::Document ] The command document.
|
732
846
|
#
|
733
847
|
# @since 2.6.0
|
734
|
-
|
735
|
-
|
848
|
+
# @api private
|
849
|
+
def suppress_read_write_concern!(command)
|
850
|
+
command.tap do |c|
|
851
|
+
next unless in_transaction?
|
852
|
+
|
853
|
+
c.delete(:readConcern) unless starting_transaction?
|
854
|
+
c.delete(:writeConcern) unless c[:commitTransaction] || c[:abortTransaction]
|
855
|
+
end
|
736
856
|
end
|
737
857
|
|
738
|
-
#
|
858
|
+
# Ensure that the read preference of a command primary.
|
739
859
|
#
|
740
|
-
#
|
860
|
+
# @example
|
861
|
+
# session.validate_read_preference!(command)
|
741
862
|
#
|
742
|
-
#
|
743
|
-
#
|
744
|
-
# should be prepared to be called more than once. The driver may retry
|
745
|
-
# the commit command within an active transaction or it may repeat the
|
746
|
-
# transaction and invoke the block again, depending on the error
|
747
|
-
# encountered if any. Note also that the retries may be executed against
|
748
|
-
# different servers.
|
863
|
+
# @raise [ Mongo::Error::InvalidTransactionOperation ] If the read preference of the command is
|
864
|
+
# not primary.
|
749
865
|
#
|
750
|
-
#
|
751
|
-
#
|
866
|
+
# @since 2.6.0
|
867
|
+
# @api private
|
868
|
+
def validate_read_preference!(command)
|
869
|
+
return unless in_transaction?
|
870
|
+
return unless command['$readPreference']
|
871
|
+
|
872
|
+
mode = command['$readPreference']['mode'] || command['$readPreference'][:mode]
|
873
|
+
|
874
|
+
if mode && mode != 'primary'
|
875
|
+
raise Mongo::Error::InvalidTransactionOperation.new(
|
876
|
+
"read preference in a transaction must be primary (requested: #{mode})"
|
877
|
+
)
|
878
|
+
end
|
879
|
+
end
|
880
|
+
|
881
|
+
# Update the state of the session due to a (non-commit and non-abort) operation being run.
|
752
882
|
#
|
753
|
-
#
|
754
|
-
#
|
755
|
-
|
756
|
-
|
883
|
+
# @since 2.6.0
|
884
|
+
# @api private
|
885
|
+
def update_state!
|
886
|
+
case @state
|
887
|
+
when STARTING_TRANSACTION_STATE
|
888
|
+
@state = TRANSACTION_IN_PROGRESS_STATE
|
889
|
+
when TRANSACTION_COMMITTED_STATE, TRANSACTION_ABORTED_STATE
|
890
|
+
@state = NO_TRANSACTION_STATE
|
891
|
+
end
|
892
|
+
end
|
893
|
+
|
894
|
+
# Validate the session.
|
757
895
|
#
|
758
|
-
#
|
759
|
-
#
|
760
|
-
# executing. This timeout is not configurable and may change in a future
|
761
|
-
# driver version.
|
896
|
+
# @example
|
897
|
+
# session.validate!(cluster)
|
762
898
|
#
|
763
|
-
# @
|
764
|
-
# itself is placed in a loop, its block should not call next or break to
|
765
|
-
# control the outer loop because this will instead affect the loop in
|
766
|
-
# with_transaction. The driver will warn and abort the transaction
|
767
|
-
# if it detects this situation.
|
899
|
+
# @param [ Cluster ] cluster The cluster the session is attempted to be used with.
|
768
900
|
#
|
769
|
-
# @
|
770
|
-
# session.with_transaction(write_concern: {w: :majority}) do
|
771
|
-
# collection.update_one({ id: 3 }, { '$set' => { status: 'Inactive'} },
|
772
|
-
# session: session)
|
901
|
+
# @return [ nil ] nil if the session is valid.
|
773
902
|
#
|
774
|
-
#
|
903
|
+
# @raise [ Mongo::Error::InvalidSession ] Raise error if the session is not valid.
|
775
904
|
#
|
776
|
-
# @
|
777
|
-
#
|
778
|
-
|
779
|
-
|
780
|
-
|
905
|
+
# @since 2.5.0
|
906
|
+
# @api private
|
907
|
+
def validate!(cluster)
|
908
|
+
check_matching_cluster!(cluster)
|
909
|
+
check_if_ended!
|
910
|
+
self
|
911
|
+
end
|
912
|
+
|
913
|
+
# Process a response from the server that used this session.
|
781
914
|
#
|
782
|
-
#
|
783
|
-
#
|
915
|
+
# @example Process a response from the server.
|
916
|
+
# session.process(result)
|
784
917
|
#
|
785
|
-
# @param [
|
786
|
-
# These are the same options that start_transaction accepts.
|
918
|
+
# @param [ Operation::Result ] result The result from the operation.
|
787
919
|
#
|
788
|
-
# @
|
789
|
-
# progress or if the write concern is unacknowledged.
|
920
|
+
# @return [ Operation::Result ] The result.
|
790
921
|
#
|
791
|
-
# @since 2.
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
if options
|
799
|
-
commit_options[:write_concern] = options[:write_concern]
|
922
|
+
# @since 2.5.0
|
923
|
+
# @api private
|
924
|
+
def process(result)
|
925
|
+
unless implicit?
|
926
|
+
set_operation_time(result)
|
927
|
+
if cluster_time_doc = result.cluster_time
|
928
|
+
advance_cluster_time(cluster_time_doc)
|
800
929
|
end
|
801
|
-
|
802
|
-
|
803
|
-
begin
|
804
|
-
rv = yield self
|
805
|
-
rescue Exception => e
|
806
|
-
if within_states?(STARTING_TRANSACTION_STATE, TRANSACTION_IN_PROGRESS_STATE)
|
807
|
-
abort_transaction
|
808
|
-
transaction_in_progress = false
|
809
|
-
end
|
810
|
-
|
811
|
-
if Time.now >= deadline
|
812
|
-
transaction_in_progress = false
|
813
|
-
raise
|
814
|
-
end
|
815
|
-
|
816
|
-
if e.is_a?(Mongo::Error) && e.label?(Mongo::Error::TRANSIENT_TRANSACTION_ERROR_LABEL)
|
817
|
-
next
|
818
|
-
end
|
819
|
-
|
820
|
-
raise
|
821
|
-
else
|
822
|
-
if within_states?(TRANSACTION_ABORTED_STATE, NO_TRANSACTION_STATE, TRANSACTION_COMMITTED_STATE)
|
823
|
-
transaction_in_progress = false
|
824
|
-
return rv
|
825
|
-
end
|
930
|
+
end
|
931
|
+
@server_session.set_last_use!
|
826
932
|
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
return rv
|
831
|
-
rescue Mongo::Error => e
|
832
|
-
if e.label?(Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)
|
833
|
-
# WriteConcernFailed
|
834
|
-
if e.is_a?(Mongo::Error::OperationFailure) && e.code == 64 && e.wtimeout?
|
835
|
-
transaction_in_progress = false
|
836
|
-
raise
|
837
|
-
end
|
838
|
-
if Time.now >= deadline
|
839
|
-
transaction_in_progress = false
|
840
|
-
raise
|
841
|
-
end
|
842
|
-
wc_options = case v = commit_options[:write_concern]
|
843
|
-
when WriteConcern::Base
|
844
|
-
v.options
|
845
|
-
when nil
|
846
|
-
{}
|
847
|
-
else
|
848
|
-
v
|
849
|
-
end
|
850
|
-
commit_options[:write_concern] = wc_options.merge(w: :majority)
|
851
|
-
retry
|
852
|
-
elsif e.label?(Mongo::Error::TRANSIENT_TRANSACTION_ERROR_LABEL)
|
853
|
-
if Time.now >= deadline
|
854
|
-
transaction_in_progress = false
|
855
|
-
raise
|
856
|
-
end
|
857
|
-
next
|
858
|
-
else
|
859
|
-
transaction_in_progress = false
|
860
|
-
raise
|
861
|
-
end
|
862
|
-
end
|
933
|
+
if doc = result.reply && result.reply.documents.first
|
934
|
+
if doc[:recoveryToken]
|
935
|
+
self.recovery_token = doc[:recoveryToken]
|
863
936
|
end
|
864
937
|
end
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
938
|
+
|
939
|
+
result
|
940
|
+
end
|
941
|
+
|
942
|
+
# Advance the cached operation time for this session.
|
943
|
+
#
|
944
|
+
# @example Advance the operation time.
|
945
|
+
# session.advance_operation_time(timestamp)
|
946
|
+
#
|
947
|
+
# @param [ BSON::Timestamp ] new_operation_time The new operation time.
|
948
|
+
#
|
949
|
+
# @return [ BSON::Timestamp ] The max operation time, considering the current and new times.
|
950
|
+
#
|
951
|
+
# @since 2.5.0
|
952
|
+
def advance_operation_time(new_operation_time)
|
953
|
+
if @operation_time
|
954
|
+
@operation_time = [ @operation_time, new_operation_time ].max
|
955
|
+
else
|
956
|
+
@operation_time = new_operation_time
|
872
957
|
end
|
873
958
|
end
|
874
959
|
|
875
|
-
#
|
876
|
-
# active transaction.
|
960
|
+
# Increment and return the next transaction number.
|
877
961
|
#
|
878
|
-
#
|
962
|
+
# @example Get the next transaction number.
|
963
|
+
# session.next_txn_num
|
879
964
|
#
|
880
|
-
# @
|
881
|
-
# session.txn_read_preference
|
965
|
+
# @return [ Integer ] The next transaction number.
|
882
966
|
#
|
883
|
-
# @
|
967
|
+
# @since 2.5.0
|
968
|
+
# @api private
|
969
|
+
def next_txn_num
|
970
|
+
if ended?
|
971
|
+
raise Error::SessionEnded
|
972
|
+
end
|
973
|
+
|
974
|
+
@server_session.next_txn_num
|
975
|
+
end
|
976
|
+
|
977
|
+
# Get the current transaction number.
|
978
|
+
#
|
979
|
+
# @example Get the current transaction number.
|
980
|
+
# session.txn_num
|
981
|
+
#
|
982
|
+
# @return [ Integer ] The current transaction number.
|
884
983
|
#
|
885
984
|
# @since 2.6.0
|
886
|
-
def
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
rp
|
891
|
-
end
|
985
|
+
def txn_num
|
986
|
+
if ended?
|
987
|
+
raise Error::SessionEnded
|
988
|
+
end
|
892
989
|
|
893
|
-
|
894
|
-
@client.cluster
|
990
|
+
@server_session.txn_num
|
895
991
|
end
|
896
992
|
|
897
|
-
|
993
|
+
private
|
898
994
|
|
899
995
|
# Get the read concern the session will use when starting a transaction.
|
900
996
|
#
|
@@ -908,19 +1004,13 @@ module Mongo
|
|
908
1004
|
# @since 2.9.0
|
909
1005
|
def txn_read_concern
|
910
1006
|
# Read concern is inherited from client but not db or collection.
|
911
|
-
txn_options
|
1007
|
+
txn_options[:read_concern] || @client.read_concern
|
912
1008
|
end
|
913
1009
|
|
914
|
-
private
|
915
|
-
|
916
1010
|
def within_states?(*states)
|
917
1011
|
states.include?(@state)
|
918
1012
|
end
|
919
1013
|
|
920
|
-
def starting_transaction?
|
921
|
-
within_states?(STARTING_TRANSACTION_STATE)
|
922
|
-
end
|
923
|
-
|
924
1014
|
def check_if_no_transaction!
|
925
1015
|
return unless within_states?(NO_TRANSACTION_STATE)
|
926
1016
|
|
@@ -929,17 +1019,10 @@ module Mongo
|
|
929
1019
|
end
|
930
1020
|
|
931
1021
|
def txn_write_concern
|
932
|
-
|
1022
|
+
txn_options[:write_concern] ||
|
933
1023
|
(@client.write_concern && @client.write_concern.options)
|
934
1024
|
end
|
935
1025
|
|
936
|
-
def non_primary_read_preference_mode?(command)
|
937
|
-
return false unless command['$readPreference']
|
938
|
-
|
939
|
-
mode = command['$readPreference']['mode'] || command['$readPreference'][:mode]
|
940
|
-
mode && mode != 'primary'
|
941
|
-
end
|
942
|
-
|
943
1026
|
# Returns causal consistency document if the last operation time is
|
944
1027
|
# known and causal consistency is enabled, otherwise returns nil.
|
945
1028
|
def causal_consistency_doc
|
@@ -964,16 +1047,6 @@ module Mongo
|
|
964
1047
|
end
|
965
1048
|
end
|
966
1049
|
|
967
|
-
def set_cluster_time(result)
|
968
|
-
if cluster_time_doc = result.cluster_time
|
969
|
-
if @cluster_time.nil?
|
970
|
-
@cluster_time = cluster_time_doc
|
971
|
-
elsif cluster_time_doc[Cluster::CLUSTER_TIME] > @cluster_time[Cluster::CLUSTER_TIME]
|
972
|
-
@cluster_time = cluster_time_doc
|
973
|
-
end
|
974
|
-
end
|
975
|
-
end
|
976
|
-
|
977
1050
|
def check_if_ended!
|
978
1051
|
raise Mongo::Error::InvalidSession.new(SESSION_ENDED_ERROR_MSG) if ended?
|
979
1052
|
end
|