mongo 2.5.0 → 2.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/README.md +2 -0
  5. data/Rakefile +4 -1
  6. data/lib/mongo/address.rb +2 -1
  7. data/lib/mongo/client.rb +6 -51
  8. data/lib/mongo/cluster.rb +34 -4
  9. data/lib/mongo/cluster/reapers/socket_reaper.rb +1 -1
  10. data/lib/mongo/cluster/topology/replica_set.rb +3 -1
  11. data/lib/mongo/collection.rb +6 -6
  12. data/lib/mongo/collection/view.rb +2 -4
  13. data/lib/mongo/cursor.rb +9 -4
  14. data/lib/mongo/error.rb +2 -0
  15. data/lib/mongo/operation/uses_command_op_msg.rb +1 -1
  16. data/lib/mongo/server.rb +3 -0
  17. data/lib/mongo/server/description.rb +1 -1
  18. data/lib/mongo/server/description/features.rb +18 -12
  19. data/lib/mongo/server_selector/selectable.rb +5 -1
  20. data/lib/mongo/session.rb +38 -43
  21. data/lib/mongo/session/session_pool.rb +12 -30
  22. data/lib/mongo/socket.rb +24 -0
  23. data/lib/mongo/socket/tcp.rb +0 -1
  24. data/lib/mongo/uri.rb +26 -5
  25. data/lib/mongo/version.rb +1 -1
  26. data/spec/mongo/address_spec.rb +37 -2
  27. data/spec/mongo/bulk_write_spec.rb +4 -10
  28. data/spec/mongo/change_stream_examples_spec.rb +40 -0
  29. data/spec/mongo/client_spec.rb +47 -12
  30. data/spec/mongo/cluster/topology/replica_set_spec.rb +2 -0
  31. data/spec/mongo/collection/view/aggregation_spec.rb +2 -8
  32. data/spec/mongo/collection/view/change_stream_spec.rb +1 -5
  33. data/spec/mongo/collection/view/map_reduce_spec.rb +2 -8
  34. data/spec/mongo/collection/view/readable_spec.rb +1 -1
  35. data/spec/mongo/collection_spec.rb +11 -29
  36. data/spec/mongo/crud_spec.rb +6 -2
  37. data/spec/mongo/cursor_spec.rb +84 -1
  38. data/spec/mongo/database_spec.rb +2 -8
  39. data/spec/mongo/dns_seedlist_discovery_spec.rb +67 -63
  40. data/spec/mongo/max_staleness_spec.rb +1 -0
  41. data/spec/mongo/retryable_writes_spec.rb +7 -9
  42. data/spec/mongo/sdam_spec.rb +42 -24
  43. data/spec/mongo/server/description/features_spec.rb +3 -3
  44. data/spec/mongo/server_selection_spec.rb +2 -0
  45. data/spec/mongo/server_selector_spec.rb +2 -0
  46. data/spec/mongo/session/session_pool_spec.rb +16 -22
  47. data/spec/mongo/session_spec.rb +13 -8
  48. data/spec/mongo/uri/srv_protocol_spec.rb +481 -478
  49. data/spec/mongo/uri_spec.rb +1 -1
  50. data/spec/spec_helper.rb +11 -63
  51. data/spec/support/authorization.rb +35 -1
  52. data/spec/support/connection_string_tests/invalid-uris.yml +27 -11
  53. data/spec/support/event_subscriber.rb +66 -0
  54. data/spec/support/sdam/rs/compatible.yml +41 -0
  55. data/spec/support/sdam/rs/discover_arbiters.yml +3 -1
  56. data/spec/support/sdam/rs/discover_passives.yml +6 -2
  57. data/spec/support/sdam/rs/discover_primary.yml +3 -1
  58. data/spec/support/sdam/rs/discover_secondary.yml +3 -1
  59. data/spec/support/sdam/rs/discovery.yml +12 -4
  60. data/spec/support/sdam/rs/equal_electionids.yml +6 -2
  61. data/spec/support/sdam/rs/ghost_discovered.yml +3 -1
  62. data/spec/support/sdam/rs/hosts_differ_from_seeds.yml +3 -1
  63. data/spec/support/sdam/rs/ls_timeout.yml +169 -14
  64. data/spec/support/sdam/rs/member_reconfig.yml +6 -2
  65. data/spec/support/sdam/rs/member_standalone.yml +6 -2
  66. data/spec/support/sdam/rs/new_primary.yml +6 -2
  67. data/spec/support/sdam/rs/new_primary_new_electionid.yml +9 -3
  68. data/spec/support/sdam/rs/new_primary_new_setversion.yml +9 -3
  69. data/spec/support/sdam/rs/new_primary_wrong_set_name.yml +6 -2
  70. data/spec/support/sdam/rs/non_rs_member.yml +3 -2
  71. data/spec/support/sdam/rs/normalize_case.yml +3 -1
  72. data/spec/support/sdam/rs/null_election_id.yml +12 -4
  73. data/spec/support/sdam/rs/primary_becomes_standalone.yml +6 -4
  74. data/spec/support/sdam/rs/primary_changes_set_name.yml +6 -2
  75. data/spec/support/sdam/rs/primary_disconnect.yml +3 -1
  76. data/spec/support/sdam/rs/primary_disconnect_electionid.yml +15 -5
  77. data/spec/support/sdam/rs/primary_disconnect_setversion.yml +15 -5
  78. data/spec/support/sdam/rs/primary_hint_from_secondary_with_mismatched_me.yml +6 -2
  79. data/spec/support/sdam/rs/primary_mismatched_me.yml +26 -37
  80. data/spec/support/sdam/rs/primary_reports_new_member.yml +12 -4
  81. data/spec/support/sdam/rs/primary_to_no_primary_mismatched_me.yml +6 -2
  82. data/spec/support/sdam/rs/primary_wrong_set_name.yml +3 -1
  83. data/spec/support/sdam/rs/response_from_removed.yml +6 -2
  84. data/spec/support/sdam/rs/rsother_discovered.yml +6 -2
  85. data/spec/support/sdam/rs/sec_not_auth.yml +6 -2
  86. data/spec/support/sdam/rs/secondary_mismatched_me.yml +26 -37
  87. data/spec/support/sdam/rs/secondary_wrong_set_name.yml +3 -1
  88. data/spec/support/sdam/rs/secondary_wrong_set_name_with_primary.yml +6 -2
  89. data/spec/support/sdam/rs/setversion_without_electionid.yml +6 -2
  90. data/spec/support/sdam/rs/stepdown_change_set_name.yml +6 -2
  91. data/spec/support/sdam/rs/too_new.yml +41 -0
  92. data/spec/support/sdam/rs/too_old.yml +39 -0
  93. data/spec/support/sdam/rs/unexpected_mongos.yml +3 -1
  94. data/spec/support/sdam/rs/use_setversion_without_electionid.yml +9 -3
  95. data/spec/support/sdam/rs/wrong_set_name.yml +3 -1
  96. data/spec/support/server_discovery_and_monitoring.rb +13 -0
  97. data/spec/support/shared/session.rb +10 -30
  98. metadata +10 -2
  99. metadata.gz.sig +0 -0
