mongo 2.5.0.beta → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo/address.rb +1 -1
  5. data/lib/mongo/address/unix.rb +1 -1
  6. data/lib/mongo/auth/user.rb +0 -5
  7. data/lib/mongo/auth/user/view.rb +4 -4
  8. data/lib/mongo/bulk_write.rb +60 -32
  9. data/lib/mongo/client.rb +44 -8
  10. data/lib/mongo/cluster.rb +14 -12
  11. data/lib/mongo/cluster/periodic_executor.rb +106 -0
  12. data/lib/mongo/cluster/{cursor_reaper.rb → reapers/cursor_reaper.rb} +5 -37
  13. data/lib/mongo/cluster/reapers/socket_reaper.rb +59 -0
  14. data/lib/mongo/collection.rb +9 -6
  15. data/lib/mongo/collection/view.rb +2 -2
  16. data/lib/mongo/collection/view/builder/aggregation.rb +2 -1
  17. data/lib/mongo/collection/view/builder/find_command.rb +1 -1
  18. data/lib/mongo/collection/view/change_stream.rb +14 -1
  19. data/lib/mongo/collection/view/map_reduce.rb +30 -13
  20. data/lib/mongo/collection/view/readable.rb +5 -5
  21. data/lib/mongo/collection/view/writable.rb +98 -51
  22. data/lib/mongo/error.rb +3 -0
  23. data/lib/mongo/error/invalid_txt_record.rb +27 -0
  24. data/lib/mongo/error/invalid_uri.rb +7 -6
  25. data/lib/mongo/error/mismatched_domain.rb +27 -0
  26. data/lib/mongo/error/no_srv_records.rb +26 -0
  27. data/lib/mongo/error/unsupported_features.rb +0 -18
  28. data/lib/mongo/index/view.rb +2 -2
  29. data/lib/mongo/operation.rb +1 -0
  30. data/lib/mongo/operation/causally_consistent.rb +33 -0
  31. data/lib/mongo/operation/commands.rb +2 -1
  32. data/lib/mongo/operation/commands/aggregate.rb +2 -7
  33. data/lib/mongo/operation/commands/count.rb +27 -0
  34. data/lib/mongo/operation/commands/distinct.rb +27 -0
  35. data/lib/mongo/operation/commands/find.rb +3 -1
  36. data/lib/mongo/operation/commands/map_reduce.rb +1 -0
  37. data/lib/mongo/operation/commands/parallel_scan.rb +1 -0
  38. data/lib/mongo/operation/specifiable.rb +12 -0
  39. data/lib/mongo/operation/uses_command_op_msg.rb +36 -5
  40. data/lib/mongo/operation/write.rb +0 -5
  41. data/lib/mongo/operation/write/bulk/bulkable.rb +4 -8
  42. data/lib/mongo/operation/write/bulk/mergable.rb +2 -0
  43. data/lib/mongo/operation/write/command/create_index.rb +19 -0
  44. data/lib/mongo/operation/write/command/create_user.rb +19 -0
  45. data/lib/mongo/operation/write/command/delete.rb +1 -2
  46. data/lib/mongo/operation/write/command/drop_index.rb +19 -0
  47. data/lib/mongo/operation/write/command/insert.rb +1 -2
  48. data/lib/mongo/operation/write/command/remove_user.rb +19 -0
  49. data/lib/mongo/operation/write/command/update.rb +1 -2
  50. data/lib/mongo/operation/write/command/update_user.rb +19 -0
  51. data/lib/mongo/operation/write/write_command_enabled.rb +1 -3
  52. data/lib/mongo/protocol/compressed.rb +2 -1
  53. data/lib/mongo/protocol/serializers.rb +6 -6
  54. data/lib/mongo/retryable.rb +48 -5
  55. data/lib/mongo/server.rb +15 -0
  56. data/lib/mongo/server/connection.rb +21 -1
  57. data/lib/mongo/server/connection_pool.rb +3 -0
  58. data/lib/mongo/server/connection_pool/queue.rb +50 -5
  59. data/lib/mongo/server/description.rb +11 -3
  60. data/lib/mongo/server/description/features.rb +26 -7
  61. data/lib/mongo/session.rb +133 -6
  62. data/lib/mongo/session/server_session.rb +30 -0
  63. data/lib/mongo/session/session_pool.rb +20 -20
  64. data/lib/mongo/uri.rb +88 -44
  65. data/lib/mongo/uri/srv_protocol.rb +158 -0
  66. data/lib/mongo/version.rb +1 -1
  67. data/lib/mongo/write_concern/normalizable.rb +12 -0
  68. data/mongo.gemspec +1 -2
  69. data/spec/mongo/address_spec.rb +12 -0
  70. data/spec/mongo/auth/user/view_spec.rb +1 -5
  71. data/spec/mongo/bulk_write_spec.rb +232 -401
  72. data/spec/mongo/change_stream_examples_spec.rb +150 -0
  73. data/spec/mongo/client_spec.rb +142 -2
  74. data/spec/mongo/cluster/cursor_reaper_spec.rb +0 -70
  75. data/spec/mongo/cluster/socket_reaper_spec.rb +32 -0
  76. data/spec/mongo/cluster_spec.rb +11 -7
  77. data/spec/mongo/collection/view/aggregation_spec.rb +46 -1
  78. data/spec/mongo/collection/view/builder/find_command_spec.rb +15 -0
  79. data/spec/mongo/collection/view/change_stream_spec.rb +79 -12
  80. data/spec/mongo/collection/view/map_reduce_spec.rb +120 -4
  81. data/spec/mongo/collection/view/readable_spec.rb +23 -5
  82. data/spec/mongo/collection_spec.rb +292 -102
  83. data/spec/mongo/command_monitoring_spec.rb +26 -32
  84. data/spec/mongo/crud_spec.rb +1 -1
  85. data/spec/mongo/cursor_spec.rb +2 -3
  86. data/spec/mongo/database_spec.rb +30 -14
  87. data/spec/mongo/dns_seedlist_discovery_spec.rb +94 -0
  88. data/spec/mongo/grid/fs_bucket_spec.rb +1 -1
  89. data/spec/mongo/grid/stream/write_spec.rb +1 -1
  90. data/spec/mongo/index/view_spec.rb +8 -46
  91. data/spec/mongo/operation/write/bulk/delete_spec.rb +2 -2
  92. data/spec/mongo/operation/write/bulk/insert_spec.rb +2 -10
  93. data/spec/mongo/operation/write/{create_index_spec.rb → command/create_index_spec.rb} +2 -6
  94. data/spec/mongo/operation/write/command/delete_spec.rb +35 -7
  95. data/spec/mongo/operation/write/{drop_index_spec.rb → command/drop_index_spec.rb} +1 -1
  96. data/spec/mongo/operation/write/command/insert_spec.rb +37 -6
  97. data/spec/mongo/operation/write/{remove_user_spec.rb → command/remove_user_spec.rb} +2 -6
  98. data/spec/mongo/operation/write/command/update_spec.rb +34 -7
  99. data/spec/mongo/operation/write/{update_user_spec.rb → command/update_user_spec.rb} +1 -1
  100. data/spec/mongo/operation/write/create_user_spec.rb +1 -1
  101. data/spec/mongo/operation/write/delete_spec.rb +1 -1
  102. data/spec/mongo/operation/write/insert_spec.rb +2 -10
  103. data/spec/mongo/operation/write/update_spec.rb +3 -15
  104. data/spec/mongo/retryable_spec.rb +1 -1
  105. data/spec/mongo/retryable_writes_spec.rb +815 -0
  106. data/spec/mongo/server/connection_pool/queue_spec.rb +35 -2
  107. data/spec/mongo/server/connection_pool_spec.rb +234 -1
  108. data/spec/mongo/server/connection_spec.rb +10 -6
  109. data/spec/mongo/server/description/features_spec.rb +51 -37
  110. data/spec/mongo/server/description_spec.rb +6 -3
  111. data/spec/mongo/server_spec.rb +87 -0
  112. data/spec/mongo/session/server_session_spec.rb +43 -0
  113. data/spec/mongo/session/session_pool_spec.rb +63 -27
  114. data/spec/mongo/session_spec.rb +247 -0
  115. data/spec/mongo/shell_examples_spec.rb +2 -2
  116. data/spec/mongo/uri/srv_protocol_spec.rb +933 -0
  117. data/spec/mongo/uri_spec.rb +42 -3
  118. data/spec/mongo/write_concern/acknowledged_spec.rb +11 -0
  119. data/spec/mongo/write_concern/unacknowledged_spec.rb +11 -0
  120. data/spec/spec_helper.rb +11 -25
  121. data/spec/support/authorization.rb +2 -1
  122. data/spec/support/connection_string.rb +8 -4
  123. data/spec/support/crud.rb +38 -24
  124. data/spec/support/crud/write.rb +30 -3
  125. data/spec/support/crud_tests/read/aggregate-out.yml +21 -0
  126. data/spec/support/crud_tests/write/bulkWrite-arrayFilters.yml +44 -0
  127. data/spec/support/crud_tests/write/findOneAndUpdate-arrayFilters.yml +1 -1
  128. data/spec/support/crud_tests/write/insertMany.yml +1 -3
  129. data/spec/support/crud_tests/write/replaceOne.yml +1 -1
  130. data/spec/support/crud_tests/write/updateMany-arrayFilters.yml +1 -1
  131. data/spec/support/crud_tests/write/updateOne-arrayFilters.yml +1 -1
  132. data/spec/support/dns_seedlist_discovery_tests/longer-parent-in-return.yml +11 -0
  133. data/spec/support/dns_seedlist_discovery_tests/misformatted-option.yml +5 -0
  134. data/spec/support/dns_seedlist_discovery_tests/no-results.yml +5 -0
  135. data/spec/support/dns_seedlist_discovery_tests/not-enough-parts.yml +5 -0
  136. data/spec/support/dns_seedlist_discovery_tests/one-result-default-port.yml +10 -0
  137. data/spec/support/dns_seedlist_discovery_tests/one-txt-record-multiple-strings.yml +10 -0
  138. data/spec/support/dns_seedlist_discovery_tests/one-txt-record.yml +11 -0
  139. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch1.yml +5 -0
  140. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch2.yml +5 -0
  141. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch3.yml +5 -0
  142. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch4.yml +5 -0
  143. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch5.yml +5 -0
  144. data/spec/support/dns_seedlist_discovery_tests/returned-parent-too-short.yml +5 -0
  145. data/spec/support/dns_seedlist_discovery_tests/returned-parent-wrong.yml +5 -0
  146. data/spec/support/dns_seedlist_discovery_tests/two-results-default-port.yml +11 -0
  147. data/spec/support/dns_seedlist_discovery_tests/two-results-nonstandard-port.yml +11 -0
  148. data/spec/support/dns_seedlist_discovery_tests/two-txt-records.yml +5 -0
  149. data/spec/support/dns_seedlist_discovery_tests/txt-record-not-allowed-option.yml +5 -0
  150. data/spec/support/dns_seedlist_discovery_tests/txt-record-with-overridden-ssl-option.yml +11 -0
  151. data/spec/support/dns_seedlist_discovery_tests/txt-record-with-overridden-uri-option.yml +11 -0
  152. data/spec/support/dns_seedlist_discovery_tests/txt-record-with-unallowed-option.yml +5 -0
  153. data/spec/support/dns_seedlist_discovery_tests/uri-with-port.yml +5 -0
  154. data/spec/support/dns_seedlist_discovery_tests/uri-with-two-hosts.yml +5 -0
  155. data/spec/support/retryable_writes_tests/bulkWrite.yml +305 -0
  156. data/spec/support/retryable_writes_tests/deleteOne.yml +51 -0
  157. data/spec/support/retryable_writes_tests/findOneAndDelete.yml +52 -0
  158. data/spec/support/retryable_writes_tests/findOneAndReplace.yml +57 -0
  159. data/spec/support/retryable_writes_tests/findOneAndUpdate.yml +56 -0
  160. data/spec/support/retryable_writes_tests/insertMany.yml +72 -0
  161. data/spec/support/retryable_writes_tests/insertOne.yml +55 -0
  162. data/spec/support/retryable_writes_tests/replaceOne.yml +60 -0
  163. data/spec/support/retryable_writes_tests/updateOne.yml +120 -0
  164. data/spec/support/shared/session.rb +525 -24
  165. metadata +437 -350
  166. metadata.gz.sig +0 -0
  167. data/lib/mongo/operation/commands/user_query.rb +0 -72
  168. data/lib/mongo/operation/write/create_index.rb +0 -67
  169. data/lib/mongo/operation/write/create_user.rb +0 -50
  170. data/lib/mongo/operation/write/drop_index.rb +0 -63
  171. data/lib/mongo/operation/write/remove_user.rb +0 -48
  172. data/lib/mongo/operation/write/update_user.rb +0 -50
