mongo 1.3.0 → 1.12.5

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.
Files changed (185) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/{LICENSE.txt → LICENSE} +1 -1
  4. data/README.md +122 -271
  5. data/Rakefile +25 -209
  6. data/VERSION +1 -0
  7. data/bin/mongo_console +31 -9
  8. data/lib/mongo/bulk_write_collection_view.rb +387 -0
  9. data/lib/mongo/collection.rb +576 -269
  10. data/lib/mongo/collection_writer.rb +364 -0
  11. data/lib/mongo/connection/node.rb +249 -0
  12. data/lib/mongo/connection/pool.rb +340 -0
  13. data/lib/mongo/connection/pool_manager.rb +320 -0
  14. data/lib/mongo/connection/sharding_pool_manager.rb +67 -0
  15. data/lib/mongo/connection/socket/socket_util.rb +37 -0
  16. data/lib/mongo/connection/socket/ssl_socket.rb +95 -0
  17. data/lib/mongo/connection/socket/tcp_socket.rb +87 -0
  18. data/lib/mongo/connection/socket/unix_socket.rb +39 -0
  19. data/lib/mongo/connection/socket.rb +18 -0
  20. data/lib/mongo/connection.rb +7 -875
  21. data/lib/mongo/cursor.rb +403 -117
  22. data/lib/mongo/db.rb +444 -243
  23. data/lib/mongo/exception.rb +145 -0
  24. data/lib/mongo/functional/authentication.rb +455 -0
  25. data/lib/mongo/functional/logging.rb +85 -0
  26. data/lib/mongo/functional/read_preference.rb +183 -0
  27. data/lib/mongo/functional/scram.rb +556 -0
  28. data/lib/mongo/functional/uri_parser.rb +409 -0
  29. data/lib/mongo/functional/write_concern.rb +66 -0
  30. data/lib/mongo/functional.rb +20 -0
  31. data/lib/mongo/gridfs/grid.rb +30 -24
  32. data/lib/mongo/gridfs/grid_ext.rb +6 -10
  33. data/lib/mongo/gridfs/grid_file_system.rb +38 -20
  34. data/lib/mongo/gridfs/grid_io.rb +84 -75
  35. data/lib/mongo/gridfs.rb +18 -0
  36. data/lib/mongo/legacy.rb +140 -0
  37. data/lib/mongo/mongo_client.rb +697 -0
  38. data/lib/mongo/mongo_replica_set_client.rb +535 -0
  39. data/lib/mongo/mongo_sharded_client.rb +159 -0
  40. data/lib/mongo/networking.rb +372 -0
  41. data/lib/mongo/{util → utils}/conversions.rb +29 -8
  42. data/lib/mongo/{util → utils}/core_ext.rb +28 -18
  43. data/lib/mongo/{util → utils}/server_version.rb +4 -6
  44. data/lib/mongo/{util → utils}/support.rb +29 -31
  45. data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
  46. data/lib/mongo/utils.rb +19 -0
  47. data/lib/mongo.rb +51 -50
  48. data/mongo.gemspec +29 -32
  49. data/test/functional/authentication_test.rb +39 -0
  50. data/test/functional/bulk_api_stress_test.rb +133 -0
  51. data/test/functional/bulk_write_collection_view_test.rb +1198 -0
  52. data/test/functional/client_test.rb +627 -0
  53. data/test/functional/collection_test.rb +2175 -0
  54. data/test/functional/collection_writer_test.rb +83 -0
  55. data/test/{conversions_test.rb → functional/conversions_test.rb} +47 -3
  56. data/test/functional/cursor_fail_test.rb +57 -0
  57. data/test/functional/cursor_message_test.rb +56 -0
  58. data/test/functional/cursor_test.rb +683 -0
  59. data/test/functional/db_api_test.rb +835 -0
  60. data/test/functional/db_connection_test.rb +25 -0
  61. data/test/functional/db_test.rb +348 -0
  62. data/test/functional/grid_file_system_test.rb +285 -0
  63. data/test/{grid_io_test.rb → functional/grid_io_test.rb} +72 -11
  64. data/test/{grid_test.rb → functional/grid_test.rb} +88 -15
  65. data/test/functional/pool_test.rb +136 -0
  66. data/test/functional/safe_test.rb +98 -0
  67. data/test/functional/ssl_test.rb +29 -0
  68. data/test/functional/support_test.rb +62 -0
  69. data/test/functional/timeout_test.rb +60 -0
  70. data/test/functional/uri_test.rb +446 -0
  71. data/test/functional/write_concern_test.rb +118 -0
  72. data/test/helpers/general.rb +50 -0
  73. data/test/helpers/test_unit.rb +476 -0
  74. data/test/replica_set/authentication_test.rb +37 -0
  75. data/test/replica_set/basic_test.rb +189 -0
  76. data/test/replica_set/client_test.rb +393 -0
  77. data/test/replica_set/connection_test.rb +138 -0
  78. data/test/replica_set/count_test.rb +66 -0
  79. data/test/replica_set/cursor_test.rb +220 -0
  80. data/test/replica_set/insert_test.rb +157 -0
  81. data/test/replica_set/max_values_test.rb +151 -0
  82. data/test/replica_set/pinning_test.rb +105 -0
  83. data/test/replica_set/query_test.rb +73 -0
  84. data/test/replica_set/read_preference_test.rb +219 -0
  85. data/test/replica_set/refresh_test.rb +211 -0
  86. data/test/replica_set/replication_ack_test.rb +95 -0
  87. data/test/replica_set/ssl_test.rb +32 -0
  88. data/test/sharded_cluster/basic_test.rb +203 -0
  89. data/test/shared/authentication/basic_auth_shared.rb +260 -0
  90. data/test/shared/authentication/bulk_api_auth_shared.rb +249 -0
  91. data/test/shared/authentication/gssapi_shared.rb +176 -0
  92. data/test/shared/authentication/sasl_plain_shared.rb +96 -0
  93. data/test/shared/authentication/scram_shared.rb +92 -0
  94. data/test/shared/ssl_shared.rb +235 -0
  95. data/test/test_helper.rb +53 -94
  96. data/test/threading/basic_test.rb +120 -0
  97. data/test/tools/mongo_config.rb +708 -0
  98. data/test/tools/mongo_config_test.rb +160 -0
  99. data/test/unit/client_test.rb +381 -0
  100. data/test/unit/collection_test.rb +89 -53
  101. data/test/unit/connection_test.rb +282 -32
  102. data/test/unit/cursor_test.rb +206 -8
  103. data/test/unit/db_test.rb +55 -13
  104. data/test/unit/grid_test.rb +43 -16
  105. data/test/unit/mongo_sharded_client_test.rb +48 -0
  106. data/test/unit/node_test.rb +93 -0
  107. data/test/unit/pool_manager_test.rb +111 -0
  108. data/test/unit/read_pref_test.rb +406 -0
  109. data/test/unit/read_test.rb +159 -0
  110. data/test/unit/safe_test.rb +69 -36
  111. data/test/unit/sharding_pool_manager_test.rb +84 -0
  112. data/test/unit/write_concern_test.rb +175 -0
  113. data.tar.gz.sig +3 -0
  114. metadata +227 -216
  115. metadata.gz.sig +0 -0
  116. data/docs/CREDITS.md +0 -123
  117. data/docs/FAQ.md +0 -116
  118. data/docs/GridFS.md +0 -158
  119. data/docs/HISTORY.md +0 -244
  120. data/docs/RELEASES.md +0 -33
  121. data/docs/REPLICA_SETS.md +0 -72
  122. data/docs/TUTORIAL.md +0 -247
  123. data/docs/WRITE_CONCERN.md +0 -28
  124. data/lib/mongo/exceptions.rb +0 -71
  125. data/lib/mongo/gridfs/grid_io_fix.rb +0 -38
  126. data/lib/mongo/repl_set_connection.rb +0 -342
  127. data/lib/mongo/test.rb +0 -20
  128. data/lib/mongo/util/pool.rb +0 -177
  129. data/lib/mongo/util/uri_parser.rb +0 -185
  130. data/test/async/collection_test.rb +0 -224
  131. data/test/async/connection_test.rb +0 -24
  132. data/test/async/cursor_test.rb +0 -162
  133. data/test/async/worker_pool_test.rb +0 -99
  134. data/test/auxillary/1.4_features.rb +0 -166
  135. data/test/auxillary/authentication_test.rb +0 -68
  136. data/test/auxillary/autoreconnect_test.rb +0 -41
  137. data/test/auxillary/fork_test.rb +0 -30
  138. data/test/auxillary/repl_set_auth_test.rb +0 -58
  139. data/test/auxillary/slave_connection_test.rb +0 -36
  140. data/test/auxillary/threaded_authentication_test.rb +0 -101
  141. data/test/bson/binary_test.rb +0 -15
  142. data/test/bson/bson_test.rb +0 -649
  143. data/test/bson/byte_buffer_test.rb +0 -208
  144. data/test/bson/hash_with_indifferent_access_test.rb +0 -38
  145. data/test/bson/json_test.rb +0 -17
  146. data/test/bson/object_id_test.rb +0 -154
  147. data/test/bson/ordered_hash_test.rb +0 -204
  148. data/test/bson/timestamp_test.rb +0 -24
  149. data/test/collection_test.rb +0 -910
  150. data/test/connection_test.rb +0 -309
  151. data/test/cursor_fail_test.rb +0 -75
  152. data/test/cursor_message_test.rb +0 -43
  153. data/test/cursor_test.rb +0 -483
  154. data/test/db_api_test.rb +0 -726
  155. data/test/db_connection_test.rb +0 -15
  156. data/test/db_test.rb +0 -287
  157. data/test/grid_file_system_test.rb +0 -243
  158. data/test/load/resque/load.rb +0 -21
  159. data/test/load/resque/processor.rb +0 -26
  160. data/test/load/thin/load.rb +0 -24
  161. data/test/load/unicorn/load.rb +0 -23
  162. data/test/load/unicorn/unicorn.rb +0 -29
  163. data/test/replica_sets/connect_test.rb +0 -94
  164. data/test/replica_sets/connection_string_test.rb +0 -32
  165. data/test/replica_sets/count_test.rb +0 -35
  166. data/test/replica_sets/insert_test.rb +0 -53
  167. data/test/replica_sets/pooled_insert_test.rb +0 -55
  168. data/test/replica_sets/query_secondaries.rb +0 -96
  169. data/test/replica_sets/query_test.rb +0 -51
  170. data/test/replica_sets/replication_ack_test.rb +0 -66
  171. data/test/replica_sets/rs_test_helper.rb +0 -27
  172. data/test/safe_test.rb +0 -68
  173. data/test/support/hash_with_indifferent_access.rb +0 -186
  174. data/test/support/keys.rb +0 -45
  175. data/test/support_test.rb +0 -18
  176. data/test/threading/threading_with_large_pool_test.rb +0 -90
  177. data/test/threading_test.rb +0 -87
  178. data/test/tools/auth_repl_set_manager.rb +0 -14
  179. data/test/tools/load.rb +0 -58
  180. data/test/tools/repl_set_manager.rb +0 -266
  181. data/test/tools/sharding_manager.rb +0 -202
  182. data/test/tools/test.rb +0 -4
  183. data/test/unit/pool_test.rb +0 -9
  184. data/test/unit/repl_set_connection_test.rb +0 -59
  185. data/test/uri_test.rb +0 -91