@@ -26,30 +26,30 @@ module Mongo
26
26
  # Create a SessionPool.
27
27
  #
28
28
  # @example
29
- # SessionPool.create(client)
29
+ # SessionPool.create(cluster)
30
30
  #
31
- # @param [ Mongo::Client ] client The client that will be associated with this
31
+ # @param [ Mongo::Cluster ] cluster The cluster that will be associated with this
32
32
  # session pool.
33
33
  #
34
34
  # @since 2.5.0
35
- def self.create(client)
36
- pool = new(client)
37
- client.instance_variable_set(:@session_pool, pool)
35
+ def self.create(cluster)
36
+ pool = new(cluster)
37
+ cluster.instance_variable_set(:@session_pool, pool)
38
38
  end
39
39
 
40
40
  # Initialize a SessionPool.
41
41
  #
42
42
  # @example
43
- # SessionPool.new(client)
43
+ # SessionPool.new(cluster)
44
44
  #
45
- # @param [ Mongo::Client ] client The client that will be associated with this
45
+ # @param [ Mongo::Cluster ] cluster The cluster that will be associated with this
46
46
  # session pool.
47
47
  #
48
48
  # @since 2.5.0
49
- def initialize(client)
49
+ def initialize(cluster)
50
50
  @queue = []
51
51
  @mutex = Mutex.new
52
- @client = client
52
+ @cluster = cluster
53
53
  end
54
54
 
55
55
  # Get a formatted string for use in inspection.
