mongo 2.8.0 → 2.9.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/Rakefile +12 -0
- data/lib/mongo.rb +15 -1
- data/lib/mongo/address/ipv6.rb +0 -2
- data/lib/mongo/auth/scram/conversation.rb +0 -3
- data/lib/mongo/bulk_write/result_combiner.rb +12 -2
- data/lib/mongo/client.rb +59 -6
- data/lib/mongo/cluster.rb +19 -8
- data/lib/mongo/cluster/reapers/cursor_reaper.rb +0 -2
- data/lib/mongo/cluster/reapers/socket_reaper.rb +12 -9
- data/lib/mongo/collection.rb +1 -1
- data/lib/mongo/collection/view/aggregation.rb +5 -1
- data/lib/mongo/collection/view/builder/map_reduce.rb +1 -1
- data/lib/mongo/collection/view/change_stream.rb +30 -10
- data/lib/mongo/collection/view/iterable.rb +13 -6
- data/lib/mongo/collection/view/map_reduce.rb +12 -10
- data/lib/mongo/collection/view/readable.rb +19 -14
- data/lib/mongo/cursor.rb +12 -8
- data/lib/mongo/database.rb +10 -7
- data/lib/mongo/database/view.rb +18 -11
- data/lib/mongo/error.rb +2 -2
- data/lib/mongo/error/connection_check_out_timeout.rb +49 -0
- data/lib/mongo/error/operation_failure.rb +9 -9
- data/lib/mongo/error/parser.rb +25 -3
- data/lib/mongo/error/pool_closed_error.rb +43 -0
- data/lib/mongo/error/sdam_error_detection.rb +18 -0
- data/lib/mongo/grid/file/chunk.rb +0 -2
- data/lib/mongo/grid/fs_bucket.rb +26 -12
- data/lib/mongo/grid/stream/read.rb +36 -21
- data/lib/mongo/index/view.rb +11 -7
- data/lib/mongo/logger.rb +0 -2
- data/lib/mongo/monitoring.rb +31 -0
- data/lib/mongo/monitoring/cmap_log_subscriber.rb +53 -0
- data/lib/mongo/monitoring/event.rb +1 -0
- data/lib/mongo/monitoring/event/cmap.rb +25 -0
- data/lib/mongo/monitoring/event/cmap/base.rb +28 -0
- data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +78 -0
- data/lib/mongo/monitoring/event/cmap/connection_check_out_started.rb +56 -0
- data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +63 -0
- data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +64 -0
- data/lib/mongo/monitoring/event/cmap/connection_closed.rb +103 -0
- data/lib/mongo/monitoring/event/cmap/connection_created.rb +64 -0
- data/lib/mongo/monitoring/event/cmap/connection_ready.rb +64 -0
- data/lib/mongo/monitoring/event/cmap/pool_cleared.rb +57 -0
- data/lib/mongo/monitoring/event/cmap/pool_closed.rb +57 -0
- data/lib/mongo/monitoring/event/cmap/pool_created.rb +63 -0
- data/lib/mongo/monitoring/event/command_started.rb +12 -3
- data/lib/mongo/monitoring/publishable.rb +10 -2
- data/lib/mongo/operation.rb +0 -1
- data/lib/mongo/operation/find/legacy/result.rb +1 -0
- data/lib/mongo/operation/list_collections/result.rb +7 -1
- data/lib/mongo/operation/result.rb +10 -1
- data/lib/mongo/operation/shared/executable.rb +15 -0
- data/lib/mongo/operation/shared/result/use_legacy_error_parser.rb +29 -0
- data/lib/mongo/operation/shared/specifiable.rb +0 -16
- data/lib/mongo/operation/update/legacy/result.rb +1 -0
- data/lib/mongo/protocol/compressed.rb +0 -2
- data/lib/mongo/protocol/msg.rb +25 -2
- data/lib/mongo/retryable.rb +171 -33
- data/lib/mongo/server.rb +26 -7
- data/lib/mongo/server/app_metadata.rb +0 -2
- data/lib/mongo/server/connectable.rb +8 -2
- data/lib/mongo/server/connection.rb +83 -13
- data/lib/mongo/server/connection_base.rb +1 -1
- data/lib/mongo/server/connection_pool.rb +439 -43
- data/lib/mongo/server/monitor/connection.rb +4 -1
- data/lib/mongo/session.rb +37 -5
- data/lib/mongo/session/session_pool.rb +2 -2
- data/lib/mongo/socket.rb +0 -2
- data/lib/mongo/socket/ssl.rb +0 -2
- data/lib/mongo/uri.rb +127 -66
- data/lib/mongo/uri/srv_protocol.rb +35 -13
- data/lib/mongo/version.rb +1 -1
- data/spec/README.md +190 -63
- data/spec/integration/change_stream_spec.rb +64 -0
- data/spec/integration/command_spec.rb +0 -7
- data/spec/integration/error_detection_spec.rb +39 -0
- data/spec/integration/read_concern.rb +83 -0
- data/spec/integration/retryable_writes_spec.rb +6 -50
- data/spec/integration/sdam_error_handling_spec.rb +60 -7
- data/spec/integration/ssl_uri_options_spec.rb +24 -0
- data/spec/integration/step_down_spec.rb +197 -0
- data/spec/lite_spec_helper.rb +4 -0
- data/spec/mongo/client_construction_spec.rb +42 -17
- data/spec/mongo/client_spec.rb +32 -1
- data/spec/mongo/cluster/socket_reaper_spec.rb +2 -2
- data/spec/mongo/cluster_spec.rb +36 -2
- data/spec/mongo/collection/view/aggregation_spec.rb +2 -0
- data/spec/mongo/collection/view/change_stream_spec.rb +28 -28
- data/spec/mongo/collection/view/readable_spec.rb +1 -1
- data/spec/mongo/collection/view_spec.rb +3 -1
- data/spec/mongo/cursor_spec.rb +5 -5
- data/spec/mongo/error/parser_spec.rb +61 -1
- data/spec/mongo/grid/stream/read_spec.rb +2 -2
- data/spec/mongo/monitoring/event/cmap/connection_check_out_failed_spec.rb +23 -0
- data/spec/mongo/monitoring/event/cmap/connection_check_out_started_spec.rb +19 -0
- data/spec/mongo/monitoring/event/cmap/connection_checked_in_spec.rb +23 -0
- data/spec/mongo/monitoring/event/cmap/connection_checked_out_spec.rb +23 -0
- data/spec/mongo/monitoring/event/cmap/connection_closed_spec.rb +27 -0
- data/spec/mongo/monitoring/event/cmap/connection_created_spec.rb +24 -0
- data/spec/mongo/monitoring/event/cmap/connection_ready_spec.rb +24 -0
- data/spec/mongo/monitoring/event/cmap/pool_cleared_spec.rb +19 -0
- data/spec/mongo/monitoring/event/cmap/pool_closed_spec.rb +19 -0
- data/spec/mongo/monitoring/event/cmap/pool_created_spec.rb +26 -0
- data/spec/mongo/operation/delete/bulk_spec.rb +1 -6
- data/spec/mongo/operation/delete/command_spec.rb +1 -1
- data/spec/mongo/operation/delete/op_msg_spec.rb +1 -1
- data/spec/mongo/operation/delete_spec.rb +4 -4
- data/spec/mongo/operation/insert/bulk_spec.rb +1 -1
- data/spec/mongo/operation/insert/command_spec.rb +1 -1
- data/spec/mongo/operation/insert/op_msg_spec.rb +1 -1
- data/spec/mongo/operation/update/bulk_spec.rb +1 -1
- data/spec/mongo/operation/update/command_spec.rb +2 -2
- data/spec/mongo/operation/update/op_msg_spec.rb +2 -2
- data/spec/mongo/protocol/msg_spec.rb +11 -0
- data/spec/mongo/retryable_spec.rb +78 -25
- data/spec/mongo/server/connection_pool_spec.rb +661 -126
- data/spec/mongo/server/connection_spec.rb +55 -7
- data/spec/mongo/server_spec.rb +5 -0
- data/spec/mongo/uri/srv_protocol_spec.rb +135 -2
- data/spec/mongo/uri_option_parsing_spec.rb +511 -0
- data/spec/mongo/uri_spec.rb +42 -6
- data/spec/spec_helper.rb +1 -84
- data/spec/spec_tests/cmap_spec.rb +50 -0
- data/spec/spec_tests/command_monitoring_spec.rb +7 -18
- data/spec/spec_tests/crud_spec.rb +3 -49
- data/spec/spec_tests/data/cmap/connection-must-have-id.yml +21 -0
- data/spec/spec_tests/data/cmap/connection-must-order-ids.yml +21 -0
- data/spec/spec_tests/data/cmap/pool-checkin-destroy-closed.yml +24 -0
- data/spec/spec_tests/data/cmap/pool-checkin-destroy-stale.yml +24 -0
- data/spec/spec_tests/data/cmap/pool-checkin-make-available.yml +21 -0
- data/spec/spec_tests/data/cmap/pool-checkin.yml +18 -0
- data/spec/spec_tests/data/cmap/pool-checkout-connection.yml +13 -0
- data/spec/spec_tests/data/cmap/pool-checkout-error-closed.yml +28 -0
- data/spec/spec_tests/data/cmap/pool-checkout-multiple.yml +34 -0
- data/spec/spec_tests/data/cmap/pool-checkout-no-idle.yml +31 -0
- data/spec/spec_tests/data/cmap/pool-checkout-no-stale.yml +29 -0
- data/spec/spec_tests/data/cmap/pool-close-destroy-conns.yml +26 -0
- data/spec/spec_tests/data/cmap/pool-close.yml +11 -0
- data/spec/spec_tests/data/cmap/pool-create-max-size.yml +56 -0
- data/spec/spec_tests/data/cmap/pool-create-min-size.yml +27 -0
- data/spec/spec_tests/data/cmap/pool-create-with-options.yml +20 -0
- data/spec/spec_tests/data/cmap/pool-create.yml +12 -0
- data/spec/spec_tests/data/cmap/wait-queue-fairness.yml +94 -0
- data/spec/spec_tests/data/cmap/wait-queue-timeout.yml +41 -0
- data/spec/spec_tests/data/retryable_reads/aggregate-serverErrors.yml +157 -0
- data/spec/spec_tests/data/retryable_reads/aggregate.yml +87 -0
- data/spec/spec_tests/data/retryable_reads/changeStreams-client.watch-serverErrors.yml +149 -0
- data/spec/spec_tests/data/retryable_reads/changeStreams-client.watch.yml +61 -0
- data/spec/spec_tests/data/retryable_reads/changeStreams-db.coll.watch-serverErrors.yml +149 -0
- data/spec/spec_tests/data/retryable_reads/changeStreams-db.coll.watch.yml +65 -0
- data/spec/spec_tests/data/retryable_reads/changeStreams-db.watch-serverErrors.yml +153 -0
- data/spec/spec_tests/data/retryable_reads/changeStreams-db.watch.yml +61 -0
- data/spec/spec_tests/data/retryable_reads/count-serverErrors.yml +150 -0
- data/spec/spec_tests/data/retryable_reads/count.yml +64 -0
- data/spec/spec_tests/data/retryable_reads/countDocuments-serverErrors.yml +150 -0
- data/spec/spec_tests/data/retryable_reads/countDocuments.yml +64 -0
- data/spec/spec_tests/data/retryable_reads/distinct-serverErrors.yml +156 -0
- data/spec/spec_tests/data/retryable_reads/distinct.yml +71 -0
- data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-serverErrors.yml +148 -0
- data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount.yml +62 -0
- data/spec/spec_tests/data/retryable_reads/find-serverErrors.yml +160 -0
- data/spec/spec_tests/data/retryable_reads/find.yml +86 -0
- data/spec/spec_tests/data/retryable_reads/findOne-serverErrors.yml +154 -0
- data/spec/spec_tests/data/retryable_reads/findOne.yml +68 -0
- data/spec/spec_tests/data/retryable_reads/gridfs-download-serverErrors.yml +173 -0
- data/spec/spec_tests/data/retryable_reads/gridfs-download.yml +79 -0
- data/spec/spec_tests/data/retryable_reads/gridfs-downloadByName-serverErrors.yml +174 -0
- data/spec/spec_tests/data/retryable_reads/gridfs-downloadByName.yml +79 -0
- data/spec/spec_tests/data/retryable_reads/listCollectionNames-serverErrors.yml +143 -0
- data/spec/spec_tests/data/retryable_reads/listCollectionNames.yml +59 -0
- data/spec/spec_tests/data/retryable_reads/listCollectionObjects-serverErrors.yml +144 -0
- data/spec/spec_tests/data/retryable_reads/listCollectionObjects.yml +59 -0
- data/spec/spec_tests/data/retryable_reads/listCollections-serverErrors.yml +143 -0
- data/spec/spec_tests/data/retryable_reads/listCollections.yml +59 -0
- data/spec/spec_tests/data/retryable_reads/listDatabaseNames-serverErrors.yml +143 -0
- data/spec/spec_tests/data/retryable_reads/listDatabaseNames.yml +59 -0
- data/spec/spec_tests/data/retryable_reads/listDatabaseObjects-serverErrors.yml +144 -0
- data/spec/spec_tests/data/retryable_reads/listDatabaseObjects.yml +59 -0
- data/spec/spec_tests/data/retryable_reads/listDatabases-serverErrors.yml +144 -0
- data/spec/spec_tests/data/retryable_reads/listDatabases.yml +59 -0
- data/spec/spec_tests/data/retryable_reads/listIndexNames-serverErrors.yml +144 -0
- data/spec/spec_tests/data/retryable_reads/listIndexNames.yml +60 -0
- data/spec/spec_tests/data/retryable_reads/listIndexes-serverErrors.yml +145 -0
- data/spec/spec_tests/data/retryable_reads/listIndexes.yml +60 -0
- data/spec/spec_tests/data/retryable_reads/mapReduce.yml +60 -0
- data/spec/spec_tests/data/retryable_writes/bulkWrite-serverErrors.yml +10 -7
- data/spec/spec_tests/data/retryable_writes/bulkWrite.yml +15 -22
- data/spec/spec_tests/data/retryable_writes/deleteMany.yml +22 -0
- data/spec/spec_tests/data/retryable_writes/deleteOne-serverErrors.yml +8 -7
- data/spec/spec_tests/data/retryable_writes/deleteOne.yml +5 -8
- data/spec/spec_tests/data/retryable_writes/findOneAndDelete-serverErrors.yml +8 -7
- data/spec/spec_tests/data/retryable_writes/findOneAndDelete.yml +5 -8
- data/spec/spec_tests/data/retryable_writes/findOneAndReplace-serverErrors.yml +8 -7
- data/spec/spec_tests/data/retryable_writes/findOneAndReplace.yml +5 -8
- data/spec/spec_tests/data/retryable_writes/findOneAndUpdate-serverErrors.yml +8 -7
- data/spec/spec_tests/data/retryable_writes/findOneAndUpdate.yml +5 -8
- data/spec/spec_tests/data/retryable_writes/insertMany-serverErrors.yml +8 -7
- data/spec/spec_tests/data/retryable_writes/insertMany.yml +5 -8
- data/spec/spec_tests/data/retryable_writes/insertOne-serverErrors.yml +10 -45
- data/spec/spec_tests/data/retryable_writes/insertOne.yml +5 -8
- data/spec/spec_tests/data/retryable_writes/replaceOne-serverErrors.yml +8 -7
- data/spec/spec_tests/data/retryable_writes/replaceOne.yml +5 -8
- data/spec/spec_tests/data/retryable_writes/updateMany.yml +27 -0
- data/spec/spec_tests/data/retryable_writes/updateOne-serverErrors.yml +8 -7
- data/spec/spec_tests/data/retryable_writes/updateOne.yml +5 -14
- data/spec/spec_tests/data/transactions/abort.yml +7 -2
- data/spec/spec_tests/data/transactions/bulk.yml +7 -2
- data/spec/spec_tests/data/transactions/causal-consistency.yml +11 -4
- data/spec/spec_tests/data/transactions/commit.yml +11 -4
- data/spec/spec_tests/data/transactions/count.yml +64 -0
- data/spec/spec_tests/data/transactions/delete.yml +7 -2
- data/spec/spec_tests/data/transactions/error-labels.yml +8 -2
- data/spec/spec_tests/data/transactions/errors.yml +7 -2
- data/spec/spec_tests/data/transactions/findOneAndDelete.yml +7 -2
- data/spec/spec_tests/data/transactions/findOneAndReplace.yml +7 -2
- data/spec/spec_tests/data/transactions/findOneAndUpdate.yml +7 -2
- data/spec/spec_tests/data/transactions/insert.yml +9 -2
- data/spec/spec_tests/data/transactions/isolation.yml +7 -2
- data/spec/spec_tests/data/transactions/read-concern.yml +15 -6
- data/spec/spec_tests/data/transactions/read-pref.yml +7 -2
- data/spec/spec_tests/data/transactions/reads.yml +8 -48
- data/spec/spec_tests/data/transactions/retryable-abort.yml +7 -2
- data/spec/spec_tests/data/transactions/retryable-commit.yml +7 -2
- data/spec/spec_tests/data/transactions/retryable-writes.yml +7 -2
- data/spec/spec_tests/data/transactions/run-command.yml +7 -2
- data/spec/spec_tests/data/transactions/transaction-options.yml +7 -2
- data/spec/spec_tests/data/transactions/update.yml +7 -2
- data/spec/spec_tests/data/transactions/write-concern.yml +7 -2
- data/spec/spec_tests/data/transactions_api/callback-aborts.yml +6 -1
- data/spec/spec_tests/data/transactions_api/callback-commits.yml +6 -1
- data/spec/spec_tests/data/transactions_api/callback-retry.yml +6 -1
- data/spec/spec_tests/data/transactions_api/commit-retry.yml +6 -1
- data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror-4.2.yml +6 -3
- data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror.yml +6 -1
- data/spec/spec_tests/data/transactions_api/commit-writeconcernerror.yml +6 -1
- data/spec/spec_tests/data/transactions_api/commit.yml +6 -1
- data/spec/spec_tests/data/transactions_api/transaction-options.yml +6 -1
- data/spec/spec_tests/retryable_reads_spec.rb +11 -0
- data/spec/spec_tests/retryable_writes_spec.rb +4 -69
- data/spec/spec_tests/transactions_api_spec.rb +42 -37
- data/spec/spec_tests/transactions_spec.rb +42 -33
- data/spec/support/authorization.rb +12 -0
- data/spec/support/change_streams/operation.rb +1 -1
- data/spec/support/client_registry.rb +20 -0
- data/spec/support/cluster_config.rb +16 -15
- data/spec/support/cluster_tools.rb +346 -0
- data/spec/support/cmap.rb +367 -0
- data/spec/support/cmap/verifier.rb +46 -0
- data/spec/support/command_monitoring.rb +4 -6
- data/spec/support/common_shortcuts.rb +6 -0
- data/spec/support/connection_string.rb +2 -2
- data/spec/support/crud.rb +171 -184
- data/spec/support/crud/operation.rb +43 -0
- data/spec/support/crud/outcome.rb +53 -0
- data/spec/support/crud/read.rb +102 -12
- data/spec/support/crud/requirement.rb +69 -0
- data/spec/support/crud/spec.rb +68 -0
- data/spec/support/crud/test.rb +141 -0
- data/spec/support/crud/verifier.rb +96 -18
- data/spec/support/crud/write.rb +18 -3
- data/spec/support/event_subscriber.rb +15 -0
- data/spec/support/primary_socket.rb +2 -2
- data/spec/support/spec_config.rb +89 -20
- data/spec/support/transactions.rb +2 -306
- data/spec/support/transactions/operation.rb +7 -7
- data/spec/support/transactions/spec.rb +28 -0
- data/spec/support/transactions/test.rb +191 -0
- data/spec/support/utils.rb +123 -0
- metadata +202 -9
- metadata.gz.sig +0 -0
- data/lib/mongo/server/connection_pool/queue.rb +0 -359
- data/spec/mongo/server/connection_pool/queue_spec.rb +0 -353
- data/spec/support/transactions/verifier.rb +0 -97
@@ -8,7 +8,7 @@ describe Mongo::Operation::Update::Command do
|
|
8
8
|
:upsert => false }] }
|
9
9
|
|
10
10
|
let(:write_concern) do
|
11
|
-
Mongo::WriteConcern.get(
|
11
|
+
Mongo::WriteConcern.get(w: :majority)
|
12
12
|
end
|
13
13
|
let(:session) { nil }
|
14
14
|
let(:spec) do
|
@@ -54,7 +54,7 @@ describe Mongo::Operation::Update::Command do
|
|
54
54
|
{ :updates => other_updates,
|
55
55
|
:db_name => SpecConfig.instance.test_db,
|
56
56
|
:coll_name => TEST_COLL,
|
57
|
-
:write_concern => Mongo::WriteConcern.get(
|
57
|
+
:write_concern => Mongo::WriteConcern.get(w: :majority),
|
58
58
|
:ordered => true
|
59
59
|
}
|
60
60
|
end
|
@@ -8,7 +8,7 @@ describe Mongo::Operation::Update::OpMsg do
|
|
8
8
|
:upsert => false }] }
|
9
9
|
|
10
10
|
let(:write_concern) do
|
11
|
-
Mongo::WriteConcern.get(
|
11
|
+
Mongo::WriteConcern.get(w: :majority)
|
12
12
|
end
|
13
13
|
let(:session) { nil }
|
14
14
|
let(:spec) do
|
@@ -54,7 +54,7 @@ describe Mongo::Operation::Update::OpMsg do
|
|
54
54
|
{ :updates => other_updates,
|
55
55
|
:db_name => SpecConfig.instance.test_db,
|
56
56
|
:coll_name => TEST_COLL,
|
57
|
-
:write_concern => Mongo::WriteConcern.get(
|
57
|
+
:write_concern => Mongo::WriteConcern.get(w: :majority),
|
58
58
|
:ordered => true
|
59
59
|
}
|
60
60
|
end
|
@@ -58,6 +58,17 @@ describe Mongo::Protocol::Msg do
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
61
|
+
|
62
|
+
context 'with user-provided and driver-generated keys in global_args' do
|
63
|
+
let(:global_args) do
|
64
|
+
{ 'ping' => 1, 'lsid' => '__lsid__', 'a' => 'b', '$clusterTime' => '__ct__',
|
65
|
+
'signature' => '__signature__', 'd' => 'f'}
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'reorders global_args for better logging' do
|
69
|
+
expect(message.payload[:command].keys).to eq(%w(ping a d lsid $clusterTime signature))
|
70
|
+
end
|
71
|
+
end
|
61
72
|
end
|
62
73
|
|
63
74
|
describe '#==' do
|
@@ -3,23 +3,31 @@ require 'spec_helper'
|
|
3
3
|
class RetryableTestConsumer
|
4
4
|
include Mongo::Retryable
|
5
5
|
|
6
|
+
attr_reader :client
|
6
7
|
attr_reader :cluster
|
7
8
|
attr_reader :operation
|
8
9
|
|
9
|
-
def initialize(operation, cluster)
|
10
|
+
def initialize(operation, cluster, client)
|
10
11
|
@operation = operation
|
11
12
|
@cluster = cluster
|
13
|
+
@client = client
|
12
14
|
end
|
13
15
|
|
14
16
|
def max_read_retries
|
15
|
-
|
17
|
+
client.max_read_retries
|
16
18
|
end
|
17
19
|
|
18
20
|
def read_retry_interval
|
19
|
-
|
21
|
+
client.read_retry_interval
|
20
22
|
end
|
21
23
|
|
22
24
|
def read
|
25
|
+
read_with_retry(nil, Mongo::ServerSelector.get(mode: :primary)) do
|
26
|
+
operation.execute
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def read_legacy
|
23
31
|
read_with_retry do
|
24
32
|
operation.execute
|
25
33
|
end
|
@@ -81,8 +89,22 @@ describe Mongo::Retryable do
|
|
81
89
|
|
82
90
|
let(:server) { double('server') }
|
83
91
|
|
92
|
+
let(:max_read_retries) { 1 }
|
93
|
+
let(:max_write_retries) { 1 }
|
94
|
+
|
84
95
|
let(:cluster) do
|
85
|
-
double('cluster', next_primary: server)
|
96
|
+
double('cluster', next_primary: server).tap do |cluster|
|
97
|
+
allow(cluster).to receive(:replica_set?).and_return(true)
|
98
|
+
allow(cluster).to receive(:addresses).and_return(['x'])
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
let(:client) do
|
103
|
+
double('client').tap do |client|
|
104
|
+
allow(client).to receive(:cluster).and_return(cluster)
|
105
|
+
allow(client).to receive(:max_read_retries).and_return(max_read_retries)
|
106
|
+
allow(client).to receive(:max_write_retries).and_return(max_write_retries)
|
107
|
+
end
|
86
108
|
end
|
87
109
|
|
88
110
|
let(:server_selector) do
|
@@ -90,10 +112,15 @@ describe Mongo::Retryable do
|
|
90
112
|
end
|
91
113
|
|
92
114
|
let(:retryable) do
|
93
|
-
LegacyRetryableTestConsumer.new(operation, cluster)
|
115
|
+
LegacyRetryableTestConsumer.new(operation, cluster, client)
|
94
116
|
end
|
95
117
|
|
96
|
-
|
118
|
+
before do
|
119
|
+
# Retryable reads perform server selection
|
120
|
+
allow_any_instance_of(Mongo::ServerSelector::Primary).to receive(:select_server).and_return(server)
|
121
|
+
end
|
122
|
+
|
123
|
+
shared_examples_for 'reads with retries' do
|
97
124
|
|
98
125
|
context 'when no exception occurs' do
|
99
126
|
|
@@ -102,12 +129,12 @@ describe Mongo::Retryable do
|
|
102
129
|
end
|
103
130
|
|
104
131
|
it 'executes the operation once' do
|
105
|
-
expect(
|
132
|
+
expect(read_operation).to be true
|
106
133
|
end
|
107
134
|
end
|
108
135
|
|
109
136
|
context 'when ending_transaction is true' do
|
110
|
-
let(:retryable) { RetryableTestConsumer.new(operation, cluster) }
|
137
|
+
let(:retryable) { RetryableTestConsumer.new(operation, cluster, client) }
|
111
138
|
|
112
139
|
it 'raises ArgumentError' do
|
113
140
|
expect do
|
@@ -119,28 +146,28 @@ describe Mongo::Retryable do
|
|
119
146
|
context 'when a socket error occurs' do
|
120
147
|
|
121
148
|
before do
|
149
|
+
expect(retryable).to receive(:select_server).ordered
|
122
150
|
expect(operation).to receive(:execute).and_raise(Mongo::Error::SocketError).ordered
|
123
|
-
expect(
|
124
|
-
expect(cluster).to receive(:scan!).and_return(true).ordered
|
151
|
+
expect(retryable).to receive(:select_server).ordered
|
125
152
|
expect(operation).to receive(:execute).and_return(true).ordered
|
126
153
|
end
|
127
154
|
|
128
155
|
it 'executes the operation twice' do
|
129
|
-
expect(
|
156
|
+
expect(read_operation).to be true
|
130
157
|
end
|
131
158
|
end
|
132
159
|
|
133
160
|
context 'when a socket timeout error occurs' do
|
134
161
|
|
135
162
|
before do
|
163
|
+
expect(retryable).to receive(:select_server).ordered
|
136
164
|
expect(operation).to receive(:execute).and_raise(Mongo::Error::SocketTimeoutError).ordered
|
137
|
-
expect(
|
138
|
-
expect(cluster).to receive(:scan!).and_return(true).ordered
|
165
|
+
expect(retryable).to receive(:select_server).ordered
|
139
166
|
expect(operation).to receive(:execute).and_return(true).ordered
|
140
167
|
end
|
141
168
|
|
142
169
|
it 'executes the operation twice' do
|
143
|
-
expect(
|
170
|
+
expect(read_operation).to be true
|
144
171
|
end
|
145
172
|
end
|
146
173
|
|
@@ -155,7 +182,7 @@ describe Mongo::Retryable do
|
|
155
182
|
|
156
183
|
it 'raises an exception' do
|
157
184
|
expect {
|
158
|
-
|
185
|
+
read_operation
|
159
186
|
}.to raise_error(Mongo::Error::OperationFailure)
|
160
187
|
end
|
161
188
|
end
|
@@ -175,7 +202,7 @@ describe Mongo::Retryable do
|
|
175
202
|
|
176
203
|
it 'raises the exception' do
|
177
204
|
expect {
|
178
|
-
|
205
|
+
read_operation
|
179
206
|
}.to raise_error(Mongo::Error::OperationFailure)
|
180
207
|
end
|
181
208
|
end
|
@@ -189,34 +216,39 @@ describe Mongo::Retryable do
|
|
189
216
|
context 'when the retry succeeds' do
|
190
217
|
|
191
218
|
before do
|
219
|
+
expect(retryable).to receive(:select_server).ordered
|
192
220
|
expect(operation).to receive(:execute).and_raise(error).ordered
|
193
221
|
expect(cluster).to receive(:sharded?).and_return(true)
|
194
|
-
expect(
|
195
|
-
expect(
|
222
|
+
expect(client).to receive(:read_retry_interval).and_return(0.1).ordered
|
223
|
+
expect(retryable).to receive(:select_server).ordered
|
196
224
|
expect(operation).to receive(:execute).and_return(true).ordered
|
197
225
|
end
|
198
226
|
|
199
227
|
it 'returns the result' do
|
200
|
-
expect(
|
228
|
+
expect(read_operation).to be true
|
201
229
|
end
|
202
230
|
end
|
203
231
|
|
204
232
|
context 'when the retry fails once and then succeeds' do
|
233
|
+
let(:max_read_retries) { 2 }
|
205
234
|
|
206
235
|
before do
|
236
|
+
expect(retryable).to receive(:select_server).ordered
|
207
237
|
expect(operation).to receive(:execute).and_raise(error).ordered
|
238
|
+
|
208
239
|
expect(cluster).to receive(:sharded?).and_return(true)
|
209
|
-
expect(
|
210
|
-
expect(
|
240
|
+
expect(client).to receive(:read_retry_interval).and_return(0.1).ordered
|
241
|
+
expect(retryable).to receive(:select_server).ordered
|
211
242
|
expect(operation).to receive(:execute).and_raise(error).ordered
|
243
|
+
|
212
244
|
expect(cluster).to receive(:sharded?).and_return(true)
|
213
|
-
expect(
|
214
|
-
expect(
|
245
|
+
expect(client).to receive(:read_retry_interval).and_return(0.1).ordered
|
246
|
+
expect(retryable).to receive(:select_server).ordered
|
215
247
|
expect(operation).to receive(:execute).and_return(true).ordered
|
216
248
|
end
|
217
249
|
|
218
250
|
it 'returns the result' do
|
219
|
-
expect(
|
251
|
+
expect(read_operation).to be true
|
220
252
|
end
|
221
253
|
end
|
222
254
|
end
|
@@ -224,6 +256,27 @@ describe Mongo::Retryable do
|
|
224
256
|
end
|
225
257
|
end
|
226
258
|
|
259
|
+
describe '#read_with_retry' do
|
260
|
+
let(:read_operation) do
|
261
|
+
retryable.read
|
262
|
+
end
|
263
|
+
|
264
|
+
it_behaves_like 'reads with retries'
|
265
|
+
|
266
|
+
context 'zero argument legacy invocation' do
|
267
|
+
|
268
|
+
before do
|
269
|
+
allow_any_instance_of(Mongo::ServerSelector::PrimaryPreferred).to receive(:select_server).and_return(server)
|
270
|
+
end
|
271
|
+
|
272
|
+
let(:read_operation) do
|
273
|
+
retryable.read_legacy
|
274
|
+
end
|
275
|
+
|
276
|
+
it_behaves_like 'reads with retries'
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
227
280
|
describe '#retry_write_allowed?' do
|
228
281
|
let(:retryable) { RetryableHost.new }
|
229
282
|
|
@@ -413,7 +466,7 @@ describe Mongo::Retryable do
|
|
413
466
|
describe '#write_with_retry - modern' do
|
414
467
|
|
415
468
|
let(:retryable) do
|
416
|
-
ModernRetryableTestConsumer.new(operation, cluster)
|
469
|
+
ModernRetryableTestConsumer.new(operation, cluster, client)
|
417
470
|
end
|
418
471
|
|
419
472
|
before do
|
@@ -2,8 +2,10 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Mongo::Server::ConnectionPool do
|
4
4
|
|
5
|
-
let(:options)
|
6
|
-
|
5
|
+
let(:options) { {max_pool_size: 2} }
|
6
|
+
|
7
|
+
let(:server_options) do
|
8
|
+
SpecConfig.instance.test_options.merge(options)
|
7
9
|
end
|
8
10
|
|
9
11
|
let(:address) do
|
@@ -25,15 +27,260 @@ describe Mongo::Server::ConnectionPool do
|
|
25
27
|
allow(cl).to receive(:topology).and_return(topology)
|
26
28
|
allow(cl).to receive(:app_metadata).and_return(app_metadata)
|
27
29
|
allow(cl).to receive(:options).and_return({})
|
30
|
+
allow(cl).to receive(:update_cluster_time)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
let(:server) do
|
35
|
+
Mongo::Server.new(address, cluster, monitoring, listeners, server_options)
|
36
|
+
end
|
37
|
+
|
38
|
+
let(:pool) do
|
39
|
+
described_class.new(server)
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#initialize' do
|
43
|
+
|
44
|
+
context 'when a min size is provided' do
|
45
|
+
|
46
|
+
let(:pool) do
|
47
|
+
described_class.new(server, :min_pool_size => 2)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'creates the pool with no connections' do
|
51
|
+
expect(pool.size).to eq(0)
|
52
|
+
expect(pool.available_count).to eq(0)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'does not use the same objects in the pool' do
|
56
|
+
expect(pool.check_out).to_not equal(pool.check_out)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when min size exceeds default max size' do
|
61
|
+
|
62
|
+
let(:pool) do
|
63
|
+
described_class.new(server, :min_pool_size => 10)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'sets max size to equal provided min size' do
|
67
|
+
expect(pool.max_size).to eq(10)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'when no min size is provided' do
|
72
|
+
|
73
|
+
let(:pool) do
|
74
|
+
described_class.new(server)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'creates the pool with no connections' do
|
78
|
+
expect(pool.size).to eq(0)
|
79
|
+
expect(pool.available_count).to eq(0)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'sizes given as min_size and max_size' do
|
84
|
+
|
85
|
+
let(:pool) do
|
86
|
+
described_class.new(server, min_size: 3, max_size: 7)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'sets sizes correctly' do
|
90
|
+
expect(pool.min_size).to eq(3)
|
91
|
+
expect(pool.max_size).to eq(7)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'sizes given as min_pool_size and max_pool_size' do
|
96
|
+
|
97
|
+
let(:pool) do
|
98
|
+
described_class.new(server, min_pool_size: 3, max_pool_size: 7)
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'sets sizes correctly' do
|
102
|
+
expect(pool.min_size).to eq(3)
|
103
|
+
expect(pool.max_size).to eq(7)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'timeout given as wait_timeout' do
|
108
|
+
|
109
|
+
let(:pool) do
|
110
|
+
described_class.new(server, wait_timeout: 4)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'sets wait timeout correctly' do
|
114
|
+
expect(pool.wait_timeout).to eq(4)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'timeout given as wait_queue_timeout' do
|
119
|
+
|
120
|
+
let(:pool) do
|
121
|
+
described_class.new(server, wait_queue_timeout: 4)
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'sets wait timeout correctly' do
|
125
|
+
expect(pool.wait_timeout).to eq(4)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe '#max_size' do
|
131
|
+
|
132
|
+
context 'when a max pool size option is provided' do
|
133
|
+
|
134
|
+
let(:pool) do
|
135
|
+
described_class.new(server, :max_pool_size => 3)
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'returns the max size' do
|
139
|
+
expect(pool.max_size).to eq(3)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'when no pool size option is provided' do
|
144
|
+
|
145
|
+
it 'returns the default size' do
|
146
|
+
expect(pool.max_size).to eq(5)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context 'when pool is closed' do
|
151
|
+
before do
|
152
|
+
pool.close
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'returns max size' do
|
156
|
+
expect(pool.max_size).to eq(5)
|
157
|
+
end
|
28
158
|
end
|
29
159
|
end
|
30
160
|
|
31
|
-
describe '#
|
161
|
+
describe '#wait_timeout' do
|
162
|
+
|
163
|
+
context 'when the wait timeout option is provided' do
|
164
|
+
|
165
|
+
let(:pool) do
|
166
|
+
described_class.new(server, :wait_queue_timeout => 3)
|
167
|
+
end
|
32
168
|
|
33
|
-
|
34
|
-
|
169
|
+
it 'returns the wait timeout' do
|
170
|
+
expect(pool.wait_timeout).to eq(3)
|
171
|
+
end
|
35
172
|
end
|
36
173
|
|
174
|
+
context 'when the wait timeout option is not provided' do
|
175
|
+
|
176
|
+
it 'returns the default wait timeout' do
|
177
|
+
expect(pool.wait_timeout).to eq(1)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
describe '#size' do
|
183
|
+
context 'pool without connections' do
|
184
|
+
it 'is 0' do
|
185
|
+
expect(pool.size).to eq(0)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
context 'pool with a checked out connection' do
|
190
|
+
before do
|
191
|
+
pool.check_out
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'is 1' do
|
195
|
+
expect(pool.size).to eq(1)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
context 'pool with an available connection' do
|
200
|
+
before do
|
201
|
+
connection = pool.check_out
|
202
|
+
pool.check_in(connection)
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'is 1' do
|
206
|
+
expect(pool.size).to eq(1)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
context 'when pool is closed' do
|
211
|
+
before do
|
212
|
+
pool.close
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'raises PoolClosedError' do
|
216
|
+
expect do
|
217
|
+
pool.size
|
218
|
+
end.to raise_error(Mongo::Error::PoolClosedError)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
describe '#available_count' do
|
224
|
+
context 'pool without connections' do
|
225
|
+
it 'is 0' do
|
226
|
+
expect(pool.available_count).to eq(0)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
context 'pool with a checked out connection' do
|
231
|
+
before do
|
232
|
+
pool.check_out
|
233
|
+
end
|
234
|
+
|
235
|
+
it 'is 0' do
|
236
|
+
expect(pool.available_count).to eq(0)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
context 'pool with an available connection' do
|
241
|
+
before do
|
242
|
+
connection = pool.check_out
|
243
|
+
pool.check_in(connection)
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'is 1' do
|
247
|
+
expect(pool.available_count).to eq(1)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
context 'when pool is closed' do
|
252
|
+
before do
|
253
|
+
pool.close
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'raises PoolClosedError' do
|
257
|
+
expect do
|
258
|
+
pool.available_count
|
259
|
+
end.to raise_error(Mongo::Error::PoolClosedError)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe '#closed?' do
|
265
|
+
context 'pool is not closed' do
|
266
|
+
it 'is false' do
|
267
|
+
expect(pool.closed?).to be false
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
context 'pool is closed' do
|
272
|
+
before do
|
273
|
+
pool.close
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'is true' do
|
277
|
+
expect(pool.closed?).to be true
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
describe '#check_in' do
|
283
|
+
|
37
284
|
let!(:pool) do
|
38
285
|
server.pool
|
39
286
|
end
|
@@ -43,108 +290,315 @@ describe Mongo::Server::ConnectionPool do
|
|
43
290
|
server.disconnect!
|
44
291
|
end
|
45
292
|
|
293
|
+
let(:options) { {max_pool_size: 2} }
|
294
|
+
|
295
|
+
let(:connection) do
|
296
|
+
pool.check_out
|
297
|
+
end
|
298
|
+
|
46
299
|
context 'when a connection is checked out on the thread' do
|
47
300
|
|
48
|
-
|
49
|
-
pool.
|
301
|
+
before do
|
302
|
+
pool.check_in(connection)
|
50
303
|
end
|
51
304
|
|
305
|
+
it 'returns the connection to the pool' do
|
306
|
+
expect(pool.size).to eq(1)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
context 'connection of the same generation as pool' do
|
52
311
|
before do
|
53
|
-
pool.
|
312
|
+
expect(pool.generation).to eq(connection.generation)
|
54
313
|
end
|
55
314
|
|
56
|
-
|
57
|
-
|
315
|
+
it 'adds the connection to the pool' do
|
316
|
+
# connection is checked out
|
317
|
+
expect(pool.available_count).to eq(0)
|
318
|
+
expect(pool.size).to eq(1)
|
319
|
+
pool.check_in(connection)
|
320
|
+
# now connection is in the queue
|
321
|
+
expect(pool.available_count).to eq(1)
|
322
|
+
expect(pool.size).to eq(1)
|
323
|
+
expect(pool.check_out).to eq(connection)
|
58
324
|
end
|
325
|
+
end
|
59
326
|
|
60
|
-
|
61
|
-
|
327
|
+
shared_examples 'does not add connection to pool' do
|
328
|
+
it 'disconnects connection and does not add connection to pool' do
|
329
|
+
# connection was checked out
|
330
|
+
expect(pool.available_count).to eq(0)
|
331
|
+
expect(pool.size).to eq(1)
|
332
|
+
expect(connection).to receive(:disconnect!)
|
333
|
+
pool.check_in(connection)
|
334
|
+
# connection is not added to the pool, and no replacement
|
335
|
+
# connection has been created at this point
|
336
|
+
expect(pool.available_count).to eq(0)
|
337
|
+
expect(pool.size).to eq(0)
|
338
|
+
expect(pool.check_out).not_to eq(connection)
|
62
339
|
end
|
63
340
|
end
|
64
|
-
end
|
65
341
|
|
66
|
-
|
342
|
+
context 'connection of earlier generation than pool' do
|
343
|
+
let(:connection) do
|
344
|
+
pool.check_out.tap do |connection|
|
345
|
+
expect(connection).to receive(:generation).at_least(:once).and_return(0)
|
346
|
+
expect(connection).not_to receive(:record_checkin!)
|
347
|
+
end
|
348
|
+
end
|
67
349
|
|
68
|
-
|
69
|
-
|
70
|
-
|
350
|
+
before do
|
351
|
+
expect(connection.generation < pool.generation).to be true
|
352
|
+
end
|
71
353
|
|
72
|
-
|
73
|
-
server.pool
|
354
|
+
it_behaves_like 'does not add connection to pool'
|
74
355
|
end
|
75
356
|
|
76
|
-
context '
|
77
|
-
|
78
|
-
|
79
|
-
|
357
|
+
context 'connection of later generation than pool' do
|
358
|
+
let(:connection) do
|
359
|
+
pool.check_out.tap do |connection|
|
360
|
+
expect(connection).to receive(:generation).at_least(:once).and_return(7)
|
361
|
+
expect(connection).not_to receive(:record_checkin!)
|
362
|
+
end
|
80
363
|
end
|
81
364
|
|
82
|
-
|
83
|
-
expect(connection.
|
365
|
+
before do
|
366
|
+
expect(connection.generation > pool.generation).to be true
|
84
367
|
end
|
368
|
+
|
369
|
+
it_behaves_like 'does not add connection to pool'
|
85
370
|
end
|
86
371
|
|
87
|
-
context 'when
|
372
|
+
context 'when pool is closed' do
|
373
|
+
let(:connection) { pool.check_out }
|
88
374
|
|
89
375
|
before do
|
90
|
-
|
376
|
+
connection
|
377
|
+
pool.close
|
91
378
|
end
|
92
379
|
|
93
|
-
it '
|
94
|
-
expect(
|
380
|
+
it 'closes connection' do
|
381
|
+
expect(connection.closed?).to be false
|
382
|
+
expect(pool.instance_variable_get('@available_connections').length).to eq(0)
|
383
|
+
pool.check_in(connection)
|
384
|
+
expect(connection.closed?).to be true
|
385
|
+
expect(pool.instance_variable_get('@available_connections').length).to eq(0)
|
95
386
|
end
|
96
387
|
end
|
388
|
+
end
|
389
|
+
|
390
|
+
describe '#check_out' do
|
391
|
+
|
392
|
+
let!(:pool) do
|
393
|
+
server.pool
|
394
|
+
end
|
97
395
|
|
98
396
|
context 'when a connection is checked out on a different thread' do
|
99
397
|
|
100
398
|
let!(:connection) do
|
101
|
-
Thread.new { pool.
|
399
|
+
Thread.new { pool.check_out }.join
|
102
400
|
end
|
103
401
|
|
104
402
|
it 'returns a new connection' do
|
105
|
-
expect(pool.
|
403
|
+
expect(pool.check_out.address).to eq(server.address)
|
106
404
|
end
|
107
405
|
|
108
406
|
it 'does not return the same connection instance' do
|
109
|
-
expect(pool.
|
407
|
+
expect(pool.check_out).to_not eql(connection)
|
110
408
|
end
|
111
409
|
end
|
112
410
|
|
113
411
|
context 'when connections are checked out and checked back in' do
|
114
412
|
|
115
413
|
it 'pulls the connection from the front of the queue' do
|
116
|
-
first = pool.
|
117
|
-
second = pool.
|
118
|
-
pool.
|
119
|
-
pool.
|
120
|
-
expect(pool.
|
414
|
+
first = pool.check_out
|
415
|
+
second = pool.check_out
|
416
|
+
pool.check_in(second)
|
417
|
+
pool.check_in(first)
|
418
|
+
expect(pool.check_out).to be(first)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
context 'when there is an available connection which is stale' do
|
423
|
+
let(:options) do
|
424
|
+
{max_pool_size: 2, max_idle_time: 0.1}
|
425
|
+
end
|
426
|
+
|
427
|
+
let(:connection) do
|
428
|
+
pool.check_out.tap do |connection|
|
429
|
+
allow(connection).to receive(:generation).and_return(pool.generation)
|
430
|
+
allow(connection).to receive(:record_checkin!).and_return(connection)
|
431
|
+
expect(connection).to receive(:last_checkin).at_least(:once).and_return(Time.now - 10)
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
before do
|
436
|
+
pool.check_in(connection)
|
437
|
+
end
|
438
|
+
|
439
|
+
after do
|
440
|
+
pool.close(force: true)
|
441
|
+
end
|
442
|
+
|
443
|
+
it 'closes stale connection and creates a new one' do
|
444
|
+
expect(connection).to receive(:disconnect!)
|
445
|
+
expect(Mongo::Server::Connection).to receive(:new).and_call_original
|
446
|
+
pool.check_out
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
context 'when there are no available connections' do
|
451
|
+
|
452
|
+
let(:options) do
|
453
|
+
{max_pool_size: 1}
|
454
|
+
end
|
455
|
+
|
456
|
+
context 'when the max size is not reached' do
|
457
|
+
|
458
|
+
it 'creates a new connection' do
|
459
|
+
expect(Mongo::Server::Connection).to receive(:new).once.and_call_original
|
460
|
+
expect(pool.check_out).to be_a(Mongo::Server::Connection)
|
461
|
+
expect(pool.size).to eq(1)
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
context 'when the max size is reached' do
|
466
|
+
|
467
|
+
it 'raises a timeout error' do
|
468
|
+
expect(Mongo::Server::Connection).to receive(:new).once.and_call_original
|
469
|
+
expect {
|
470
|
+
pool.check_out
|
471
|
+
pool.check_out
|
472
|
+
}.to raise_error(Timeout::Error)
|
473
|
+
expect(pool.size).to eq(1)
|
474
|
+
end
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
context 'when waiting for a connection to be checked in' do
|
479
|
+
|
480
|
+
let!(:connection) { pool.check_out }
|
481
|
+
|
482
|
+
before do
|
483
|
+
allow(connection).to receive(:record_checkin!).and_return(connection)
|
484
|
+
Thread.new do
|
485
|
+
sleep(0.5)
|
486
|
+
pool.check_in(connection)
|
487
|
+
end.join
|
488
|
+
end
|
489
|
+
|
490
|
+
it 'returns the checked in connection' do
|
491
|
+
expect(pool.check_out).to eq(connection)
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
context 'when pool is closed' do
|
496
|
+
before do
|
497
|
+
pool.close
|
498
|
+
end
|
499
|
+
|
500
|
+
it 'raises PoolClosedError' do
|
501
|
+
expect do
|
502
|
+
pool.check_out
|
503
|
+
end.to raise_error(Mongo::Error::PoolClosedError)
|
121
504
|
end
|
122
505
|
end
|
123
506
|
end
|
124
507
|
|
125
508
|
describe '#disconnect!' do
|
126
509
|
|
127
|
-
|
128
|
-
|
510
|
+
def create_pool(min_pool_size)
|
511
|
+
described_class.new(server, max_pool_size: 3, min_pool_size: min_pool_size).tap do |pool|
|
512
|
+
# make pool be of size 2 so that it has enqueued connections
|
513
|
+
# when told to disconnect
|
514
|
+
c1 = pool.check_out
|
515
|
+
c2 = pool.check_out
|
516
|
+
allow(c1).to receive(:record_checkin!).and_return(c1)
|
517
|
+
allow(c2).to receive(:record_checkin!).and_return(c2)
|
518
|
+
pool.check_in(c1)
|
519
|
+
pool.check_in(c2)
|
520
|
+
expect(pool.size).to eq(2)
|
521
|
+
expect(pool.available_count).to eq(2)
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
shared_examples_for 'disconnects and removes all connections in the pool and bumps generation' do
|
526
|
+
it 'disconnects and removes and bumps' do
|
527
|
+
old_connections = []
|
528
|
+
pool.instance_variable_get('@available_connections').each do |connection|
|
529
|
+
expect(connection).to receive(:disconnect!)
|
530
|
+
old_connections << connection
|
531
|
+
end
|
532
|
+
|
533
|
+
expect(pool.size).to eq(2)
|
534
|
+
expect(pool.available_count).to eq(2)
|
535
|
+
|
536
|
+
pool.disconnect!
|
537
|
+
|
538
|
+
expect(pool.size).to eq(0)
|
539
|
+
expect(pool.available_count).to eq(0)
|
540
|
+
|
541
|
+
new_connection = pool.check_out
|
542
|
+
expect(old_connections).not_to include(new_connection)
|
543
|
+
expect(new_connection.generation).to eq(2)
|
544
|
+
end
|
129
545
|
end
|
130
546
|
|
131
|
-
|
132
|
-
|
547
|
+
context 'min size is 0' do
|
548
|
+
let(:pool) do
|
549
|
+
create_pool(0)
|
550
|
+
end
|
551
|
+
|
552
|
+
it_behaves_like 'disconnects and removes all connections in the pool and bumps generation'
|
133
553
|
end
|
134
554
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
555
|
+
context 'min size is not 0' do
|
556
|
+
let(:pool) do
|
557
|
+
create_pool(1)
|
558
|
+
end
|
559
|
+
|
560
|
+
it_behaves_like 'disconnects and removes all connections in the pool and bumps generation'
|
561
|
+
end
|
562
|
+
|
563
|
+
context 'when pool is closed' do
|
564
|
+
before do
|
565
|
+
pool.close
|
566
|
+
end
|
567
|
+
|
568
|
+
it 'raises PoolClosedError' do
|
569
|
+
expect do
|
570
|
+
pool.disconnect!
|
571
|
+
end.to raise_error(Mongo::Error::PoolClosedError)
|
572
|
+
end
|
139
573
|
end
|
140
574
|
end
|
141
575
|
|
142
|
-
describe '#
|
576
|
+
describe '#close' do
|
577
|
+
context 'when pool is not closed' do
|
578
|
+
it 'closes the pool' do
|
579
|
+
expect(pool).not_to be_closed
|
580
|
+
|
581
|
+
pool.close
|
143
582
|
|
144
|
-
|
145
|
-
|
583
|
+
expect(pool).to be_closed
|
584
|
+
end
|
146
585
|
end
|
147
586
|
|
587
|
+
context 'when pool is closed' do
|
588
|
+
before do
|
589
|
+
pool.close
|
590
|
+
end
|
591
|
+
|
592
|
+
it 'is a no-op' do
|
593
|
+
pool.close
|
594
|
+
expect(pool).to be_closed
|
595
|
+
end
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
describe '#inspect' do
|
600
|
+
let(:options) { {min_pool_size: 3, max_pool_size: 7, wait_timeout: 9, wait_queue_timeout: 9} }
|
601
|
+
|
148
602
|
let!(:pool) do
|
149
603
|
server.pool
|
150
604
|
end
|
@@ -158,16 +612,48 @@ describe Mongo::Server::ConnectionPool do
|
|
158
612
|
expect(pool.inspect).to include(pool.object_id.to_s)
|
159
613
|
end
|
160
614
|
|
615
|
+
it 'includes the min size' do
|
616
|
+
expect(pool.inspect).to include('min_size=3')
|
617
|
+
end
|
618
|
+
|
619
|
+
it 'includes the max size' do
|
620
|
+
expect(pool.inspect).to include('max_size=7')
|
621
|
+
end
|
622
|
+
|
623
|
+
it 'includes the wait timeout' do
|
624
|
+
expect(pool.inspect).to include('wait_timeout=9')
|
625
|
+
end
|
626
|
+
|
627
|
+
it 'includes the current size' do
|
628
|
+
expect(pool.inspect).to include('current_size=0')
|
629
|
+
end
|
630
|
+
|
631
|
+
=begin obsolete
|
161
632
|
it 'includes the queue inspection' do
|
162
633
|
expect(pool.inspect).to include(pool.__send__(:queue).inspect)
|
163
634
|
end
|
164
|
-
|
635
|
+
=end
|
165
636
|
|
166
|
-
|
637
|
+
it 'indicates the pool is not closed' do
|
638
|
+
expect(pool.inspect).not_to include('closed')
|
639
|
+
end
|
640
|
+
|
641
|
+
context 'when pool is closed' do
|
642
|
+
before do
|
643
|
+
pool.close
|
644
|
+
end
|
167
645
|
|
168
|
-
|
169
|
-
|
646
|
+
it 'returns inspection string' do
|
647
|
+
expect(pool.inspect).to include('min_size=')
|
648
|
+
end
|
649
|
+
|
650
|
+
it 'indicates the pool is closed' do
|
651
|
+
expect(pool.inspect).to include('closed')
|
652
|
+
end
|
170
653
|
end
|
654
|
+
end
|
655
|
+
|
656
|
+
describe '#with_connection' do
|
171
657
|
|
172
658
|
let!(:pool) do
|
173
659
|
server.pool
|
@@ -175,69 +661,86 @@ describe Mongo::Server::ConnectionPool do
|
|
175
661
|
|
176
662
|
context 'when a connection cannot be checked out' do
|
177
663
|
|
178
|
-
|
179
|
-
|
664
|
+
it 'does not add the connection to the pool' do
|
665
|
+
pending 'Re-enable when connections are connected prior to being returned from check_out method'
|
666
|
+
|
667
|
+
allow(pool).to receive(:check_out).and_raise(Mongo::Error::SocketError)
|
180
668
|
pool.with_connection { |c| c }
|
669
|
+
|
670
|
+
expect(pool.size).to eq(0)
|
181
671
|
end
|
672
|
+
end
|
182
673
|
|
183
|
-
|
184
|
-
|
674
|
+
context 'when pool is closed' do
|
675
|
+
before do
|
676
|
+
pool.close
|
185
677
|
end
|
186
678
|
|
187
|
-
it '
|
188
|
-
expect
|
679
|
+
it 'raises PoolClosedError' do
|
680
|
+
expect do
|
681
|
+
pool.with_connection { |c| c }
|
682
|
+
end.to raise_error(Mongo::Error::PoolClosedError)
|
189
683
|
end
|
190
684
|
end
|
191
685
|
end
|
192
686
|
|
193
687
|
context 'when the connection does not finish authenticating before the thread is killed' do
|
194
688
|
|
195
|
-
let(:server) do
|
196
|
-
Mongo::Server.new(address, cluster, monitoring, listeners, options)
|
197
|
-
end
|
198
|
-
|
199
689
|
let!(:pool) do
|
200
690
|
server.pool
|
201
691
|
end
|
202
692
|
|
203
|
-
let(:
|
693
|
+
let(:server_options) do
|
204
694
|
{ user: SpecConfig.instance.root_user.name, password: SpecConfig.instance.root_user.password }.merge(SpecConfig.instance.test_options).merge(max_pool_size: 1)
|
205
695
|
end
|
206
696
|
|
207
697
|
before do
|
208
|
-
|
698
|
+
pool
|
699
|
+
ClientRegistry.instance.close_all_clients
|
700
|
+
end
|
701
|
+
|
702
|
+
it 'creates a new connection' do
|
703
|
+
invoked = nil
|
704
|
+
|
705
|
+
t = Thread.new do
|
209
706
|
# Kill the thread when it's authenticating
|
210
|
-
|
211
|
-
|
212
|
-
|
707
|
+
expect(Mongo::Auth).to receive(:get) do
|
708
|
+
if Thread.current != t
|
709
|
+
raise 'Auth invoked on unexpected thread'
|
710
|
+
end
|
711
|
+
invoked = true
|
712
|
+
t.kill
|
713
|
+
raise 'Should not get here'
|
714
|
+
end
|
715
|
+
pool.with_connection do |c|
|
716
|
+
c.send(:ensure_connected) { |socket| socket }
|
717
|
+
end
|
718
|
+
end
|
213
719
|
t.join
|
214
|
-
end
|
215
720
|
|
216
|
-
|
217
|
-
expect(pool.
|
721
|
+
#expect(Mongo::Auth).to receive(:get).and_call_original
|
722
|
+
expect(pool.check_out).to be_a(Mongo::Server::Connection)
|
723
|
+
expect(invoked).to be true
|
218
724
|
end
|
219
725
|
end
|
220
726
|
|
221
|
-
describe '#
|
222
|
-
|
223
|
-
let(:server) do
|
224
|
-
Mongo::Server.new(address, authorized_client.cluster, monitoring, listeners, options)
|
225
|
-
end
|
727
|
+
describe '#close_idle_sockets' do
|
226
728
|
|
227
729
|
let!(:pool) do
|
228
730
|
server.pool
|
229
731
|
end
|
230
732
|
|
231
|
-
let(:queue) do
|
232
|
-
pool.instance_variable_get(:@queue).queue
|
233
|
-
end
|
234
|
-
|
235
733
|
context 'when there is a max_idle_time specified' do
|
236
734
|
|
237
735
|
let(:options) do
|
238
|
-
|
736
|
+
{max_pool_size: 2, max_idle_time: 0.5}
|
239
737
|
end
|
240
738
|
|
739
|
+
after do
|
740
|
+
Timecop.return
|
741
|
+
end
|
742
|
+
|
743
|
+
=begin obsolete
|
241
744
|
context 'when the connections have not been checked out' do
|
242
745
|
|
243
746
|
before do
|
@@ -245,66 +748,64 @@ describe Mongo::Server::ConnectionPool do
|
|
245
748
|
expect(conn).not_to receive(:disconnect!)
|
246
749
|
end
|
247
750
|
sleep(0.5)
|
248
|
-
pool.
|
751
|
+
pool.close_idle_sockets
|
249
752
|
end
|
250
753
|
|
251
754
|
it 'does not close any sockets' do
|
252
755
|
expect(queue.none? { |c| c.connected? }).to be(true)
|
253
756
|
end
|
254
757
|
end
|
758
|
+
=end
|
255
759
|
|
256
|
-
context 'when
|
760
|
+
context 'when connections have been checked out and returned to the pool' do
|
257
761
|
|
258
762
|
context 'when min size is 0' do
|
259
763
|
|
260
764
|
let(:options) do
|
261
|
-
|
765
|
+
{max_pool_size: 2, min_pool_size: 0, max_idle_time: 0.5}
|
262
766
|
end
|
263
767
|
|
264
768
|
before do
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
pool.
|
269
|
-
pool.checkin(pool.checkout)
|
769
|
+
c1 = pool.check_out
|
770
|
+
c2 = pool.check_out
|
771
|
+
pool.check_in(c1)
|
772
|
+
pool.check_in(c2)
|
270
773
|
sleep(0.5)
|
271
|
-
|
774
|
+
expect(c1).to receive(:disconnect!).and_call_original
|
775
|
+
expect(c2).to receive(:disconnect!).and_call_original
|
776
|
+
pool.close_idle_sockets
|
272
777
|
end
|
273
778
|
|
274
|
-
it 'closes all
|
275
|
-
expect(
|
779
|
+
it 'closes all idle sockets' do
|
780
|
+
expect(pool.size).to be(0)
|
276
781
|
end
|
277
782
|
end
|
278
783
|
|
279
784
|
context 'when min size is > 0' do
|
280
|
-
after do
|
281
|
-
Timecop.return
|
282
|
-
end
|
283
|
-
|
284
785
|
context 'when more than the number of min_size are checked out' do
|
285
786
|
let(:options) do
|
286
|
-
|
787
|
+
{max_pool_size: 5, min_pool_size: 3, max_idle_time: 0.5}
|
287
788
|
end
|
288
789
|
|
289
|
-
it 'closes and removes connections with
|
290
|
-
first = pool.
|
291
|
-
second = pool.
|
292
|
-
third = pool.
|
293
|
-
fourth = pool.
|
294
|
-
fifth = pool.
|
790
|
+
it 'closes and removes connections with idle sockets and does not connect new ones' do
|
791
|
+
first = pool.check_out
|
792
|
+
second = pool.check_out
|
793
|
+
third = pool.check_out
|
794
|
+
fourth = pool.check_out
|
795
|
+
fifth = pool.check_out
|
295
796
|
|
296
|
-
pool.
|
797
|
+
pool.check_in(fifth)
|
297
798
|
|
298
799
|
expect(fifth).to receive(:disconnect!).and_call_original
|
299
800
|
expect(fifth).not_to receive(:connect!)
|
300
801
|
|
301
802
|
Timecop.travel(Time.now + 1)
|
302
|
-
expect(
|
303
|
-
pool.
|
803
|
+
expect(pool.size).to be(5)
|
804
|
+
expect(pool.available_count).to be(1)
|
805
|
+
pool.close_idle_sockets
|
304
806
|
|
305
|
-
expect(pool.
|
306
|
-
expect(pool.
|
307
|
-
expect(queue.length).to be(0)
|
807
|
+
expect(pool.size).to be(4)
|
808
|
+
expect(pool.available_count).to be(0)
|
308
809
|
expect(fifth.connected?).to be(false)
|
309
810
|
end
|
310
811
|
end
|
@@ -312,19 +813,19 @@ describe Mongo::Server::ConnectionPool do
|
|
312
813
|
context 'when between 0 and min_size number of connections are checked out' do
|
313
814
|
|
314
815
|
let(:options) do
|
315
|
-
|
816
|
+
{max_pool_size: 5, min_pool_size: 3, max_idle_time: 0.5}
|
316
817
|
end
|
317
818
|
|
318
|
-
it 'closes and removes connections with
|
319
|
-
first = pool.
|
320
|
-
second = pool.
|
321
|
-
third = pool.
|
322
|
-
fourth = pool.
|
323
|
-
fifth = pool.
|
819
|
+
it 'closes and removes connections with idle sockets and does not connect new ones' do
|
820
|
+
first = pool.check_out
|
821
|
+
second = pool.check_out
|
822
|
+
third = pool.check_out
|
823
|
+
fourth = pool.check_out
|
824
|
+
fifth = pool.check_out
|
324
825
|
|
325
|
-
pool.
|
326
|
-
pool.
|
327
|
-
pool.
|
826
|
+
pool.check_in(third)
|
827
|
+
pool.check_in(fourth)
|
828
|
+
pool.check_in(fifth)
|
328
829
|
|
329
830
|
|
330
831
|
expect(third).to receive(:disconnect!).and_call_original
|
@@ -337,12 +838,12 @@ describe Mongo::Server::ConnectionPool do
|
|
337
838
|
expect(fifth).not_to receive(:connect!).and_call_original
|
338
839
|
|
339
840
|
Timecop.travel(Time.now + 1)
|
340
|
-
expect(
|
341
|
-
pool.
|
841
|
+
expect(pool.size).to be(5)
|
842
|
+
expect(pool.available_count).to be(3)
|
843
|
+
pool.close_idle_sockets
|
342
844
|
|
343
|
-
expect(pool.
|
344
|
-
expect(pool.
|
345
|
-
expect(queue.length).to be(0)
|
845
|
+
expect(pool.size).to be(2)
|
846
|
+
expect(pool.available_count).to be(0)
|
346
847
|
|
347
848
|
expect(third.connected?).to be(false)
|
348
849
|
expect(fourth.connected?).to be(false)
|
@@ -353,18 +854,52 @@ describe Mongo::Server::ConnectionPool do
|
|
353
854
|
end
|
354
855
|
end
|
355
856
|
|
857
|
+
context 'when available connections include idle and non-idle ones' do
|
858
|
+
let(:pool) do
|
859
|
+
described_class.new(server, max_pool_size: 2, max_idle_time: 0.5)
|
860
|
+
end
|
861
|
+
|
862
|
+
let(:connection) do
|
863
|
+
pool.check_out.tap do |con|
|
864
|
+
allow(con).to receive(:disconnect!)
|
865
|
+
end
|
866
|
+
end
|
867
|
+
|
868
|
+
it 'disconnects all expired and only expired connections' do
|
869
|
+
c1 = pool.check_out
|
870
|
+
expect(c1).to receive(:disconnect!)
|
871
|
+
c2 = pool.check_out
|
872
|
+
expect(c2).not_to receive(:disconnect!)
|
873
|
+
|
874
|
+
pool.check_in(c1)
|
875
|
+
Timecop.travel(Time.now + 1)
|
876
|
+
pool.check_in(c2)
|
877
|
+
|
878
|
+
expect(pool.size).to eq(2)
|
879
|
+
expect(pool.available_count).to eq(2)
|
880
|
+
|
881
|
+
expect(c1).not_to receive(:connect!)
|
882
|
+
expect(c2).not_to receive(:connect!)
|
883
|
+
|
884
|
+
pool.close_idle_sockets
|
885
|
+
|
886
|
+
expect(pool.size).to eq(1)
|
887
|
+
expect(pool.available_count).to eq(1)
|
888
|
+
end
|
889
|
+
end
|
890
|
+
|
356
891
|
context 'when there is no max_idle_time specified' do
|
357
892
|
|
358
893
|
let(:connection) do
|
359
|
-
conn = pool.
|
894
|
+
conn = pool.check_out
|
360
895
|
conn.connect!
|
361
|
-
pool.
|
896
|
+
pool.check_in(conn)
|
362
897
|
conn
|
363
898
|
end
|
364
899
|
|
365
900
|
before do
|
366
901
|
expect(connection).not_to receive(:disconnect!)
|
367
|
-
pool.
|
902
|
+
pool.close_idle_sockets
|
368
903
|
end
|
369
904
|
|
370
905
|
it 'does not close any sockets' do
|