@@ -1,6 +1,6 @@
1
1
  shared_examples 'an operation using a session' do
2
2
 
3
- describe 'operation execution', if: sessions_enabled? do
3
+ describe 'operation execution', if: test_sessions? do
4
4
 
5
5
  context 'when the session is created from the same client used for the operation' do
6
6
 
@@ -17,7 +17,7 @@ shared_examples 'an operation using a session' do
17
17
  end
18
18
 
19
19
  let!(:before_operation_time) do
20
- (session.instance_variable_get(:@operation_time) || 0)
20
+ (session.operation_time || 0)
21
21
  end
22
22
 
23
23
  let!(:operation_result) do
@@ -33,7 +33,7 @@ shared_examples 'an operation using a session' do
33
33
  end
34
34
 
35
35
  it 'updates the operation time value' do
36
- expect(session.instance_variable_get(:@operation_time)).not_to eq(before_operation_time)
36
+ expect(session.operation_time).not_to eq(before_operation_time)
37
37
  end
38
38
 
39
39
  it 'does not close the session when the operation completes' do
@@ -44,11 +44,7 @@ shared_examples 'an operation using a session' do
44
44
  context 'when a session from another client is provided' do
45
45
 
46
46
  let(:session) do
47
- client.start_session
48
- end
49
-
50
- let(:client) do
51
- authorized_client.with(read: { mode: :secondary })
47
+ authorized_client.with(read: { mode: :secondary }).start_session
52
48
  end
