mongo 2.19.3 → 2.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +37 -1
- data/Rakefile +81 -172
- data/lib/mongo/cluster/topology/base.rb +16 -0
- data/lib/mongo/cluster.rb +27 -1
- data/lib/mongo/collection/view/iterable.rb +1 -0
- data/lib/mongo/collection.rb +4 -2
- data/lib/mongo/config.rb +2 -2
- data/lib/mongo/error/transactions_not_supported.rb +34 -0
- data/lib/mongo/error.rb +1 -0
- data/lib/mongo/grid/fs_bucket.rb +6 -0
- data/lib/mongo/monitoring/event/secure.rb +1 -1
- data/lib/mongo/operation/shared/executable.rb +43 -27
- data/lib/mongo/operation/shared/response_handling.rb +23 -25
- data/lib/mongo/retryable/base_worker.rb +28 -3
- data/lib/mongo/retryable/read_worker.rb +16 -14
- data/lib/mongo/retryable/write_worker.rb +11 -8
- data/lib/mongo/retryable.rb +2 -2
- data/lib/mongo/server/app_metadata/environment.rb +64 -9
- data/lib/mongo/server/app_metadata.rb +5 -4
- data/lib/mongo/server/description/features.rb +1 -0
- data/lib/mongo/server/pending_connection.rb +19 -6
- data/lib/mongo/server_selector/base.rb +32 -6
- data/lib/mongo/session/server_session/dirtyable.rb +52 -0
- data/lib/mongo/session/server_session.rb +3 -0
- data/lib/mongo/session/session_pool.rb +12 -18
- data/lib/mongo/session.rb +32 -0
- data/lib/mongo/socket/ssl.rb +22 -1
- data/lib/mongo/uri.rb +0 -4
- data/lib/mongo/version.rb +1 -5
- data/mongo.gemspec +9 -18
- data/spec/atlas/atlas_connectivity_spec.rb +4 -4
- data/spec/faas/ruby-sam-app/Gemfile +9 -0
- data/spec/faas/ruby-sam-app/mongodb/Gemfile +4 -0
- data/spec/faas/ruby-sam-app/mongodb/app.rb +149 -0
- data/spec/faas/ruby-sam-app/template.yaml +48 -0
- data/spec/integration/client_side_encryption/corpus_spec.rb +10 -2
- data/spec/integration/client_side_encryption/range_explicit_encryption_prose_spec.rb +3 -0
- data/spec/integration/retryable_reads_errors_spec.rb +196 -31
- data/spec/integration/retryable_writes_errors_spec.rb +156 -0
- data/spec/integration/sdam_error_handling_spec.rb +2 -0
- data/spec/lite_spec_helper.rb +0 -10
- data/spec/mongo/cluster_spec.rb +36 -0
- data/spec/mongo/collection/view/aggregation_spec.rb +6 -1
- data/spec/mongo/collection/view/explainable_spec.rb +2 -0
- data/spec/mongo/collection_crud_spec.rb +2 -1
- data/spec/mongo/operation/insert_spec.rb +1 -1
- data/spec/mongo/retryable/write_worker_spec.rb +39 -0
- data/spec/mongo/server/app_metadata/environment_spec.rb +135 -0
- data/spec/mongo/server/app_metadata_spec.rb +12 -2
- data/spec/mongo/server/connection_spec.rb +26 -0
- data/spec/mongo/session/session_pool_spec.rb +1 -16
- data/spec/mongo/session_transaction_spec.rb +15 -0
- data/spec/mongo/uri_spec.rb +0 -9
- data/spec/runners/crud/test.rb +0 -8
- data/spec/runners/crud.rb +1 -1
- data/spec/runners/transactions/test.rb +12 -3
- data/spec/runners/unified/assertions.rb +16 -3
- data/spec/runners/unified/crud_operations.rb +12 -0
- data/spec/runners/unified/support_operations.rb +3 -5
- data/spec/runners/unified/test.rb +8 -1
- data/spec/spec_tests/data/client_side_encryption/explain.yml +2 -2
- data/spec/spec_tests/data/client_side_encryption/fle2v2-BypassQueryAnalysis.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Compact.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-CreateCollection.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-DecryptExistingData.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Delete.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFields-vs-jsonSchema.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFieldsMap-defaults.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-FindOneAndUpdate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-InsertFind-Indexed.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-InsertFind-Unindexed.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-MissingKey.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-NoEncryption.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Aggregate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Correctness.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Delete.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-FindOneAndUpdate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-InsertFind.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Update.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Aggregate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Correctness.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Delete.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-FindOneAndUpdate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-InsertFind.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Update.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Aggregate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Correctness.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Delete.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-InsertFind.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Update.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Aggregate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Correctness.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Delete.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-FindOneAndUpdate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-InsertFind.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Update.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Aggregate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Correctness.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Delete.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-FindOneAndUpdate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-InsertFind.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Update.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Aggregate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Correctness.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Delete.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-FindOneAndUpdate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-InsertFind.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Update.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Aggregate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Correctness.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Delete.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-FindOneAndUpdate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-InsertFind.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Update.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-WrongType.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Update.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-validatorAndPartialFieldExpression.yml +2 -1
- data/spec/spec_tests/data/connection_string/invalid-uris.yml +0 -10
- data/spec/spec_tests/data/connection_string/valid-options.yml +13 -0
- data/spec/spec_tests/data/crud_unified/aggregate-write-readPreference.yml +2 -0
- data/spec/spec_tests/data/crud_unified/db-aggregate-write-readPreference.yml +2 -0
- data/spec/spec_tests/data/crud_unified/find-test-all-options.yml +348 -0
- data/spec/spec_tests/data/index_management/createSearchIndex.yml +5 -3
- data/spec/spec_tests/data/index_management/createSearchIndexes.yml +7 -4
- data/spec/spec_tests/data/index_management/dropSearchIndex.yml +2 -1
- data/spec/spec_tests/data/index_management/listSearchIndexes.yml +13 -7
- data/spec/spec_tests/data/index_management/updateSearchIndex.yml +2 -1
- data/spec/spec_tests/data/retryable_writes/unified/bulkWrite-serverErrors.yml +3 -6
- data/spec/spec_tests/data/retryable_writes/unified/insertOne-serverErrors.yml +3 -6
- data/spec/spec_tests/data/run_command_unified/runCommand.yml +319 -0
- data/spec/spec_tests/data/sessions_unified/driver-sessions-dirty-session-errors.yml +351 -0
- data/spec/spec_tests/data/unified/valid-pass/poc-crud.yml +1 -1
- data/spec/spec_tests/data/unified/valid-pass/poc-retryable-writes.yml +7 -7
- data/spec/spec_tests/data/unified/valid-pass/poc-sessions.yml +3 -4
- data/spec/spec_tests/data/unified/valid-pass/poc-transactions-convenient-api.yml +1 -1
- data/spec/spec_tests/data/unified/valid-pass/poc-transactions-mongos-pin-auto.yml +1 -1
- data/spec/spec_tests/data/unified/valid-pass/poc-transactions.yml +3 -3
- data/spec/spec_tests/run_command_unified_spec.rb +13 -0
- data/spec/spec_tests/sdam_unified_spec.rb +2 -0
- data/spec/spec_tests/transactions_unified_spec.rb +2 -1
- data/spec/support/certificates/atlas-ocsp-ca.crt +89 -77
- data/spec/support/certificates/atlas-ocsp.crt +117 -122
- data/spec/support/certificates/retrieve-atlas-cert +1 -1
- data/spec/support/constraints.rb +6 -0
- data/spec/support/ocsp +1 -1
- data/spec/support/recording_logger.rb +27 -0
- metadata +1245 -1298
- checksums.yaml.gz.sig +0 -0
- data/spec/shared/LICENSE +0 -20
- data/spec/shared/bin/get-mongodb-download-url +0 -17
- data/spec/shared/bin/s3-copy +0 -45
- data/spec/shared/bin/s3-upload +0 -69
- data/spec/shared/lib/mrss/child_process_helper.rb +0 -80
- data/spec/shared/lib/mrss/cluster_config.rb +0 -231
- data/spec/shared/lib/mrss/constraints.rb +0 -378
- data/spec/shared/lib/mrss/docker_runner.rb +0 -295
- data/spec/shared/lib/mrss/eg_config_utils.rb +0 -51
- data/spec/shared/lib/mrss/event_subscriber.rb +0 -210
- data/spec/shared/lib/mrss/lite_constraints.rb +0 -238
- data/spec/shared/lib/mrss/server_version_registry.rb +0 -113
- data/spec/shared/lib/mrss/session_registry.rb +0 -69
- data/spec/shared/lib/mrss/session_registry_legacy.rb +0 -60
- data/spec/shared/lib/mrss/spec_organizer.rb +0 -179
- data/spec/shared/lib/mrss/utils.rb +0 -37
- data/spec/shared/share/Dockerfile.erb +0 -330
- data/spec/shared/share/haproxy-1.conf +0 -16
- data/spec/shared/share/haproxy-2.conf +0 -17
- data/spec/shared/shlib/config.sh +0 -27
- data/spec/shared/shlib/distro.sh +0 -74
- data/spec/shared/shlib/server.sh +0 -416
- data/spec/shared/shlib/set_env.sh +0 -169
- data/spec/spec_tests/data/cmap/pool-clear-interrupt-immediately.yml +0 -49
- data/spec/support/faas/app/aws_lambda/mongodb/Gemfile.lock +0 -19
- data.tar.gz.sig +0 -3
- metadata.gz.sig +0 -0
|
@@ -50,35 +50,33 @@ module Mongo
|
|
|
50
50
|
# the operation is performed.
|
|
51
51
|
# @param [ Mongo::Operation::Context ] context The operation context.
|
|
52
52
|
def add_error_labels(connection, context)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
end
|
|
53
|
+
yield
|
|
54
|
+
rescue Mongo::Error::SocketError => e
|
|
55
|
+
if context.in_transaction? && !context.committing_transaction?
|
|
56
|
+
e.add_label('TransientTransactionError')
|
|
57
|
+
end
|
|
58
|
+
if context.committing_transaction?
|
|
59
|
+
e.add_label('UnknownTransactionCommitResult')
|
|
60
|
+
end
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
end
|
|
62
|
+
maybe_add_retryable_write_error_label!(e, connection, context)
|
|
63
|
+
|
|
64
|
+
raise e
|
|
65
|
+
rescue Mongo::Error::SocketTimeoutError => e
|
|
66
|
+
maybe_add_retryable_write_error_label!(e, connection, context)
|
|
67
|
+
raise e
|
|
68
|
+
rescue Mongo::Error::OperationFailure => e
|
|
69
|
+
if context.committing_transaction?
|
|
70
|
+
if e.write_retryable? || e.wtimeout? || (e.write_concern_error? &&
|
|
71
|
+
!Session::UNLABELED_WRITE_CONCERN_CODES.include?(e.write_concern_error_code)
|
|
72
|
+
) || e.max_time_ms_expired?
|
|
73
|
+
e.add_label('UnknownTransactionCommitResult')
|
|
76
74
|
end
|
|
75
|
+
end
|
|
77
76
|
|
|
78
|
-
|
|
77
|
+
maybe_add_retryable_write_error_label!(e, connection, context)
|
|
79
78
|
|
|
80
|
-
|
|
81
|
-
end
|
|
79
|
+
raise e
|
|
82
80
|
end
|
|
83
81
|
|
|
84
82
|
# Unpins the session and/or the connection if the yielded to block
|
|
@@ -49,7 +49,8 @@ module Mongo
|
|
|
49
49
|
|
|
50
50
|
private
|
|
51
51
|
|
|
52
|
-
# Indicate which exception classes that are generally retryable
|
|
52
|
+
# Indicate which exception classes that are generally retryable
|
|
53
|
+
# when using modern retries mechanism.
|
|
53
54
|
#
|
|
54
55
|
# @return [ Array<Mongo:Error> ] Array of exception classes that are
|
|
55
56
|
# considered retryable.
|
|
@@ -58,18 +59,42 @@ module Mongo
|
|
|
58
59
|
Error::ConnectionPerished,
|
|
59
60
|
Error::ServerNotUsable,
|
|
60
61
|
Error::SocketError,
|
|
61
|
-
Error::SocketTimeoutError
|
|
62
|
+
Error::SocketTimeoutError,
|
|
62
63
|
].freeze
|
|
63
64
|
end
|
|
64
65
|
|
|
66
|
+
# Indicate which exception classes that are generally retryable
|
|
67
|
+
# when using legacy retries mechanism.
|
|
68
|
+
#
|
|
69
|
+
# @return [ Array<Mongo:Error> ] Array of exception classes that are
|
|
70
|
+
# considered retryable.
|
|
71
|
+
def legacy_retryable_exceptions
|
|
72
|
+
[
|
|
73
|
+
Error::ConnectionPerished,
|
|
74
|
+
Error::ServerNotUsable,
|
|
75
|
+
Error::SocketError,
|
|
76
|
+
Error::SocketTimeoutError,
|
|
77
|
+
Error::PoolClearedError,
|
|
78
|
+
Error::PoolPausedError,
|
|
79
|
+
].freeze
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
|
|
65
83
|
# Tests to see if the given exception instance is of a type that can
|
|
66
|
-
# be retried.
|
|
84
|
+
# be retried with modern retry mechanism.
|
|
67
85
|
#
|
|
68
86
|
# @return [ true | false ] true if the exception is retryable.
|
|
69
87
|
def is_retryable_exception?(e)
|
|
70
88
|
retryable_exceptions.any? { |klass| klass === e }
|
|
71
89
|
end
|
|
72
90
|
|
|
91
|
+
# Tests to see if the given exception instance is of a type that can
|
|
92
|
+
# be retried with legacy retry mechanism.
|
|
93
|
+
#
|
|
94
|
+
# @return [ true | false ] true if the exception is retryable.
|
|
95
|
+
def is_legacy_retryable_exception?(e)
|
|
96
|
+
legacy_retryable_exceptions.any? { |klass| klass === e }
|
|
97
|
+
end
|
|
73
98
|
# Logs the given deprecation warning the first time it is called for a
|
|
74
99
|
# given key; after that, it does nothing when given the same key.
|
|
75
100
|
def deprecation_warning(key, warning)
|
|
@@ -190,14 +190,15 @@ module Mongo
|
|
|
190
190
|
#
|
|
191
191
|
# @return [ Result ] The result of the operation.
|
|
192
192
|
def modern_read_with_retry(session, server_selector, &block)
|
|
193
|
-
|
|
193
|
+
server = select_server(cluster, server_selector, session)
|
|
194
|
+
yield server
|
|
194
195
|
rescue *retryable_exceptions, Error::OperationFailure, Auth::Unauthorized, Error::PoolError => e
|
|
195
196
|
e.add_notes('modern retry', 'attempt 1')
|
|
196
197
|
raise e if session.in_transaction?
|
|
197
198
|
raise e if !is_retryable_exception?(e) && !e.write_retryable?
|
|
198
|
-
retry_read(e, session, server_selector, &block)
|
|
199
|
+
retry_read(e, session, server_selector, failed_server: server, &block)
|
|
199
200
|
end
|
|
200
|
-
|
|
201
|
+
|
|
201
202
|
# Attempts to do a "legacy" read with retry. The operation will be
|
|
202
203
|
# attempted multiple times, up to the client's `max_read_retries`
|
|
203
204
|
# setting.
|
|
@@ -212,17 +213,18 @@ module Mongo
|
|
|
212
213
|
def legacy_read_with_retry(session, server_selector, &block)
|
|
213
214
|
attempt = attempt ? attempt + 1 : 1
|
|
214
215
|
yield select_server(cluster, server_selector, session)
|
|
215
|
-
rescue *
|
|
216
|
+
rescue *legacy_retryable_exceptions, Error::OperationFailure => e
|
|
216
217
|
e.add_notes('legacy retry', "attempt #{attempt}")
|
|
217
|
-
|
|
218
|
-
if
|
|
218
|
+
|
|
219
|
+
if is_legacy_retryable_exception?(e)
|
|
220
|
+
|
|
219
221
|
raise e if attempt > client.max_read_retries || session&.in_transaction?
|
|
220
222
|
elsif e.retryable? && !session&.in_transaction?
|
|
221
223
|
raise e if attempt > client.max_read_retries
|
|
222
224
|
else
|
|
223
225
|
raise e
|
|
224
226
|
end
|
|
225
|
-
|
|
227
|
+
|
|
226
228
|
log_retry(e, message: 'Legacy read retry')
|
|
227
229
|
sleep(client.read_retry_interval) unless is_retryable_exception?(e)
|
|
228
230
|
retry
|
|
@@ -257,19 +259,21 @@ module Mongo
|
|
|
257
259
|
# being run on.
|
|
258
260
|
# @param [ Mongo::ServerSelector::Selectable ] server_selector Server
|
|
259
261
|
# selector for the operation.
|
|
262
|
+
# @param [ Mongo::Server ] failed_server The server on which the original
|
|
263
|
+
# operation failed.
|
|
260
264
|
# @param [ Proc ] block The block to execute.
|
|
261
|
-
#
|
|
265
|
+
#
|
|
262
266
|
# @return [ Result ] The result of the operation.
|
|
263
|
-
def retry_read(original_error, session, server_selector, &block)
|
|
267
|
+
def retry_read(original_error, session, server_selector, failed_server: nil, &block)
|
|
264
268
|
begin
|
|
265
|
-
server = select_server(cluster, server_selector, session)
|
|
269
|
+
server = select_server(cluster, server_selector, session, failed_server)
|
|
266
270
|
rescue Error, Error::AuthError => e
|
|
267
271
|
original_error.add_note("later retry failed: #{e.class}: #{e}")
|
|
268
272
|
raise original_error
|
|
269
273
|
end
|
|
270
|
-
|
|
274
|
+
|
|
271
275
|
log_retry(original_error, message: 'Read retry')
|
|
272
|
-
|
|
276
|
+
|
|
273
277
|
begin
|
|
274
278
|
yield server, true
|
|
275
279
|
rescue *retryable_exceptions => e
|
|
@@ -289,8 +293,6 @@ module Mongo
|
|
|
289
293
|
raise original_error
|
|
290
294
|
end
|
|
291
295
|
end
|
|
292
|
-
|
|
293
296
|
end
|
|
294
|
-
|
|
295
297
|
end
|
|
296
298
|
end
|
|
@@ -103,8 +103,9 @@ module Mongo
|
|
|
103
103
|
def nro_write_with_retry(write_concern, context:, &block)
|
|
104
104
|
session = context.session
|
|
105
105
|
server = select_server(cluster, ServerSelector.primary, session)
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
options = session&.client&.options || {}
|
|
107
|
+
|
|
108
|
+
if options[:retry_writes]
|
|
108
109
|
begin
|
|
109
110
|
server.with_connection(connection_global_id: context.connection_global_id) do |connection|
|
|
110
111
|
yield connection, nil, context
|
|
@@ -218,7 +219,7 @@ module Mongo
|
|
|
218
219
|
def modern_write_with_retry(session, server, context, &block)
|
|
219
220
|
txn_num = nil
|
|
220
221
|
connection_succeeded = false
|
|
221
|
-
|
|
222
|
+
|
|
222
223
|
server.with_connection(connection_global_id: context.connection_global_id) do |connection|
|
|
223
224
|
connection_succeeded = true
|
|
224
225
|
|
|
@@ -240,7 +241,7 @@ module Mongo
|
|
|
240
241
|
|
|
241
242
|
# Context#with creates a new context, which is not necessary here
|
|
242
243
|
# but the API is less prone to misuse this way.
|
|
243
|
-
retry_write(e, txn_num, context: context.with(is_retry: true), &block)
|
|
244
|
+
retry_write(e, txn_num, context: context.with(is_retry: true), failed_server: server, &block)
|
|
244
245
|
end
|
|
245
246
|
|
|
246
247
|
# Called after a failed write, this will retry the write no more than
|
|
@@ -250,9 +251,11 @@ module Mongo
|
|
|
250
251
|
# retry.
|
|
251
252
|
# @param [ Number ] txn_num The transaction number.
|
|
252
253
|
# @param [ Operation::Context ] context The context for the operation.
|
|
254
|
+
# @param [ Mongo::Server ] failed_server The server on which the original
|
|
255
|
+
# operation failed.
|
|
253
256
|
#
|
|
254
257
|
# @return [ Result ] The result of the operation.
|
|
255
|
-
def retry_write(original_error, txn_num, context:, &block)
|
|
258
|
+
def retry_write(original_error, txn_num, context:, failed_server: nil, &block)
|
|
256
259
|
session = context.session
|
|
257
260
|
|
|
258
261
|
# We do not request a scan of the cluster here, because error handling
|
|
@@ -260,8 +263,8 @@ module Mongo
|
|
|
260
263
|
# server description and/or topology as necessary (specifically,
|
|
261
264
|
# a socket error or a not master error should have marked the respective
|
|
262
265
|
# server unknown). Here we just need to wait for server selection.
|
|
263
|
-
server = select_server(cluster, ServerSelector.primary, session)
|
|
264
|
-
|
|
266
|
+
server = select_server(cluster, ServerSelector.primary, session, failed_server)
|
|
267
|
+
|
|
265
268
|
unless server.retry_writes?
|
|
266
269
|
# Do not need to add "modern retry" here, it should already be on
|
|
267
270
|
# the first exception.
|
|
@@ -275,7 +278,7 @@ module Mongo
|
|
|
275
278
|
# special marker class to bypass the ordinarily applicable rescues.
|
|
276
279
|
raise Error::RaiseOriginalError
|
|
277
280
|
end
|
|
278
|
-
|
|
281
|
+
|
|
279
282
|
log_retry(original_error, message: 'Write retry')
|
|
280
283
|
server.with_connection(connection_global_id: context.connection_global_id) do |connection|
|
|
281
284
|
yield(connection, txn_num, context)
|
data/lib/mongo/retryable.rb
CHANGED
|
@@ -46,8 +46,8 @@ module Mongo
|
|
|
46
46
|
# @api private
|
|
47
47
|
#
|
|
48
48
|
# @return [ Mongo::Server ] A server matching the server preference.
|
|
49
|
-
def select_server(cluster, server_selector, session)
|
|
50
|
-
server_selector.select_server(cluster, nil, session)
|
|
49
|
+
def select_server(cluster, server_selector, session, failed_server = nil)
|
|
50
|
+
server_selector.select_server(cluster, nil, session, deprioritized: [failed_server].compact)
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
# Returns the read worker for handling retryable reads.
|
|
@@ -18,9 +18,12 @@ module Mongo
|
|
|
18
18
|
class Server
|
|
19
19
|
class AppMetadata
|
|
20
20
|
# Implements the logic from the handshake spec, for deducing and
|
|
21
|
-
# reporting the current
|
|
21
|
+
# reporting the current environment in which the program is
|
|
22
22
|
# executing.
|
|
23
23
|
#
|
|
24
|
+
# This includes FaaS environment checks, as well as checks for the
|
|
25
|
+
# presence of a container (Docker) and/or orchestrator (Kubernetes).
|
|
26
|
+
#
|
|
24
27
|
# @api private
|
|
25
28
|
class Environment
|
|
26
29
|
# Error class for reporting that too many discriminators were found
|
|
@@ -39,6 +42,10 @@ module Mongo
|
|
|
39
42
|
# Error class for reporting that the value for a field is too long.
|
|
40
43
|
class ValueTooLong < Mongo::Error; end
|
|
41
44
|
|
|
45
|
+
# The name and location of the .dockerenv file that will signal the
|
|
46
|
+
# presence of Docker.
|
|
47
|
+
DOCKERENV_PATH = '/.dockerenv'
|
|
48
|
+
|
|
42
49
|
# This value is not explicitly specified in the spec, only implied to be
|
|
43
50
|
# less than 512.
|
|
44
51
|
MAXIMUM_VALUE_LENGTH = 500
|
|
@@ -102,9 +109,11 @@ module Mongo
|
|
|
102
109
|
# if the environment contains invalid or contradictory state, it will
|
|
103
110
|
# be initialized with {{name}} set to {{nil}}.
|
|
104
111
|
def initialize
|
|
112
|
+
@fields = {}
|
|
105
113
|
@error = nil
|
|
106
114
|
@name = detect_environment
|
|
107
|
-
|
|
115
|
+
populate_faas_fields
|
|
116
|
+
detect_container
|
|
108
117
|
rescue TooManyEnvironments => e
|
|
109
118
|
self.error = "too many environments detected: #{e.message}"
|
|
110
119
|
rescue MissingVariable => e
|
|
@@ -115,6 +124,23 @@ module Mongo
|
|
|
115
124
|
self.error = "value for #{e.message} is too long"
|
|
116
125
|
end
|
|
117
126
|
|
|
127
|
+
# Queries the detected container information.
|
|
128
|
+
#
|
|
129
|
+
# @return [ Hash | nil ] the detected container information, or
|
|
130
|
+
# nil if no container was detected.
|
|
131
|
+
def container
|
|
132
|
+
fields[:container]
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Queries whether any environment information was able to be
|
|
136
|
+
# detected.
|
|
137
|
+
#
|
|
138
|
+
# @return [ true | false ] if any environment information was
|
|
139
|
+
# detected.
|
|
140
|
+
def present?
|
|
141
|
+
@name || fields.any?
|
|
142
|
+
end
|
|
143
|
+
|
|
118
144
|
# Queries whether the current environment is a valid FaaS environment.
|
|
119
145
|
#
|
|
120
146
|
# @return [ true | false ] whether the environment is a FaaS
|
|
@@ -159,14 +185,11 @@ module Mongo
|
|
|
159
185
|
@name == 'vercel'
|
|
160
186
|
end
|
|
161
187
|
|
|
162
|
-
# Compiles the detected environment information into a Hash.
|
|
163
|
-
# always include a {{name}} key, but may include other keys as well,
|
|
164
|
-
# depending on the detected FaaS environment. (See the handshake
|
|
165
|
-
# spec for details.)
|
|
188
|
+
# Compiles the detected environment information into a Hash.
|
|
166
189
|
#
|
|
167
190
|
# @return [ Hash ] the detected environment information.
|
|
168
191
|
def to_h
|
|
169
|
-
fields.merge(name: name)
|
|
192
|
+
name ? fields.merge(name: name) : fields
|
|
170
193
|
end
|
|
171
194
|
|
|
172
195
|
private
|
|
@@ -192,6 +215,38 @@ module Mongo
|
|
|
192
215
|
names.first
|
|
193
216
|
end
|
|
194
217
|
|
|
218
|
+
# Looks for the presence of a container. Currently can detect
|
|
219
|
+
# Docker (by the existence of a .dockerenv file in the root
|
|
220
|
+
# directory) and Kubernetes (by the existence of the KUBERNETES_SERVICE_HOST
|
|
221
|
+
# environment variable).
|
|
222
|
+
def detect_container
|
|
223
|
+
runtime = docker_present? && 'docker'
|
|
224
|
+
orchestrator = kubernetes_present? && 'kubernetes'
|
|
225
|
+
|
|
226
|
+
return unless runtime || orchestrator
|
|
227
|
+
|
|
228
|
+
fields[:container] = {}
|
|
229
|
+
fields[:container][:runtime] = runtime if runtime
|
|
230
|
+
fields[:container][:orchestrator] = orchestrator if orchestrator
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Checks for the existence of a .dockerenv in the root directory.
|
|
234
|
+
def docker_present?
|
|
235
|
+
File.exist?(dockerenv_path)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Implementing this as a method so that it can be mocked in tests, to
|
|
239
|
+
# test the presence or absence of Docker.
|
|
240
|
+
def dockerenv_path
|
|
241
|
+
DOCKERENV_PATH
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Checks for the presence of a non-empty KUBERNETES_SERVICE_HOST
|
|
245
|
+
# environment variable.
|
|
246
|
+
def kubernetes_present?
|
|
247
|
+
!ENV['KUBERNETES_SERVICE_HOST'].to_s.empty?
|
|
248
|
+
end
|
|
249
|
+
|
|
195
250
|
# Determines whether the named environment variable exists, and (if
|
|
196
251
|
# a pattern has been declared for that descriminator) whether the
|
|
197
252
|
# pattern matches the value of the variable.
|
|
@@ -212,10 +267,10 @@ module Mongo
|
|
|
212
267
|
# Extracts environment information from the current environment
|
|
213
268
|
# variables, based on the detected FaaS environment. Populates the
|
|
214
269
|
# {{@fields}} instance variable.
|
|
215
|
-
def
|
|
270
|
+
def populate_faas_fields
|
|
216
271
|
return unless name
|
|
217
272
|
|
|
218
|
-
|
|
273
|
+
FIELDS[name].each_with_object(@fields) do |(var, defn), fields|
|
|
219
274
|
fields[defn[:field]] = extract_field(var, defn)
|
|
220
275
|
end
|
|
221
276
|
end
|
|
@@ -187,13 +187,14 @@ module Mongo
|
|
|
187
187
|
}
|
|
188
188
|
end
|
|
189
189
|
|
|
190
|
-
# Returns the environment doc describing the current
|
|
190
|
+
# Returns the environment doc describing the current execution
|
|
191
|
+
# environment.
|
|
191
192
|
#
|
|
192
|
-
# @return [ Hash | nil ] the environment doc (or nil if
|
|
193
|
-
#
|
|
193
|
+
# @return [ Hash | nil ] the environment doc (or nil if no relevant
|
|
194
|
+
# environment info was detected)
|
|
194
195
|
def env_doc
|
|
195
196
|
env = Environment.new
|
|
196
|
-
env.
|
|
197
|
+
env.present? ? env.to_h : nil
|
|
197
198
|
end
|
|
198
199
|
|
|
199
200
|
def type
|
|
@@ -48,6 +48,7 @@ module Mongo
|
|
|
48
48
|
# provided by the client during findAndModify operations, requiring the
|
|
49
49
|
# driver to raise client-side errors when those options are provided.
|
|
50
50
|
find_and_modify_option_validation: 8,
|
|
51
|
+
sharded_transactions: 8,
|
|
51
52
|
transactions: 7,
|
|
52
53
|
scram_sha_256: 7,
|
|
53
54
|
array_filters: 6,
|
|
@@ -110,6 +110,24 @@ module Mongo
|
|
|
110
110
|
|
|
111
111
|
private
|
|
112
112
|
|
|
113
|
+
# Sends the hello command to the server, then receive and deserialize
|
|
114
|
+
# the response.
|
|
115
|
+
#
|
|
116
|
+
# This method is extracted to be mocked in the tests.
|
|
117
|
+
#
|
|
118
|
+
# @param [ Protocol::Message ] Command that should be sent to a server
|
|
119
|
+
# for handshake purposes.
|
|
120
|
+
#
|
|
121
|
+
# @return [ Mongo::Protocol::Reply ] Deserialized server response.
|
|
122
|
+
def get_handshake_response(hello_command)
|
|
123
|
+
@server.round_trip_time_averager.measure do
|
|
124
|
+
add_server_diagnostics do
|
|
125
|
+
socket.write(hello_command.serialize.to_s)
|
|
126
|
+
Protocol::Message.deserialize(socket, Protocol::Message::MAX_MESSAGE_SIZE)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
113
131
|
# @param [ BSON::Document | nil ] speculative_auth_doc The document to
|
|
114
132
|
# provide in speculativeAuthenticate field of handshake command.
|
|
115
133
|
#
|
|
@@ -131,12 +149,7 @@ module Mongo
|
|
|
131
149
|
doc = nil
|
|
132
150
|
@server.handle_handshake_failure! do
|
|
133
151
|
begin
|
|
134
|
-
response =
|
|
135
|
-
add_server_diagnostics do
|
|
136
|
-
socket.write(hello_command.serialize.to_s)
|
|
137
|
-
Protocol::Message.deserialize(socket, Protocol::Message::MAX_MESSAGE_SIZE)
|
|
138
|
-
end
|
|
139
|
-
end
|
|
152
|
+
response = get_handshake_response(hello_command)
|
|
140
153
|
result = Operation::Result.new([response])
|
|
141
154
|
result.validate!
|
|
142
155
|
doc = result.documents.first
|
|
@@ -164,6 +164,10 @@ module Mongo
|
|
|
164
164
|
# for mongos pinning. Added in version 2.10.0.
|
|
165
165
|
# @param [ true | false ] write_aggregation Whether we need a server that
|
|
166
166
|
# supports writing aggregations (e.g. with $merge/$out) on secondaries.
|
|
167
|
+
# @param [ Array<Server> ] deprioritized A list of servers that should
|
|
168
|
+
# be selected from only if no other servers are available. This is
|
|
169
|
+
# used to avoid selecting the same server twice in a row when
|
|
170
|
+
# retrying a command.
|
|
167
171
|
#
|
|
168
172
|
# @return [ Mongo::Server ] A server matching the server preference.
|
|
169
173
|
#
|
|
@@ -174,8 +178,8 @@ module Mongo
|
|
|
174
178
|
# lint mode is enabled.
|
|
175
179
|
#
|
|
176
180
|
# @since 2.0.0
|
|
177
|
-
def select_server(cluster, ping = nil, session = nil, write_aggregation: false)
|
|
178
|
-
select_server_impl(cluster, ping, session, write_aggregation).tap do |server|
|
|
181
|
+
def select_server(cluster, ping = nil, session = nil, write_aggregation: false, deprioritized: [])
|
|
182
|
+
select_server_impl(cluster, ping, session, write_aggregation, deprioritized).tap do |server|
|
|
179
183
|
if Lint.enabled? && !server.pool.ready?
|
|
180
184
|
raise Error::LintError, 'Server selector returning a server with a pool which is not ready'
|
|
181
185
|
end
|
|
@@ -183,7 +187,7 @@ module Mongo
|
|
|
183
187
|
end
|
|
184
188
|
|
|
185
189
|
# Parameters and return values are the same as for select_server.
|
|
186
|
-
private def select_server_impl(cluster, ping, session, write_aggregation)
|
|
190
|
+
private def select_server_impl(cluster, ping, session, write_aggregation, deprioritized)
|
|
187
191
|
if cluster.topology.is_a?(Cluster::Topology::LoadBalanced)
|
|
188
192
|
return cluster.servers.first
|
|
189
193
|
end
|
|
@@ -266,7 +270,7 @@ module Mongo
|
|
|
266
270
|
end
|
|
267
271
|
end
|
|
268
272
|
|
|
269
|
-
server = try_select_server(cluster, write_aggregation: write_aggregation)
|
|
273
|
+
server = try_select_server(cluster, write_aggregation: write_aggregation, deprioritized: deprioritized)
|
|
270
274
|
|
|
271
275
|
if server
|
|
272
276
|
unless cluster.topology.compatible?
|
|
@@ -321,11 +325,15 @@ module Mongo
|
|
|
321
325
|
# an eligible server.
|
|
322
326
|
# @param [ true | false ] write_aggregation Whether we need a server that
|
|
323
327
|
# supports writing aggregations (e.g. with $merge/$out) on secondaries.
|
|
328
|
+
# @param [ Array<Server> ] deprioritized A list of servers that should
|
|
329
|
+
# be selected from only if no other servers are available. This is
|
|
330
|
+
# used to avoid selecting the same server twice in a row when
|
|
331
|
+
# retrying a command.
|
|
324
332
|
#
|
|
325
333
|
# @return [ Server | nil ] A suitable server, if one exists.
|
|
326
334
|
#
|
|
327
335
|
# @api private
|
|
328
|
-
def try_select_server(cluster, write_aggregation: false)
|
|
336
|
+
def try_select_server(cluster, write_aggregation: false, deprioritized: [])
|
|
329
337
|
servers = if write_aggregation && cluster.replica_set?
|
|
330
338
|
# 1. Check if ALL servers in cluster support secondary writes.
|
|
331
339
|
is_write_supported = cluster.servers.reduce(true) do |res, server|
|
|
@@ -347,7 +355,7 @@ module Mongo
|
|
|
347
355
|
# by the selector (e.g. for secondary preferred, the first
|
|
348
356
|
# server may be a secondary and the second server may be primary)
|
|
349
357
|
# and we should take the first server here respecting the order
|
|
350
|
-
server = servers
|
|
358
|
+
server = suitable_server(servers, deprioritized)
|
|
351
359
|
|
|
352
360
|
if server
|
|
353
361
|
if Lint.enabled?
|
|
@@ -418,6 +426,24 @@ module Mongo
|
|
|
418
426
|
|
|
419
427
|
private
|
|
420
428
|
|
|
429
|
+
# Returns a server from the list of servers that is suitable for
|
|
430
|
+
# executing the operation.
|
|
431
|
+
#
|
|
432
|
+
# @param [ Array<Server> ] servers The candidate servers.
|
|
433
|
+
# @param [ Array<Server> ] deprioritized A list of servers that should
|
|
434
|
+
# be selected from only if no other servers are available.
|
|
435
|
+
#
|
|
436
|
+
# @return [ Server | nil ] The suitable server or nil if no suitable
|
|
437
|
+
# server is available.
|
|
438
|
+
def suitable_server(servers, deprioritized)
|
|
439
|
+
preferred = servers - deprioritized
|
|
440
|
+
if preferred.empty?
|
|
441
|
+
servers.first
|
|
442
|
+
else
|
|
443
|
+
preferred.first
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
|
|
421
447
|
# Convert this server preference definition into a format appropriate
|
|
422
448
|
# for sending to a MongoDB server (i.e., as a command field).
|
|
423
449
|
#
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (C) 2024 MongoDB Inc.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
module Mongo
|
|
18
|
+
class Session
|
|
19
|
+
class ServerSession
|
|
20
|
+
# Functionality for manipulating and querying a session's
|
|
21
|
+
# "dirty" state, per the last paragraph at
|
|
22
|
+
# https://github.com/mongodb/specifications/blob/master/source/sessions/driver-sessions.rst#server-session-pool
|
|
23
|
+
#
|
|
24
|
+
# If a driver has a server session pool and a network error is
|
|
25
|
+
# encountered when executing any command with a ClientSession, the
|
|
26
|
+
# driver MUST mark the associated ServerSession as dirty. Dirty server
|
|
27
|
+
# sessions are discarded when returned to the server session pool. It is
|
|
28
|
+
# valid for a dirty session to be used for subsequent commands (e.g. an
|
|
29
|
+
# implicit retry attempt, a later command in a bulk write, or a later
|
|
30
|
+
# operation on an explicit session), however, it MUST remain dirty for
|
|
31
|
+
# the remainder of its lifetime regardless if later commands succeed.
|
|
32
|
+
#
|
|
33
|
+
# @api private
|
|
34
|
+
module Dirtyable
|
|
35
|
+
# Query whether the server session has been marked dirty or not.
|
|
36
|
+
#
|
|
37
|
+
# @return [ true | false ] the server session's dirty state
|
|
38
|
+
def dirty?
|
|
39
|
+
@dirty
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Mark the server session as dirty (the default) or clean.
|
|
43
|
+
#
|
|
44
|
+
# @param [ true | false ] mark whether the mark the server session
|
|
45
|
+
# dirty or not.
|
|
46
|
+
def dirty!(mark = true)
|
|
47
|
+
@dirty = mark
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
# See the License for the specific language governing permissions and
|
|
16
16
|
# limitations under the License.
|
|
17
17
|
|
|
18
|
+
require 'mongo/session/server_session/dirtyable'
|
|
19
|
+
|
|
18
20
|
module Mongo
|
|
19
21
|
|
|
20
22
|
class Session
|
|
@@ -25,6 +27,7 @@ module Mongo
|
|
|
25
27
|
#
|
|
26
28
|
# @since 2.5.0
|
|
27
29
|
class ServerSession
|
|
30
|
+
include Dirtyable
|
|
28
31
|
|
|
29
32
|
# Regex for removing dashes from the UUID string.
|
|
30
33
|
#
|
|
@@ -25,21 +25,6 @@ module Mongo
|
|
|
25
25
|
#
|
|
26
26
|
# @since 2.5.0
|
|
27
27
|
class SessionPool
|
|
28
|
-
|
|
29
|
-
# Create a SessionPool.
|
|
30
|
-
#
|
|
31
|
-
# @example
|
|
32
|
-
# SessionPool.create(cluster)
|
|
33
|
-
#
|
|
34
|
-
# @param [ Mongo::Cluster ] cluster The cluster that will be associated with this
|
|
35
|
-
# session pool.
|
|
36
|
-
#
|
|
37
|
-
# @since 2.5.0
|
|
38
|
-
def self.create(cluster)
|
|
39
|
-
pool = new(cluster)
|
|
40
|
-
cluster.instance_variable_set(:@session_pool, pool)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
28
|
# Initialize a SessionPool.
|
|
44
29
|
#
|
|
45
30
|
# @example
|
|
@@ -105,9 +90,7 @@ module Mongo
|
|
|
105
90
|
|
|
106
91
|
@mutex.synchronize do
|
|
107
92
|
prune!
|
|
108
|
-
|
|
109
|
-
@queue.unshift(session)
|
|
110
|
-
end
|
|
93
|
+
@queue.unshift(session) if return_to_queue?(session)
|
|
111
94
|
end
|
|
112
95
|
end
|
|
113
96
|
|
|
@@ -136,6 +119,17 @@ module Mongo
|
|
|
136
119
|
|
|
137
120
|
private
|
|
138
121
|
|
|
122
|
+
# Query whether the given session is okay to return to the
|
|
123
|
+
# pool's queue.
|
|
124
|
+
#
|
|
125
|
+
# @param [ Session::ServerSession ] session the session to query
|
|
126
|
+
#
|
|
127
|
+
# @return [ true | false ] whether to return the session to the
|
|
128
|
+
# queue.
|
|
129
|
+
def return_to_queue?(session)
|
|
130
|
+
!session.dirty? && !about_to_expire?(session)
|
|
131
|
+
end
|
|
132
|
+
|
|
139
133
|
def about_to_expire?(session)
|
|
140
134
|
if session.nil?
|
|
141
135
|
raise ArgumentError, 'session cannot be nil'
|