@@ -64,24 +64,6 @@ module Mongo
64
64
  "#<Mongo::Session::SessionPool:0x#{object_id} current_size=#{@queue.size}>"
65
65
  end
66
66
 
67
- # Checkout a session to be used in the context of a block and return the session back to
68
- # the pool after the block completes.
69
- #
70
- # @example Checkout, use a session, and return it back to the pool after the block.
71
- # pool.with_session do |session|
72
- # ...
73
- # end
74
- #
75
- # @yieldparam [ ServerSession ] The server session.
76
- #
77
- # @since 2.5.0
78
- def with_session
79
- server_session = checkout
80
- yield(server_session)
81
- ensure
82
- begin; checkin(server_session) if server_session; rescue; end
83
- end
84
-
85
67
  # Checkout a server session from the pool.
86
68
  #
87
69
  # @example Checkout a session.
@@ -130,7 +112,7 @@ module Mongo
130
112
  # @since 2.5.0
131
113
  def end_sessions
132
114
  while !@queue.empty?
133
- server = ServerSelector.get(mode: :primary_preferred).select_server(@client.cluster)
115
+ server = ServerSelector.get(mode: :primary_preferred).select_server(@cluster)
134
116
  Operation::Commands::Command.new(
135
117
  :selector => {endSessions: @queue.shift(10_000).collect { |s| s.session_id }},
136
118
  :db_name => Database::ADMIN).execute(server)
@@ -141,9 +123,9 @@ module Mongo
141
123
  private
142
124
 
143
125
  def about_to_expire?(session)
144
- if @client.logical_session_timeout
126
+ if @cluster.logical_session_timeout
145
127
  idle_time_minutes = (Time.now - session.last_use) / 60
146
- (idle_time_minutes + 1) >= @client.logical_session_timeout
128
+ (idle_time_minutes + 1) >= @cluster.logical_session_timeout
147
129
  end
148
130
  end
149
131
 
@@ -190,8 +190,32 @@ module Mongo
190
190
  defined?(UNIXSocket) && sock.is_a?(UNIXSocket)
191
191
  end
192
192
 
193
+ DEFAULT_TCP_KEEPINTVL = 10
194
+
195
+ DEFAULT_TCP_KEEPCNT = 9
196
+
197
+ DEFAULT_TCP_KEEPIDLE = 300
198
+
199
+ def set_keepalive_opts(sock)
200
+ sock.setsockopt(SOL_SOCKET, SO_KEEPALIVE, true)
201
+ set_option(sock, :TCP_KEEPINTVL, DEFAULT_TCP_KEEPINTVL)
202
+ set_option(sock, :TCP_KEEPCNT, DEFAULT_TCP_KEEPCNT)
203
+ set_option(sock, :TCP_KEEPIDLE, DEFAULT_TCP_KEEPIDLE)
204
+ rescue
205
+ end
206
+
207
+ def set_option(sock, option, default)
208
+ if Socket.const_defined?(option)
209
+ system_default = sock.getsockopt(IPPROTO_TCP, option).int
210
+ if system_default > default
211
+ sock.setsockopt(IPPROTO_TCP, option, default)
212
+ end
213
+ end
214
+ end
215
+
193
216
  def set_socket_options(sock)
194
217
  sock.set_encoding(BSON::BINARY)
218
+ set_keepalive_opts(sock)
195
219
  end
196
220
 
197
221
  def handle_errors
@@ -43,7 +43,6 @@ module Mongo
43
43
  def connect!(connect_timeout = nil)
44
44
  Timeout.timeout(connect_timeout, Error::SocketTimeoutError) do
45
45
  socket.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
46
- socket.setsockopt(SOL_SOCKET, SO_KEEPALIVE, true)
47
46
  handle_errors { socket.connect(::Socket.pack_sockaddr_in(port, host)) }
48
47
  self
49
48
  end
@@ -84,6 +84,11 @@ module Mongo
84
84
  # @since 2.1.0
85
85
  UNSAFE = /[\:\/\+\@]/
86
86
 
87
+ # Percent sign that must be encoded in user creds.
88
+ #
89
+ # @since 2.5.1
90
+ PERCENT_CHAR = /\%/
91
+
87
92
  # Unix socket suffix.
88
93
  #
89
94
  # @since 2.1.0
@@ -334,15 +339,27 @@ module Mongo
334
339
 
335
340
  def parse_user!(string)