53
49
 
54
50
  let(:operation_result) do
@@ -87,17 +83,18 @@ end
87
83
 
88
84
  shared_examples 'a failed operation using a session' do
89
85
 
90
- context 'when the operation fails', if: sessions_enabled? do
86
+ context 'when the operation fails', if: test_sessions? do
91
87
 
92
88
  let!(:before_last_use) do
93
89
  session.instance_variable_get(:@server_session).last_use
94
90
  end
95
91
 
96
92
  let!(:before_operation_time) do
97
- (session.instance_variable_get(:@operation_time) || 0)
93
+ (session.operation_time || 0)
98
94
  end
99
95
 
100
96
  let!(:operation_result) do
97
+ sleep 0.2
101
98
  begin; failed_operation; rescue => e; e; end
102
99
  end
103
100
 
@@ -115,7 +112,451 @@ shared_examples 'a failed operation using a session' do
115
112
  end
116
113
 
117
114
  it 'updates the operation time value' do
118
- expect(session.instance_variable_get(:@operation_time)).not_to eq(before_operation_time)
115
+ expect(session.operation_time).not_to eq(before_operation_time)
116
+ end
117
+ end
118
+ end
119
+
120
+ shared_examples 'a causally consistent client session with an unacknowledged write' do
121
+
122
+ context 'when an unacknowledged write is executed in the context of a causally consistent session', if: sessions_enabled? do
123
+
124
+ let(:session) do
125
+ client.start_session(causal_consistency: true)
126
+ end
127
+
128
+ it 'does not update the operation time of the session' do
129
+ operation
130
+ expect(session.operation_time).to be_nil
131
+ end
132
+ end
133
+ end
134
+
135
+ shared_examples 'an operation supporting causally consistent reads' do
136
+
137
+ let(:client) do
138
+ authorized_client.with(heartbeat_frequency: 100).tap do |cl|
139
+ cl.subscribe(Mongo::Monitoring::COMMAND, subscriber)
140
+ end
141
+ end
142
+
143
+ let(:subscriber) do
144
+ EventSubscriber.new
145
+ end
146
+
147
+ after do
148
+ client.close
149
+ end
150
+
151
+ context 'when connected to a standalone', if: sessions_enabled? && standalone? do
152
+
153
+ context 'when the collection specifies a read concern' do
154
+
155
+ let(:collection) do
156
+ client[TEST_COLL, read_concern: { level: 'majority' }]
157
+ end
158
+
159
+ context 'when the session has causal_consistency set to true' do
160
+
161
+ let(:session) do
162
+ client.start_session(causal_consistency: true)
163
+ end
164
+
165
+ it 'does not add the afterClusterTime to the read concern in the command' do
166
+ expect(command['readConcern']['afterClusterTime']).to be_nil
167
+ end
168
+ end
169
+
170
+ context 'when the session has causal_consistency set to false' do
171
+
172
+ let(:session) do
173
+ client.start_session(causal_consistency: false)
174
+ end
175
+
176
+ it 'does not add the afterClusterTime to the read concern in the command' do
177
+ expect(command['readConcern']['afterClusterTime']).to be_nil
178
+ end
179
+ end
180
+
181
+ context 'when the session has causal_consistency not set' do
182
+
183
+ let(:session) do
184
+ client.start_session
185
+ end
186
+
187
+ it 'does not add the afterClusterTime to the read concern in the command' do
188
+ expect(command['readConcern']['afterClusterTime']).to be_nil
189
+ end
190
+ end
191
+ end
192
+
193
+ context 'when the collection does not specify a read concern' do
194
+
195
+ let(:collection) do
196
+ client[TEST_COLL]
197
+ end
198
+
199
+ context 'when the session has causal_consistency set to true' do
200
+
201
+ let(:session) do
202
+ client.start_session(causal_consistency: true)
203
+ end
204
+
205
+ it 'does not include the read concern in the command' do
206
+ expect(command['readConcern']).to be_nil
207
+ end
208
+ end
209
+
210
+ context 'when the session has causal_consistency set to false' do
211
+
212
+ let(:session) do
213
+ client.start_session(causal_consistency: false)
214
+ end
215
+
216
+ it 'does not include the read concern in the command' do
217
+ expect(command['readConcern']).to be_nil
218
+ end
219
+ end
220
+
221
+ context 'when the session has causal_consistency not set' do
222
+
223
+ let(:session) do
224
+ client.start_session
225
+ end
226
+
227
+ it 'does not include the read concern in the command' do
228
+ expect(command['readConcern']).to be_nil
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ context 'when connected to replica set or sharded cluster', if: test_sessions? do
235
+
236
+ context 'when the collection specifies a read concern' do
237
+
238
+ let(:collection) do
239
+ client[TEST_COLL, read_concern: { level: 'majority' }]
240
+ end
241
+
242
+ context 'when the session has causal_consistency set to true' do
243
+
244
+ let(:session) do
245
+ client.start_session(causal_consistency: true)
246
+ end
247
+
248
+ context 'when the session has an operation time' do
249
+
250
+ before do
251
+ client.database.command({ ping: 1 }, session: session)
252
+ end
253
+
254
+ let!(:operation_time) do
255
+ session.operation_time
256
+ end
257
+
258
+ let(:expected_read_concern) do
259
+ BSON::Document.new(level: 'majority', afterClusterTime: operation_time)
260
+ end
261
+
262
+ it 'merges the afterClusterTime with the read concern in the command' do
263
+ expect(command['readConcern']).to eq(expected_read_concern)
264
+ end
265
+ end
266
+
267
+ context 'when the session does not have an operation time' do
268
+
269
+ let(:expected_read_concern) do
270
+ BSON::Document.new(level: 'majority')
271
+ end
272
+
273
+ it 'leaves the read concern document unchanged' do
274
+ expect(command['readConcern']).to eq(expected_read_concern)
275
+ end
276
+ end
277
+
278
+ context 'when the operation time is advanced' do
279
+
280
+ before do
281
+ session.advance_operation_time(operation_time)
282
+ end
283
+
284
+ let(:operation_time) do
285
+ BSON::Timestamp.new(0, 1)
286
+ end
287
+
288
+ let(:expected_read_concern) do
289
+ BSON::Document.new(level: 'majority', afterClusterTime: operation_time)
290
+ end
291
+
292
+ it 'merges the afterClusterTime with the new operation time and read concern in the command' do
293
+ expect(command['readConcern']).to eq(expected_read_concern)
294
+ end
295
+ end
296
+ end
297
+
298
+ context 'when the session has causal_consistency set to false' do
299
+
300
+ let(:session) do
301
+ client.start_session(causal_consistency: false)
302
+ end
303
+
304
+ context 'when the session does not have an operation time' do
305
+
306
+ let(:expected_read_concern) do
307
+ BSON::Document.new(level: 'majority')
308
+ end
309
+
310
+ it 'leaves the read concern document unchanged' do
311
+ expect(command['readConcern']).to eq(expected_read_concern)
312
+ end
313
+ end
314
+
315
+ context 'when the session has an operation time' do
316
+
317
+ before do
318
+ client.database.command({ ping: 1 }, session: session)
319
+ end
320
+
321
+ let(:expected_read_concern) do
322
+ BSON::Document.new(level: 'majority')
323
+ end
324
+
325
+ it 'leaves the read concern document unchanged' do
326
+ expect(command['readConcern']).to eq(expected_read_concern)
327
+ end
328
+ end
329
+
330
+ context 'when the operation time is advanced' do
331
+
332
+ before do
333
+ session.advance_operation_time(operation_time)
334
+ end
335
+
336
+ let(:operation_time) do
337
+ BSON::Timestamp.new(0, 1)
338
+ end
339
+
340
+ let(:expected_read_concern) do
341
+ BSON::Document.new(level: 'majority')
342
+ end
343
+
344
+ it 'leaves the read concern document unchanged' do
345
+ expect(command['readConcern']).to eq(expected_read_concern)
346
+ end
347
+ end
348
+ end
349
+
350
+ context 'when the session has causal_consistency not set' do
351
+
352
+ let(:session) do
353
+ client.start_session
354
+ end
355
+
356
+ context 'when the session does not have an operation time' do
357
+
358
+ let(:expected_read_concern) do
359
+ BSON::Document.new(level: 'majority')
360
+ end
361
+
362
+ it 'leaves the read concern document unchanged' do
363
+ expect(command['readConcern']).to eq(expected_read_concern)
364
+ end
365
+ end
366
+
367
+ context 'when the session has an operation time' do
368
+
369
+ before do
370
+ client.database.command({ ping: 1 }, session: session)
371
+ end
372
+
373
+ let!(:operation_time) do
374
+ session.operation_time
375
+ end
376
+
377
+ let(:expected_read_concern) do
378
+ BSON::Document.new(level: 'majority', afterClusterTime: operation_time)
379
+ end
380
+
381
+ it 'merges the afterClusterTime with the new operation time and read concern in the command' do
382
+ expect(command['readConcern']).to eq(expected_read_concern)
383
+ end
384
+ end
385
+
386
+ context 'when the operation time is advanced' do
387
+
388
+ before do
389
+ session.advance_operation_time(operation_time)
390
+ end
391
+
392
+ let(:operation_time) do
393
+ BSON::Timestamp.new(0, 1)
394
+ end
395
+
396
+ let(:expected_read_concern) do
397
+ BSON::Document.new(level: 'majority', afterClusterTime: operation_time)
398
+ end
399
+
400
+ it 'merges the afterClusterTime with the new operation time and read concern in the command' do
401
+ expect(command['readConcern']).to eq(expected_read_concern)
402
+ end
403
+ end
404
+ end
405
+ end
406
+
407
+ context 'when the collection does not specify a read concern' do
408
+
409
+ let(:collection) do
410
+ client[TEST_COLL]
411
+ end
412
+
413
+ context 'when the session has causal_consistency set to true' do
414
+
415
+ let(:session) do
416
+ client.start_session(causal_consistency: true)
417
+ end
418
+
419
+ context 'when the session does not have an operation time' do
420
+
421
+ it 'does not include the read concern in the command' do
422
+ expect(command['readConcern']).to be_nil
423
+ end
424
+ end
425
+
426
+ context 'when the session has an operation time' do
427
+
428
+ before do
429
+ client.database.command({ ping: 1 }, session: session)
430
+ end
431
+
432
+ let!(:operation_time) do
433
+ session.operation_time
434
+ end
435
+
436
+ let(:expected_read_concern) do
437
+ BSON::Document.new(afterClusterTime: operation_time)
438
+ end
439
+
440
+ it 'merges the afterClusterTime with the read concern in the command' do
441
+ expect(command['readConcern']).to eq(expected_read_concern)
442
+ end
443
+ end
444
+
445
+ context 'when the operation time is advanced' do
446
+
447
+ before do
448
+ session.advance_operation_time(operation_time)
449
+ end
450
+
451
+ let(:operation_time) do
452
+ BSON::Timestamp.new(0, 1)
453
+ end
454
+
455
+ let(:expected_read_concern) do
456
+ BSON::Document.new(afterClusterTime: operation_time)
457
+ end
458
+
459
+ it 'merges the afterClusterTime with the new operation time in the command' do
460
+ expect(command['readConcern']).to eq(expected_read_concern)
461
+ end
462
+ end
463
+ end
464
+
465
+ context 'when the session has causal_consistency set to false' do
466
+
467
+ let(:session) do
468
+ client.start_session(causal_consistency: false)
469
+ end
470
+
471
+ context 'when the session does not have an operation time' do
472
+
473
+ it 'does not include the read concern in the command' do
474
+ expect(command['readConcern']).to be_nil
475
+ end
476
+ end
477
+
478
+ context 'when the session has an operation time' do
479
+
480
+ before do
481
+ client.database.command({ ping: 1 }, session: session)
482
+ end
483
+
484
+ it 'does not include the read concern in the command' do
485
+ expect(command['readConcern']).to be_nil
486
+ end
487
+ end
488
+
489
+ context 'when the operation time is advanced' do
490
+
491
+ before do
492
+ session.advance_operation_time(operation_time)
493
+ end
494
+
495
+ let(:operation_time) do
496
+ BSON::Timestamp.new(0, 1)
497
+ end
498
+
499
+ let(:expected_read_concern) do
500
+ BSON::Document.new(afterClusterTime: operation_time)
501
+ end
502
+
503
+ it 'does not include the read concern in the command' do
504
+ expect(command['readConcern']).to be_nil
505
+ end
506
+ end
507
+ end
508
+
509
+ context 'when the session has causal_consistency not set' do
510
+
511
+ let(:session) do
512
+ client.start_session
513
+ end
514
+
515
+ context 'when the session does not have an operation time' do
516
+
517
+ it 'does not include the read concern in the command' do
518
+ expect(command['readConcern']).to be_nil
519
+ end
520
+ end
521
+
522
+ context 'when the session has an operation time' do
523
+
524
+ before do
525
+ client.database.command({ ping: 1 }, session: session)
526
+ end
527
+
528
+ let!(:operation_time) do
529
+ session.operation_time
530
+ end
531
+
532
+ let(:expected_read_concern) do
533
+ BSON::Document.new(afterClusterTime: operation_time)
534
+ end
535
+
536
+ it 'merges the afterClusterTime with the read concern in the command' do
537
+ expect(command['readConcern']).to eq(expected_read_concern)
538
+ end
539
+ end
540
+
541
+ context 'when the operation time is advanced' do
542
+
543
+ before do
544
+ session.advance_operation_time(operation_time)
545
+ end
546
+
547
+ let(:operation_time) do
548
+ BSON::Timestamp.new(0, 1)
549
+ end
550
+
551
+ let(:expected_read_concern) do
552
+ BSON::Document.new(afterClusterTime: operation_time)
553
+ end
554
+
555
+ it 'merges the afterClusterTime with the new operation time in the command' do
556
+ expect(command['readConcern']).to eq(expected_read_concern)
557
+ end
558
+ end
559
+ end
119
560
  end