@@ -0,0 +1,111 @@
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'test_helper'
16
+ include Mongo
17
+
18
+ class PoolManagerUnitTest < Test::Unit::TestCase
19
+
20
+ context "Initialization: " do
21
+
22
+ setup do
23
+ TCPSocket.stubs(:new).returns(new_mock_socket)
24
+ @db = new_mock_db
25
+
26
+ @client = stub("MongoClient")
27
+ @client.stubs(:connect_timeout).returns(5)
28
+ @client.stubs(:op_timeout).returns(5)
29
+ @client.stubs(:pool_size).returns(2)
30
+ @client.stubs(:pool_timeout).returns(100)
31
+ @client.stubs(:seeds).returns(['localhost:30000'])
32
+ @client.stubs(:socket_class).returns(TCPSocket)
33
+ @client.stubs(:mongos?).returns(false)
34
+ @client.stubs(:[]).returns(@db)
35
+ @client.stubs(:socket_opts)
36
+
37
+ @client.stubs(:replica_set_name).returns(nil)
38
+ @client.stubs(:log)
39
+ @arbiters = ['localhost:27020']
40
+ @hosts = [
41
+ 'localhost:27017',
42
+ 'localhost:27018',
43
+ 'localhost:27019',
44
+ 'localhost:27020'
45
+ ]
46
+
47
+ @ismaster = {
48
+ 'hosts' => @hosts,
49
+ 'arbiters' => @arbiters,
50
+ 'maxBsonObjectSize' => 1024,
51
+ 'maxMessageSizeBytes' => 1024 * 2.5,
52
+ 'maxWireVersion' => 1,
53
+ 'minWireVersion' => 0
54
+ }
55
+ end
56
+
57
+ should "populate pools correctly" do
58
+
59
+ @db.stubs(:command).returns(
60
+ # First call to get a socket.
61
+ @ismaster.merge({'ismaster' => true}),
62
+
63
+ # Subsequent calls to configure pools.
64
+ @ismaster.merge({'ismaster' => true}),
65
+ @ismaster.merge({'secondary' => true, 'maxBsonObjectSize' => 500}),
66
+ @ismaster.merge({'secondary' => true, 'maxMessageSizeBytes' => 700}),
67
+ @ismaster.merge({'arbiterOnly' => true})
68
+ )
69
+
70
+ seeds = [['localhost', 27017]]
71
+ manager = Mongo::PoolManager.new(@client, seeds)
72
+ @client.stubs(:local_manager).returns(manager)
73
+ manager.connect
74
+
75
+ assert_equal ['localhost', 27017], manager.primary
76
+ assert_equal 27017, manager.primary_pool.port
77
+ assert_equal 2, manager.secondaries.length
78
+ assert_equal [27018, 27019], manager.secondary_pools.map(&:port).sort
79
+ assert_equal [['localhost', 27020]], manager.arbiters
80
+ assert_equal 500, manager.max_bson_size
81
+ assert_equal 700, manager.max_message_size
82
+ end
83
+
84
+ should "populate pools with single unqueryable seed" do
85
+
86
+ @db.stubs(:command).returns(
87
+ # First call to recovering node
88
+ @ismaster.merge({'ismaster' => false, 'secondary' => false}),
89
+
90
+ # Subsequent calls to configure pools.
91
+ @ismaster.merge({'ismaster' => false, 'secondary' => false}),
92
+ @ismaster.merge({'ismaster' => true}),
93
+ @ismaster.merge({'secondary' => true}),
94
+ @ismaster.merge({'arbiterOnly' => true})
95
+ )
96
+
97
+ seeds = [['localhost', 27017]]
98
+ manager = PoolManager.new(@client, seeds)
99
+ @client.stubs(:local_manager).returns(manager)
100
+ manager.connect
101
+
102
+ assert_equal ['localhost', 27018], manager.primary
103
+ assert_equal 27018, manager.primary_pool.port
104
+ assert_equal 1, manager.secondaries.length
105
+ assert_equal 27019, manager.secondary_pools[0].port
106
+ assert_equal [['localhost', 27020]], manager.arbiters
107
+ end
108
+
109
+ end
110
+
111
+ end
@@ -0,0 +1,406 @@
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'test_helper'
16
+
17
+ class ReadPreferenceUnitTest < Test::Unit::TestCase
18
+
19
+ include ReadPreference
20
+
21
+ def setup
22
+ mock_pool = mock()
23
+ mock_pool.stubs(:ping_time).returns(Pool::MAX_PING_TIME)
24
+
25
+ stubs(:primary_pool).returns(mock_pool)
26
+ stubs(:secondary_pools).returns([mock_pool])
27
+ stubs(:pools).returns([mock_pool])
28
+ end
29
+
30
+ def test_select_pool
31
+ ReadPreference::READ_PREFERENCES.map do |rp|
32
+ assert select_pool({:mode => rp, :tags => [], :latency => 15})
33
+ end
34
+ end
35
+
36
+ def test_sok_mapreduce_out_string_returns_false
37
+ command = BSON::OrderedHash['mapreduce', 'test-collection',
38
+ 'out', 'new-test-collection']
39
+ assert_equal false, ReadPreference::secondary_ok?(command)
40
+ end
41
+
42
+ def test_sok_mapreduce_replace_collection_returns_false
43
+ command = BSON::OrderedHash['mapreduce', 'test-collection',
44
+ 'out', BSON::OrderedHash['replace', 'new-test-collection']]
45
+ assert_equal false, ReadPreference::secondary_ok?(command)
46
+ end
47
+
48
+ def test_sok_mapreduce_inline_collection_returns_false
49
+ command = BSON::OrderedHash['mapreduce', 'test-collection',
50
+ 'out', 'inline']
51
+ assert_equal false, ReadPreference::secondary_ok?(command)
52
+ end
53
+
54
+ def test_sok_inline_symbol_mapreduce_returns_true
55
+ command = BSON::OrderedHash['mapreduce', 'test-collection',
56
+ 'out', BSON::OrderedHash[:inline, 'true']]
57
+ assert_equal true, ReadPreference::secondary_ok?(command)
58
+ end
59
+
60
+ def test_sok_inline_string_mapreduce_returns_true
61
+ command = BSON::OrderedHash['mapreduce', 'test-collection',
62
+ 'out', BSON::OrderedHash['inline', 'true']]
63
+ assert_equal true, ReadPreference::secondary_ok?(command)
64
+ end
65
+
66
+ def test_sok_count_true
67
+ command = BSON::OrderedHash['count', 'test-collection',
68
+ 'query', BSON::OrderedHash['a', 'b']]
69
+ assert_equal true, ReadPreference::secondary_ok?(command)
70
+ end
71
+
72
+ def test_sok_server_status_returns_false
73
+ command = BSON::OrderedHash['serverStatus', 1]
74
+ assert_equal false, ReadPreference::secondary_ok?(command)
75
+ end
76
+
77
+ def test_sok_text_returns_true
78
+ command = BSON::OrderedHash['text', BSON::OrderedHash['search', 'coffee']]
79
+ assert_equal true, ReadPreference::secondary_ok?(command)
80
+ end
81
+
82
+ def test_cmd_reroute_with_secondary
83
+ ReadPreference::expects(:warn).with(regexp_matches(/rerouted to primary/))
84
+ command = BSON::OrderedHash['mapreduce', 'test-collection',
85
+ 'out', 'new-test-collection']
86
+ assert_equal :primary, ReadPreference::cmd_read_pref(:secondary, command)
87
+ end
88
+
89
+ def test_find_and_modify_reroute_with_secondary
90
+ ReadPreference::expects(:warn).with(regexp_matches(/rerouted to primary/))
91
+ command = BSON::OrderedHash['findAndModify', 'test-collection',
92
+ 'query', {}]
93
+ assert_equal :primary, ReadPreference::cmd_read_pref(:secondary, command)
94
+ end
95
+
96
+ def test_cmd_no_reroute_with_secondary
97
+ command = BSON::OrderedHash['mapreduce', 'test-collection',
98
+ 'out', BSON::OrderedHash['inline', 'true']]
99
+ assert_equal :secondary, ReadPreference::cmd_read_pref(:secondary, command)
100
+ end
101
+
102
+ def test_cmd_no_reroute_with_primary
103
+ command = BSON::OrderedHash['mapreduce', 'test-collection',
104
+ 'out', 'new-test-collection']
105
+ assert_equal :primary, ReadPreference::cmd_read_pref(:primary, command)
106
+ end
107
+
108
+ def test_cmd_no_reroute_with_primary_secondary_ok
109
+ command = BSON::OrderedHash['mapreduce', 'test-collection',
110
+ 'out', BSON::OrderedHash['inline', 'true']]
111
+ assert_equal :primary, ReadPreference::cmd_read_pref(:primary, command)
112
+ end
113
+
114
+ def test_parallel_scan_secondary_ok
115
+ command = BSON::OrderedHash['parallelCollectionScan', 'test-collection',
116
+ 'numCursors', 3]
117
+ assert_equal true, ReadPreference::secondary_ok?(command)
118
+ end
119
+
120
+ def test_primary_with_tags_raises_error
121
+ # Confirm that an error is raised if you provide tags and read pref is primary
122
+ client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
123
+ client.stubs(:primary_pool).returns(mock_pool)
124
+ read_pref_tags = {'dc' => 'nyc'}
125
+ read_pref = client.read_preference.merge(:mode => :primary,
126
+ :tags => [read_pref_tags],
127
+ :latency => 6)
128
+ assert_raise Mongo::MongoArgumentError do
129
+ client.select_pool(read_pref)
130
+ end
131
+ end
132
+
133
+ def test_secondary_pref
134
+ # Confirm that a primary is not selected
135
+ primary_pool = mock('pool')
136
+
137
+ secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
138
+ secondary_chi = mock_pool({'dc' => 'chicago'}, 5)
139
+ secondary_pools = [secondary_nyc, secondary_chi]
140
+
141
+ client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
142
+ client.stubs(:primary_pool).returns(primary_pool)
143
+ client.stubs(:secondary_pools).returns(secondary_pools)
144
+
145
+ read_pref = client.read_preference.merge(:mode => :secondary)
146
+ assert_not_equal Hash.new, client.select_pool(read_pref).tags
147
+ end
148
+
149
+ def test_secondary_tags_pref
150
+ # Confirm that a secondary with matching tags is selected
151
+ secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
152
+ secondary_chi = mock_pool({'dc' => 'chicago'}, 5)
153
+ secondary_pools = [secondary_nyc, secondary_chi]
154
+
155
+ client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
156
+ client.stubs(:primary_pool).returns(mock_pool)
157
+ client.stubs(:secondary_pools).returns(secondary_pools)
158
+
159
+ read_pref_tags = {'dc' => 'nyc'}
160
+ read_pref = client.read_preference.merge(:mode => :secondary,
161
+ :tags => [read_pref_tags])
162
+ assert_equal read_pref_tags, client.select_pool(read_pref).tags
163
+ end
164
+
165
+ def test_secondary_tags_with_latency
166
+ # Confirm that between more than 1 secondary matching tags, only the one within
167
+ # max acceptable latency is selected
168
+ secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
169
+ secondary_nyc2 = mock_pool({'dc' => 'nyc'}, 25)
170
+ secondary_pools = [secondary_nyc, secondary_nyc2]
171
+
172
+ client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
173
+ client.stubs(:secondary_pools).returns(secondary_pools)
174
+
175
+ read_pref_tags = {'dc' => 'nyc'}
176
+ read_pref = client.read_preference.merge(:mode => :secondary,
177
+ :tags => [read_pref_tags])
178
+ assert_equal 5, client.select_pool(read_pref).ping_time
179
+ end
180
+
181
+ def test_secondary_latency_pref
182
+ # Confirm that only the latency of pools matching tags is considered
183
+ secondary_nyc = mock_pool({'dc' => 'nyc'}, 10)
184
+ secondary_chi = mock_pool({'dc' => 'chicago'}, 5)
185
+ secondary_pools = [secondary_nyc, secondary_chi]
186
+
187
+ client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
188
+ client.stubs(:primary_pool).returns(mock_pool)
189
+ client.stubs(:secondary_pools).returns(secondary_pools)
190
+
191
+ read_pref_tags = {'dc' => 'nyc'}
192
+ read_pref = client.read_preference.merge(:mode => :secondary,
193
+ :tags => [read_pref_tags],
194
+ :latency => 3)
195
+ assert_equal read_pref_tags, client.select_pool(read_pref).tags
196
+ end
197
+
198
+ def test_primary_preferred_primary_available
199
+ # Confirm that the primary is always selected if it's available
200
+ secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
201
+ secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
202
+ secondary_pools = [secondary_nyc, secondary_chi]
203
+ primary_pool = mock_pool
204
+
205
+ client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
206
+ client.stubs(:secondary_pools).returns(secondary_pools)
207
+ client.stubs(:primary_pool).returns(primary_pool)
208
+
209
+ read_pref_tags = {'dc' => 'chicago'}
210
+ read_pref = client.read_preference.merge(:mode => :primary_preferred,
211
+ :tags => [read_pref_tags],
212
+ :latency => 6)
213
+ assert_equal primary_pool, client.select_pool(read_pref)
214
+ end
215
+
216
+ def test_primary_preferred_primary_not_available
217
+ # Confirm that a secondary with matching tags is selected if primary is
218
+ # not available
219
+ secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
220
+ secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
221
+ secondary_pools = [secondary_nyc, secondary_chi]
222
+
223
+ client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
224
+ client.stubs(:secondary_pools).returns(secondary_pools)
225
+
226
+ read_pref_tags = {'dc' => 'chicago'}
227
+ read_pref = client.read_preference.merge(:mode => :primary_preferred,
228
+ :tags => [read_pref_tags],
229
+ :latency => 6)
230
+ assert_equal read_pref_tags, client.select_pool(read_pref).tags
231
+ end
232
+
233
+ def test_primary_preferred_primary_not_available_and_no_matching_tags
234
+ # Confirm that tags are taken into account if primary is not available and
235
+ # secondaries are considered for selection.
236
+ secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
237
+ secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
238
+ secondary_pools = [secondary_nyc, secondary_chi]
239
+
240
+ client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
241
+ client.stubs(:secondary_pools).returns(secondary_pools)
242
+
243
+ read_pref_tags = {'dc' => 'other_city'}
244
+ read_pref = client.read_preference.merge(:mode => :primary_preferred,
245
+ :tags => [read_pref_tags],
246
+ :latency => 6)
247
+ assert_equal nil, client.select_pool(read_pref)
248
+ end
249
+
250
+ def test_secondary_preferred_with_tags
251
+ # Confirm that tags are taken into account
252
+ secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
253
+ secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
254
+ secondary_pools = [secondary_nyc, secondary_chi]
255
+
256
+ client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
257
+ client.stubs(:secondary_pools).returns(secondary_pools)
258
+
259
+ read_pref_tags = {'dc' => 'chicago'}
260
+ read_pref = client.read_preference.merge(:mode => :secondary_preferred,
261
+ :tags => [read_pref_tags],
262
+ :latency => 6)
263
+ assert_equal read_pref_tags, client.select_pool(read_pref).tags
264
+ end
265
+
266
+ def test_secondary_preferred_with_no_matching_tags
267
+ # Confirm that the primary is selected if no secondaries with matching tags
268
+ # are found
269
+ secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
270
+ secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
271
+ secondary_pools = [secondary_nyc, secondary_chi]
272
+
273
+ client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
274
+ client.stubs(:secondary_pools).returns(secondary_pools)
275
+ client.stubs(:primary_pool).returns(mock_pool)
276
+
277
+ read_pref_tags = {'dc' => 'other_city'}
278
+ read_pref = client.read_preference.merge(:mode => :secondary_preferred,
279
+ :tags => [read_pref_tags],
280
+ :latency => 6)
281
+ assert_equal Hash.new, client.select_pool(read_pref).tags
282
+ end
283
+
284
+ def test_nearest_with_tags
285
+ # Confirm that tags are taken into account when selecting nearest
286
+ secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
287
+ secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
288
+ primary_pool = mock_pool
289
+ pools = [secondary_nyc, secondary_chi, primary_pool]
290
+
291
+ client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
292
+ client.stubs(:pools).returns(pools)
293
+
294
+ read_pref_tags = {'dc' => 'nyc'}
295
+ read_pref = client.read_preference.merge(:mode => :nearest,
296
+ :tags => [read_pref_tags],
297
+ :latency => 3)
298
+ assert_equal read_pref_tags, client.select_pool(read_pref).tags
299
+ end
300
+
301
+ def test_nearest
302
+ # Confirm that the nearest is selected when tags aren't specified
303
+ secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
304
+ secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
305
+ primary_pool = mock_pool({}, 1)
306
+ pools = [secondary_nyc, secondary_chi, primary_pool]
307
+
308
+ client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
309
+ client.stubs(:pools).returns(pools)
310
+
311
+ read_pref = client.read_preference.merge(:mode => :nearest,
312
+ :latency => 3)
313
+ assert_equal Hash.new, client.select_pool(read_pref).tags
314
+ end
315
+
316
+ def test_nearest_primary_matching
317
+ # Confirm that a primary matching tags is included in nearest candidates
318
+ secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
319
+ secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
320
+ primary_pool = mock_pool({'dc' => 'boston'}, 1)
321
+ secondary_pools = [secondary_nyc, secondary_chi]
322
+
323
+ client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
324
+ client.stubs(:secondary_pools).returns(secondary_pools)
325
+ client.stubs(:primary_pool).returns(primary_pool)
326
+ client.stubs(:pools).returns(secondary_pools << primary_pool)
327
+
328
+ read_pref_tags = {'dc' => 'boston'}
329
+ read_pref = client.read_preference.merge(:mode => :nearest,
330
+ :tags => [read_pref_tags])
331
+ assert_equal primary_pool, client.select_pool(read_pref)
332
+ end
333
+
334
+ def test_nearest_primary_not_matching
335
+ # Confirm that a primary not matching tags is not included in nearest candidates
336
+ secondary_nyc = mock_pool({'dc' => 'nyc'}, 25)
337
+ secondary_chi = mock_pool({'dc' => 'chicago'}, 25)
338
+ primary_pool = mock_pool({'dc' => 'boston'}, 1)
339
+ secondary_pools = [secondary_nyc, secondary_chi]
340
+
341
+ client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
342
+ client.stubs(:secondary_pools).returns(secondary_pools)
343
+ client.stubs(:primary_pool).returns(mock_pool)
344
+ client.stubs(:pools).returns(secondary_pools << primary_pool)
345
+
346
+ read_pref_tags = {'dc' => 'SF'}
347
+ read_pref = client.read_preference.merge(:mode => :nearest,
348
+ :tags => [read_pref_tags])
349
+ assert_equal nil, client.select_pool(read_pref)
350
+ end
351
+
352
+ def test_nearest_primary_not_matching_excluded_from_latency_calculations
353
+ # Confirm that a primary not matching tags is not included in the latency calculations
354
+ secondary1 = mock_pool({'dc' => 'nyc'}, 10)
355
+ secondary2 = mock_pool({'dc' => 'nyc'}, 10)
356
+ primary_pool = mock_pool({'dc' => 'boston'}, 1)
357
+ secondary_pools = [secondary1, secondary2]
358
+
359
+ client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
360
+ client.stubs(:secondary_pools).returns(secondary_pools)
361
+ client.stubs(:primary_pool).returns(mock_pool)
362
+ client.stubs(:pools).returns(secondary_pools << primary_pool)
363
+
364
+ read_pref_tags = {'dc' => 'nyc'}
365
+ read_pref = client.read_preference.merge(:mode => :nearest,
366
+ :tags => [read_pref_tags],
367
+ :latency => 5)
368
+ assert_equal 'nyc', client.select_pool(read_pref).tags['dc']
369
+ end
370
+
371
+ def test_nearest_matching_tags_but_not_available
372
+ # Confirm that even if a server matches a tag, it's not selected if it's down
373
+ secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
374
+ secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
375
+ primary_pool = mock_pool({'dc' => 'chicago'}, nil)
376
+ secondary_pools = [secondary_nyc, secondary_chi]
377
+
378
+ client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
379
+ client.stubs(:secondary_pools).returns(secondary_pools)
380
+ client.stubs(:primary_pool).returns(primary_pool)
381
+ client.stubs(:pools).returns(secondary_pools << primary_pool)
382
+
383
+ tags = [{'dc' => 'nyc'}, {'dc' => 'chicago'}, {}]
384
+ read_pref = client.read_preference.merge(:mode => :nearest,
385
+ :tags => tags)
386
+ assert_equal secondary_nyc, client.select_pool(read_pref)
387
+ end
388
+
389
+ def test_nearest_multiple_tags
390
+ # Confirm that with multiple tags in the read preference, servers are still selected
391
+ secondary_nyc = mock_pool({}, 5)
392
+ secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
393
+ primary_pool = mock_pool({}, 1)
394
+ secondary_pools = [secondary_nyc, secondary_chi]
395
+
396
+ client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
397
+ client.stubs(:secondary_pools).returns(secondary_pools)
398
+ client.stubs(:primary_pool).returns(mock_pool)
399
+ client.stubs(:pools).returns(secondary_pools << primary_pool)
400
+
401
+ tags = [{'dc' => 'nyc'}, {'dc' => 'chicago'}, {}]
402
+ read_pref = client.read_preference.merge(:mode => :nearest,
403
+ :tags => tags)
404
+ assert_equal secondary_chi, client.select_pool(read_pref)
405
+ end
406
+ end
@@ -0,0 +1,159 @@
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'test_helper'
16
+
17
+ class ReadUnitTest < Test::Unit::TestCase
18
+
19
+ context "Read mode on standard connection: " do
20
+ setup do
21
+ @read = :secondary
22
+ @client = MongoClient.new('localhost', 27017, :read => @read, :connect => false)
23
+ end
24
+
25
+ end
26
+
27
+ context "Read preferences on replica set connection: " do
28
+ setup do
29
+ @read = :secondary_preferred
30
+ @acceptable_latency = 100
31
+ @tags = {"dc" => "Tyler", "rack" => "Brock"}
32
+ @bad_tags = {"wow" => "cool"}
33
+ @client = MongoReplicaSetClient.new(
34
+ ['localhost:27017'],
35
+ :read => @read,
36
+ :tag_sets => @tags,
37
+ :secondary_acceptable_latency_ms => @acceptable_latency,
38
+ :connect => false
39
+ )
40
+ end
41
+
42
+ should "store read preference on MongoClient" do
43
+ assert_equal @read, @client.read
44
+ assert_equal @tags, @client.tag_sets
45
+ assert_equal @acceptable_latency, @client.acceptable_latency
46
+ end
47
+
48
+ should "propogate to DB" do
49
+ db = @client[TEST_DB]
50
+ assert_equal @read, db.read
51
+ assert_equal @tags, db.tag_sets
52
+ assert_equal @acceptable_latency, db.acceptable_latency
53
+
54
+ db = @client.db(TEST_DB)
55
+ assert_equal @read, db.read
56
+ assert_equal @tags, db.tag_sets
57
+ assert_equal @acceptable_latency, db.acceptable_latency
58
+
59
+ db = DB.new(TEST_DB, @client)
60
+ assert_equal @read, db.read
61
+ assert_equal @tags, db.tag_sets
62
+ assert_equal @acceptable_latency, db.acceptable_latency
63
+ end
64
+
65
+ should "allow db override" do
66
+ db = DB.new(TEST_DB, @client, :read => :primary, :tag_sets => @bad_tags, :acceptable_latency => 25)
67
+ assert_equal :primary, db.read
68
+ assert_equal @bad_tags, db.tag_sets
69
+ assert_equal 25, db.acceptable_latency
70
+
71
+ db = @client.db(TEST_DB, :read => :primary, :tag_sets => @bad_tags, :acceptable_latency => 25)
72
+ assert_equal :primary, db.read
73
+ assert_equal @bad_tags, db.tag_sets
74
+ assert_equal 25, db.acceptable_latency
75
+ end
76
+
77
+ context "on DB: " do
78
+ setup do
79
+ @db = @client[TEST_DB]
80
+ end
81
+
82
+ should "propogate to collection" do
83
+ col = @db.collection('read-unit-test')
84
+ assert_equal @read, col.read
85
+ assert_equal @tags, col.tag_sets
86
+ assert_equal @acceptable_latency, col.acceptable_latency
87
+
88
+ col = @db['read-unit-test']
89
+ assert_equal @read, col.read
90
+ assert_equal @tags, col.tag_sets
91
+ assert_equal @acceptable_latency, col.acceptable_latency
92
+
93
+ col = Collection.new('read-unit-test', @db)
94
+ assert_equal @read, col.read
95
+ assert_equal @tags, col.tag_sets
96
+ assert_equal @acceptable_latency, col.acceptable_latency
97
+ end
98
+
99
+ should "allow override on collection" do
100
+ col = @db.collection('read-unit-test', :read => :primary, :tag_sets => @bad_tags, :acceptable_latency => 25)
101
+ assert_equal :primary, col.read
102
+ assert_equal @bad_tags, col.tag_sets
103
+ assert_equal 25, col.acceptable_latency
104
+
105
+ col = Collection.new('read-unit-test', @db, :read => :primary, :tag_sets => @bad_tags, :acceptable_latency => 25)
106
+ assert_equal :primary, col.read
107
+ assert_equal @bad_tags, col.tag_sets
108
+ assert_equal 25, col.acceptable_latency
109
+ end
110
+ end
111
+
112
+ context "on read mode ops" do
113
+ setup do
114
+ @col = @client[TEST_DB]['read-unit-test']
115
+ @mock_socket = new_mock_socket
116
+ end
117
+
118
+ should "use default value on query" do
119
+ @cursor = @col.find({:a => 1})
120
+ sock = new_mock_socket
121
+ read_pool = stub(:checkin => true)
122
+ @client.stubs(:read_pool).returns(read_pool)
123
+ local_manager = PoolManager.new(@client, @client.seeds)
124
+ @client.stubs(:local_manager).returns(local_manager)
125
+ primary_pool = stub(:checkin => true)
126
+ sock.stubs(:pool).returns(primary_pool)
127
+ @client.stubs(:primary_pool).returns(primary_pool)
128
+ @client.expects(:checkout_reader).returns(sock)
129
+ @client.expects(:receive_message).with do |o, m, l, s, c, r|
130
+ r == nil
131
+ end.returns([[], 0, 0])
132
+
133
+ @cursor.next
134
+ end
135
+
136
+ should "allow override default value on query" do
137
+ @cursor = @col.find({:a => 1}, :read => :primary)
138
+ sock = new_mock_socket
139
+ local_manager = PoolManager.new(@client, @client.seeds)
140
+ @client.stubs(:local_manager).returns(local_manager)
141
+ primary_pool = stub(:checkin => true)
142
+ sock.stubs(:pool).returns(primary_pool)
143
+ @client.stubs(:primary_pool).returns(primary_pool)
144
+ @client.expects(:checkout_reader).returns(sock)
145
+ @client.expects(:receive_message).with do |o, m, l, s, c, r|
146
+ r == nil
147
+ end.returns([[], 0, 0])
148
+
149
+ @cursor.next
150
+ end
151
+
152
+ should "allow override alternate value on query" do
153
+ assert_raise MongoArgumentError do
154
+ @col.find_one({:a => 1}, :read => {:dc => "ny"})
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end