336
341
  if (string && user = string.partition(AUTH_USER_PWD_DELIM)[0])
337
- raise_invalid_error!(UNESCAPED_USER_PWD) if user =~ UNSAFE
338
- decode(user) if user.length > 0
342
+ if user.length > 0
343
+ raise_invalid_error!(UNESCAPED_USER_PWD) if user =~ UNSAFE
344
+ user_decoded = decode(user)
345
+ if user_decoded =~ PERCENT_CHAR && encode(user_decoded) != user
346
+ raise_invalid_error!(UNESCAPED_USER_PWD)
347
+ end
348
+ user_decoded
349
+ end
339
350
  end
340
351
  end
341
352
 
342
353
  def parse_password!(string)
343
354
  if (string && pwd = string.partition(AUTH_USER_PWD_DELIM)[2])
344
- raise_invalid_error!(UNESCAPED_USER_PWD) if pwd =~ UNSAFE
345
- decode(pwd) if pwd.length > 0
355
+ if pwd.length > 0
356
+ raise_invalid_error!(UNESCAPED_USER_PWD) if pwd =~ UNSAFE
357
+ pwd_decoded = decode(pwd)
358
+ if pwd_decoded =~ PERCENT_CHAR && encode(pwd_decoded) != pwd
359
+ raise_invalid_error!(UNESCAPED_USER_PWD)
360
+ end
361
+ pwd_decoded
362
+ end
346
363
  end
347
364
  end
348
365
 
@@ -382,7 +399,11 @@ module Mongo
382
399
  end
383
400
 
384
401
  def decode(value)
385
- ::URI::DEFAULT_PARSER.unescape(value)
402
+ ::URI.decode(value)
403
+ end
404
+
405
+ def encode(value)
406
+ ::URI.encode(value)
386
407
  end
387
408
 
388
409
  # Hash for storing map of URI option parameters to conversion strategies
@@ -17,5 +17,5 @@ module Mongo
17
17
  # The current version of the driver.
18
18
  #
19
19
  # @since 2.0.0
20
- VERSION = '2.5.0'.freeze
20
+ VERSION = '2.5.1'.freeze
21
21
  end
@@ -214,13 +214,22 @@ describe Mongo::Address do
214
214
  address.host
215
215
  end
216
216
 
217
+ let(:addr_info) do
218
+ family = (host == 'localhost') ? ::Socket::AF_INET : ::Socket::AF_UNSPEC
219
+ ::Socket.getaddrinfo(host, nil, family, ::Socket::SOCK_STREAM)
220
+ end
221
+
222
+ let(:socket_address_or_host) do
223
+ (host == 'localhost') ? addr_info.first[3] : host
224
+ end
225
+
217
226
  context 'when providing a DNS entry that resolves to both IPv6 and IPv4' do
218
227
 
219
228
  before do
220
229
  address.instance_variable_set(:@resolver, nil)
221
230
  allow(::Socket).to receive(:getaddrinfo).and_return(
222
231
  [ ["AF_INET6", 0, '::1', '::1', ::Socket::AF_INET6, 1, 6],
223
- ["AF_INET", 0, host, host, ::Socket::AF_INET, 1, 6]]
232
+ ["AF_INET", 0, socket_address_or_host, socket_address_or_host, ::Socket::AF_INET, 1, 6]]
224
233
  )
225
234
  end
226
235
 
@@ -237,7 +246,33 @@ describe Mongo::Address do
237
246
  end
238
247
 
239
248
  it 'uses the host, not the IP address' do
240
- expect(address.socket(0.0).host).to eq(host)
249
+ expect(address.socket(0.0).host).to eq(socket_address_or_host)
250
+ end
251
+
252
+ let(:socket) do
253
+ if running_ssl?
254
+ address.socket(0.0, SSL_OPTIONS).instance_variable_get(:@tcp_socket)
255
+ else
256
+ address.socket(0.0).instance_variable_get(:@socket)
257
+ end
258
+ end
259
+
260
+ if Socket.const_defined?(:TCP_KEEPINTVL)
261
+ it 'sets the socket TCP_KEEPINTVL option' do
262
+ expect(socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPINTVL).int).to be <= 10
263
+ end
264
+ end
265
+
266
+ if Socket.const_defined?(:TCP_KEEPCNT)
267
+ it 'sets the socket TCP_KEEPCNT option' do
268
+ expect(socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPCNT).int).to be <= 9
269
+ end
270
+ end
271
+
272
+ if Socket.const_defined?(:TCP_KEEPIDLE)
273
+ it 'sets the socket TCP_KEEPIDLE option' do
274
+ expect(socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPIDLE).int).to be <= 300
275
+ end
241
276
  end