120
561
  end
121
562
  end
@@ -126,6 +567,10 @@ shared_examples 'an operation updating cluster time' do
126
567
  client.cluster
127
568
  end
128
569
 
570
+ let(:session) do
571
+ client.start_session
572
+ end
573
+
129
574
  let(:client) do
130
575
  authorized_client.with(heartbeat_frequency: 100).tap do |cl|
131
576
  cl.subscribe(Mongo::Monitoring::COMMAND, subscriber)
@@ -144,32 +589,40 @@ shared_examples 'an operation updating cluster time' do
144
589
 
145
590
  context 'when the server is version 3.6' do
146
591
 
147
- context 'when the server is a mongos', if: (sharded? && sessions_enabled?) do
592
+ context 'when the cluster is sharded or a replica set', if: test_sessions? do
148
593
 
149
594
  let!(:reply_cluster_time) do
150
- operation
595
+ operation_with_session
151
596
  subscriber.succeeded_events[-1].reply['$clusterTime']
152
597
  end
153
598
 
154
599
  it 'updates the cluster time of the cluster' do
155
600
  expect(cluster.cluster_time).to eq(reply_cluster_time)
156
601
  end
602
+
603
+ it 'updates the cluster time of the session' do
604
+ expect(session.cluster_time).to eq(reply_cluster_time)
605
+ end
157
606
  end