242
277
  end
243
278
  end
@@ -1889,32 +1889,26 @@ describe Mongo::BulkWrite do
1889
1889
  it_behaves_like 'an operation using a session'
1890
1890
  end
1891
1891
 
1892
- context 'when retryable writes are supported', if: (sessions_enabled? && (replica_set? || sharded?)) do
1892
+ context 'when retryable writes are supported', if: test_sessions? do
1893
1893
 
1894
1894
  let(:client) do
1895
- authorized_client.with(heartbeat_frequency: 100, retry_writes: true).tap do |cl|
1896
- cl.subscribe(Mongo::Monitoring::COMMAND, subscriber)
1897
- end
1895
+ authorized_client_with_retry_writes
1898
1896
  end
1899
1897
 
1900
1898
  let(:collection) do
1901
1899
  client[authorized_collection.name]
1902
1900
  end
1903
1901
 
1904
- let(:subscriber) do
1905
- EventSubscriber.new
1906
- end
1907
-
1908
1902
  let!(:result) do
1909
1903
  bulk_write.execute
1910
1904
  end
1911
1905
 
1912
1906
  let(:first_txn_number) do
1913
- subscriber.started_events[-2].command['txnNumber'].instance_variable_get(:@integer)
1907
+ EventSubscriber.started_events[-2].command['txnNumber'].instance_variable_get(:@integer)
1914
1908
  end
1915
1909
 
1916
1910
  let(:second_txn_number) do
1917
- subscriber.started_events[-1].command['txnNumber'].instance_variable_get(:@integer)
1911
+ EventSubscriber.started_events[-1].command['txnNumber'].instance_variable_get(:@integer)
1918
1912
  end
1919
1913
 
1920
1914
  it 'inserts the documents' do
@@ -147,4 +147,44 @@ describe 'change streams examples in Ruby', if: test_change_streams? do
147
147
  resumed_change.each { |key| expect(resumed_change[key]).to eq(next_next_change[key]) }
148
148
  end
149
149
  end
150
+
151
+ context 'example 4 - using a pipeline to filter changes' do
152
+
153
+ it 'returns the filtered changes' do
154
+
155
+ ops_thread = Thread.new do
156
+ sleep 2
157
+ inventory.insert_one(username: 'wallace')
158
+ inventory.insert_one(username: 'alice')
159
+ inventory.delete_one(username: 'wallace')
160
+ end
161
+
162
+ stream_thread = Thread.new do
163
+
164
+ # Start Changestream Example 4
165
+
166
+ pipeline = [ {'$match' => { '$or' => [{ 'fullDocument.username' => 'alice' },
167
+ { 'operationType' => 'delete' }] } }]
168
+ cursor = inventory.watch(pipeline).to_enum
169
+ cursor.next
170
+
171
+ # End Changestream Example 4
172
+ end
173
+
174
+ ops_thread.value
175
+ change = stream_thread.value
176
+
177
+ expect(change['_id']).not_to be_nil
178
+ expect(change['_id']['_data']).not_to be_nil
179
+ expect(change['operationType']).to eq('insert')
180
+ expect(change['fullDocument']).not_to be_nil
181
+ expect(change['fullDocument']['_id']).not_to be_nil
182
+ expect(change['fullDocument']['username']).to eq('alice')
183
+ expect(change['ns']).not_to be_nil
184
+ expect(change['ns']['db']).to eq(TEST_DB)
185
+ expect(change['ns']['coll']).to eq(inventory.name)
186
+ expect(change['documentKey']).not_to be_nil
187
+ expect(change['documentKey']['_id']).to eq(change['fullDocument']['_id'])
188
+ end
189
+ end
150
190
  end
@@ -228,6 +228,10 @@ describe Mongo::Client do
228
228
  described_class.new([default_address.seed], authorized_client.options.merge(options))
229
229
  end
230
230
 
231
+ after do
232
+ client.close
233
+ end
234
+
231
235
  it 'sets the option' do
232
236
  expect(client.options['retry_writes']).to eq(options[:retry_writes])
233
237
  end
@@ -632,7 +636,7 @@ describe Mongo::Client do
632
636
 
633
637
  context 'when providing a connection string' do
634
638
 
635
- context 'when the string uses the SRV Protocol' do
639
+ context 'when the string uses the SRV Protocol', if: test_connecting_externally? do
636
640
 
637
641
  let!(:uri) do
638
642
  'mongodb+srv://test5.test.build.10gen.cc/testdb'
@@ -1356,24 +1360,23 @@ describe Mongo::Client do
1356
1360
  end
1357
1361
 
1358
1362
  let(:client) do
1359
- # Monitoring subscribers won't be set up when using Client#with, so a new client must be created.
1360
1363
  Mongo::Client.new(ADDRESSES, client_options).tap do |cl|
1361
- cl.subscribe(Mongo::Monitoring::COMMAND, subscriber)
1364
+ cl.subscribe(Mongo::Monitoring::COMMAND, EventSubscriber.clear_events!)
1362
1365
  end
1363
1366
  end
1364
1367
 
1365
- let(:subscriber) do
1366
- EventSubscriber.new
1367
- end
1368
-
1369
1368
  let(:command) do
1370
- subscriber.started_events.find { |c| c.command_name == :listDatabases }.command
1369
+ EventSubscriber.started_events.find { |c| c.command_name == :listDatabases }.command
1371
1370
  end
1372
1371
 
1373
1372
  before do
1374
1373
  client.list_databases({}, true)
1375
1374
  end
1376
1375
 
1376
+ after do
1377
+ client.close
1378
+ end
1379
+
1377
1380
  it 'sends the command with the nameOnly flag set to true' do
1378
1381
  expect(command[:nameOnly]).to be(true)
1379
1382
  end
@@ -1503,7 +1506,7 @@ describe Mongo::Client do
1503
1506
  context 'when options are provided' do
1504
1507
 
1505
1508
  let(:options) do
1506
- { causally_consistent: true }
1509
+ { causal_consistency: true }
1507
1510
  end
1508
1511
 
1509
1512
  let(:session) do
@@ -1511,14 +1514,14 @@ describe Mongo::Client do
1511
1514
  end
1512
1515
 
1513
1516
  it 'sets the options on the session' do
1514
- expect(session.options).to eq(options)
1517
+ expect(session.options[:causal_consistency]).to eq(options[:causal_consistency])
1515
1518
  end
1516
1519
  end
1517
1520
 
1518
1521
  context 'when options are not provided' do
1519
1522
 
1520
1523
  it 'does not set options on the session' do
1521
- expect(session.options).to be_empty
1524
+ expect(session.options).to eq({ implicit: false })
1522
1525
  end
1523
1526
  end
1524
1527
 
@@ -1567,7 +1570,7 @@ describe Mongo::Client do
1567
1570
  end
1568
1571
 
1569
1572
  let(:pool) do
1570
- authorized_client.instance_variable_get(:@session_pool)
1573
+ authorized_client.cluster.session_pool
1571
1574
  end
1572
1575
 
1573
1576
  let!(:before_last_use) do
@@ -1581,6 +1584,38 @@ describe Mongo::Client do
1581
1584
  end
1582
1585
  end
1583
1586
 
1587
+ context 'when two clients have the same cluster', if: test_sessions? do
1588
+
1589
+ let(:client) do
1590
+ authorized_client.with(read: { mode: :secondary })
1591
+ end
1592
+
1593
+ let(:session) do
1594
+ authorized_client.start_session
1595
+ end
1596
+
1597
+ it 'allows the session to be used across the clients' do
1598
+ client[TEST_COLL].insert_one({ a: 1 }, session: session)
1599
+ end
1600
+ end
1601
+
1602
+ context 'when two clients have different clusters', if: test_sessions? do
1603
+
1604
+ let(:client) do
1605
+ authorized_client_with_retry_writes
1606
+ end
1607
+
1608
+ let(:session) do
1609
+ authorized_client.start_session
1610
+ end
1611
+
1612
+ it 'raises an exception' do
1613
+ expect {
1614
+ client[TEST_COLL].insert_one({ a: 1 }, session: session)
1615
+ }.to raise_exception(Mongo::Error::InvalidSession)
1616
+ end
1617
+ end
1618
+
1584
1619
  context 'when sessions are not supported', unless: sessions_enabled? do
1585
1620
 
1586
1621
  it 'raises an exception' do