158
607
 
159
- context 'when the server is not a mongos', if: (!sharded? && sessions_enabled?) do
608
+ context 'when the server is a standalone', if: (standalone? && sessions_enabled?) do
160
609
 
161
610
  let(:before_cluster_time) do
162
611
  client.cluster.cluster_time
163
612
  end
164
613
 
165
614
  let!(:reply_cluster_time) do
166
- operation
615
+ operation_with_session
167
616
  subscriber.succeeded_events[-1].reply['$clusterTime']
168
617
  end
169
618
 
170
619
  it 'does not update the cluster time of the cluster' do
171
620
  expect(before_cluster_time).to eq(before_cluster_time)
172
621
  end
622
+
623
+ it 'does not update the cluster time of the session' do
624
+ expect(session.cluster_time).to be_nil
625
+ end
173
626
  end
174
627
  end
175
628
 
@@ -193,28 +646,76 @@ shared_examples 'an operation updating cluster time' do
193
646
  context 'when the command is run twice' do
194
647
 
195
648
  let!(:reply_cluster_time) do
196
- operation
649
+ operation_with_session
197
650
  subscriber.succeeded_events[-1].reply['$clusterTime']
198
651
  end
199
652
 
200
- let(:second_command_cluster_time) do
201
- second_operation
202
- subscriber.started_events[-1].command['$clusterTime']
203
- end
653
+ context 'when the cluster is sharded or a replica set', if: test_sessions? do
654
+
655
+ context 'when the session cluster time is advanced' do
656
+
657
+ before do
658
+ session.advance_cluster_time(advanced_cluster_time)
659
+ end
660
+
661
+ let(:second_command_cluster_time) do
662
+ second_operation
663
+ subscriber.started_events[-1].command['$clusterTime']
664
+ end
204
665
 
205
- context 'when the server is a mongos', if: (sharded? && sessions_enabled?) do
666
+ context 'when the advanced cluster time is greater than the existing cluster time' do
206
667
 
207
- it 'includes the received cluster time in the second command' do
208
- expect(second_command_cluster_time).to eq(reply_cluster_time)
668
+ let(:advanced_cluster_time) do
669
+ new_timestamp = BSON::Timestamp.new(reply_cluster_time[Mongo::Cluster::CLUSTER_TIME].seconds,
670
+ reply_cluster_time[Mongo::Cluster::CLUSTER_TIME].increment + 1)
671
+ new_cluster_time = reply_cluster_time.dup
672
+ new_cluster_time.merge(Mongo::Cluster::CLUSTER_TIME => new_timestamp)
673
+ end
674
+
675
+ it 'includes the advanced cluster time in the second command' do
676
+ expect(second_command_cluster_time).to eq(advanced_cluster_time)
677
+ end
678
+ end
679
+
680
+ context 'when the advanced cluster time is not greater than the existing cluster time' do
681
+
682
+ let(:advanced_cluster_time) do
683
+ new_timestamp = BSON::Timestamp.new(reply_cluster_time[Mongo::Cluster::CLUSTER_TIME].seconds,
684
+ reply_cluster_time[Mongo::Cluster::CLUSTER_TIME].increment - 1)
685
+ new_cluster_time = reply_cluster_time.dup
686
+ new_cluster_time.merge(Mongo::Cluster::CLUSTER_TIME => new_timestamp)
687
+ end
688
+
689
+ it 'does not advance the cluster time' do
690
+ expect(second_command_cluster_time).to eq(reply_cluster_time)
691
+ end
692
+ end
693
+ end
694
+
695
+ context 'when the session cluster time is not advanced' do
696
+
697
+ let(:second_command_cluster_time) do
698
+ second_operation
699
+ subscriber.started_events[-1].command['$clusterTime']
700
+ end
701
+
702
+ it 'includes the received cluster time in the second command' do
703
+ expect(second_command_cluster_time).to eq(reply_cluster_time)
704
+ end
209
705
  end
210
706
  end
211
707
 
212
- context 'when the server is not a mongos', if: (!sharded? && sessions_enabled?) do
708
+ context 'when the server is a standalone', if: (standalone? && sessions_enabled?) do
213
709
 
214
710
  let(:before_cluster_time) do
215
711
  client.cluster.cluster_time
216
712
  end
217
713
 
714
+ let(:second_command_cluster_time) do
715
+ second_operation
716
+ subscriber.started_events[-1].command['$clusterTime']
717
+ end
718
+
218
719
  it 'does not update the cluster time of the cluster' do
219
720
  second_command_cluster_time
220
721
  expect(before_cluster_time).to eq(before_cluster_time)