mongo 1.8.5 → 1.8.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/lib/mongo/db.rb +6 -1
- data/lib/mongo/mongo_client.rb +28 -27
- data/lib/mongo/mongo_replica_set_client.rb +15 -21
- data/lib/mongo/mongo_sharded_client.rb +6 -9
- data/lib/mongo/util/node.rb +10 -3
- data/lib/mongo/util/pool_manager.rb +47 -29
- data/lib/mongo/util/read_preference.rb +6 -6
- data/lib/mongo/util/sharding_pool_manager.rb +31 -18
- data/test/functional/authentication_test.rb +1 -4
- data/test/replica_set/authentication_test.rb +2 -5
- data/test/replica_set/client_test.rb +15 -0
- data/test/replica_set/max_values_test.rb +35 -20
- data/test/replica_set/refresh_test.rb +31 -0
- data/test/sharded_cluster/basic_test.rb +2 -2
- data/test/shared/authentication.rb +17 -0
- data/test/test_helper.rb +7 -1
- data/test/tools/mongo_config.rb +13 -7
- data/test/unit/cursor_test.rb +6 -6
- data/test/unit/mongo_sharded_client_test.rb +4 -4
- data/test/unit/read_pref_test.rb +21 -0
- data/test/unit/sharding_pool_manager_test.rb +0 -25
- metadata +6 -4
- metadata.gz.sig +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5bdb78ad2291ea3ecb387526ccdbd162ffd51687
|
4
|
+
data.tar.gz: 6b3ec42fadc3f0beea953a08fe07a2d2ff206cba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83bb86f711779590a47d0d8d83fea4d844cd8ebdcde6617109c9172dd9d6961688d3faf03c1c7d0c0601575ed1a253522aecc2b26f2e81fb7980079ca3dff9fd
|
7
|
+
data.tar.gz: 880a70826f29e9e60fff3d26ede30390da593e78ad095f635743ffec711ce22af34da2d89318fd48f7825e2583bbec02da80c78899d4388386a76c769ee1e80e
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/Rakefile
CHANGED
@@ -8,10 +8,10 @@ rescue LoadError
|
|
8
8
|
raise '[FAIL] Bundler not found! Install it with `gem install bundler; bundle install`.'
|
9
9
|
end
|
10
10
|
|
11
|
-
if ENV
|
11
|
+
if ENV.has_key?('TEST') || ENV.has_key?('TRAVIS_TEST')
|
12
12
|
Bundler.require(:default, :testing)
|
13
13
|
else
|
14
|
-
Bundler.require(:default, :deploy, :
|
14
|
+
Bundler.require(:default, :testing, :deploy, :development)
|
15
15
|
end
|
16
16
|
|
17
17
|
Dir.glob(File.join('tasks', '**', '*.rake')).sort.each { |rake| load File.expand_path(rake) }
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.8.
|
1
|
+
1.8.6
|
data/lib/mongo/db.rb
CHANGED
@@ -192,7 +192,12 @@ module Mongo
|
|
192
192
|
user = users.find_one({:user => username}) || {:user => username}
|
193
193
|
user['pwd'] = Mongo::Support.hash_password(username, password)
|
194
194
|
user['readOnly'] = true if read_only;
|
195
|
-
|
195
|
+
begin
|
196
|
+
users.save(user)
|
197
|
+
rescue OperationFailure => ex
|
198
|
+
# adding first admin user fails GLE in MongoDB 2.2
|
199
|
+
raise ex unless ex.message =~ /login/
|
200
|
+
end
|
196
201
|
user
|
197
202
|
end
|
198
203
|
|
data/lib/mongo/mongo_client.rb
CHANGED
@@ -160,7 +160,7 @@ module Mongo
|
|
160
160
|
|
161
161
|
# Initialize a connection to MongoDB using the MongoDB URI spec.
|
162
162
|
#
|
163
|
-
# Since MongoClient.new cannot be used with any <code>ENV["MONGODB_URI"]</code> that has multiple hosts (implying a replicaset),
|
163
|
+
# Since MongoClient.new cannot be used with any <code>ENV["MONGODB_URI"]</code> that has multiple hosts (implying a replicaset),
|
164
164
|
# you may use this when the type of your connection varies by environment and should be determined solely from <code>ENV["MONGODB_URI"]</code>.
|
165
165
|
#
|
166
166
|
# @param uri [String]
|
@@ -469,7 +469,7 @@ module Mongo
|
|
469
469
|
# NOTE: Do check if this needs to be more stringent.
|
470
470
|
# Probably not since if any node raises a connection failure, all nodes will be closed.
|
471
471
|
def connected?
|
472
|
-
@primary_pool && !@primary_pool.closed?
|
472
|
+
!!(@primary_pool && !@primary_pool.closed?)
|
473
473
|
end
|
474
474
|
|
475
475
|
# Determine if the connection is active. In a normal case the *server_info* operation
|
@@ -545,6 +545,32 @@ module Mongo
|
|
545
545
|
end
|
546
546
|
end
|
547
547
|
|
548
|
+
# Internal method for checking isMaster() on a given node.
|
549
|
+
#
|
550
|
+
# @param node [Array] Port and host for the target node
|
551
|
+
# @return [Hash] Response from isMaster()
|
552
|
+
#
|
553
|
+
# @private
|
554
|
+
def check_is_master(node)
|
555
|
+
begin
|
556
|
+
host, port = *node
|
557
|
+
config = nil
|
558
|
+
socket = @socket_class.new(host, port, @op_timeout, @connect_timeout)
|
559
|
+
if @connect_timeout
|
560
|
+
Timeout::timeout(@connect_timeout, OperationTimeout) do
|
561
|
+
config = self['admin'].command({:ismaster => 1}, :socket => socket)
|
562
|
+
end
|
563
|
+
else
|
564
|
+
config = self['admin'].command({:ismaster => 1}, :socket => socket)
|
565
|
+
end
|
566
|
+
rescue OperationFailure, SocketError, SystemCallError, IOError
|
567
|
+
close
|
568
|
+
ensure
|
569
|
+
socket.close unless socket.nil? || socket.closed?
|
570
|
+
end
|
571
|
+
config
|
572
|
+
end
|
573
|
+
|
548
574
|
protected
|
549
575
|
|
550
576
|
def valid_opts
|
@@ -621,31 +647,6 @@ module Mongo
|
|
621
647
|
|
622
648
|
private
|
623
649
|
|
624
|
-
def check_is_master(node)
|
625
|
-
begin
|
626
|
-
host, port = *node
|
627
|
-
socket = nil
|
628
|
-
config = nil
|
629
|
-
|
630
|
-
socket = @socket_class.new(host, port, @op_timeout, @connect_timeout)
|
631
|
-
if(@connect_timeout)
|
632
|
-
Timeout::timeout(@connect_timeout, OperationTimeout) do
|
633
|
-
config = self['admin'].command({:ismaster => 1}, :socket => socket)
|
634
|
-
end
|
635
|
-
else
|
636
|
-
config = self['admin'].command({:ismaster => 1}, :socket => socket)
|
637
|
-
end
|
638
|
-
rescue OperationFailure, SocketError, SystemCallError, IOError
|
639
|
-
close
|
640
|
-
ensure
|
641
|
-
if socket
|
642
|
-
socket.close unless socket.closed?
|
643
|
-
end
|
644
|
-
end
|
645
|
-
|
646
|
-
config
|
647
|
-
end
|
648
|
-
|
649
650
|
# Set the specified node as primary.
|
650
651
|
def set_primary(node)
|
651
652
|
host, port = *node
|
@@ -137,14 +137,9 @@ module Mongo
|
|
137
137
|
# Lock for request ids.
|
138
138
|
@id_lock = Mutex.new
|
139
139
|
|
140
|
-
@pool_mutex = Mutex.new
|
141
140
|
@connected = false
|
142
141
|
|
143
|
-
@safe_mutex_lock = Mutex.new
|
144
|
-
@safe_mutexes = Hash.new {|hash, key| hash[key] = Mutex.new}
|
145
|
-
|
146
142
|
@connect_mutex = Mutex.new
|
147
|
-
@refresh_mutex = Mutex.new
|
148
143
|
|
149
144
|
@mongos = false
|
150
145
|
|
@@ -162,7 +157,8 @@ module Mongo
|
|
162
157
|
end
|
163
158
|
|
164
159
|
# Initiate a connection to the replica set.
|
165
|
-
def connect
|
160
|
+
def connect(force = !connected?)
|
161
|
+
return unless force
|
166
162
|
log(:info, "Connecting...")
|
167
163
|
|
168
164
|
# Prevent recursive connection attempts from the same thread.
|
@@ -170,15 +166,18 @@ module Mongo
|
|
170
166
|
# infinitely while attempting to connect and continually failing. Instead, fail fast.
|
171
167
|
raise ConnectionFailure, "Failed to get node data." if thread_local[:locks][:connecting] == true
|
172
168
|
|
169
|
+
current_version = @refresh_version
|
173
170
|
@connect_mutex.synchronize do
|
174
|
-
|
171
|
+
# don't try to connect if another thread has done so while we were waiting for the lock
|
172
|
+
return unless current_version == @refresh_version
|
175
173
|
begin
|
176
174
|
thread_local[:locks][:connecting] = true
|
177
175
|
if @manager
|
178
|
-
|
176
|
+
ensure_manager
|
177
|
+
@manager.refresh!(@seeds)
|
179
178
|
else
|
180
179
|
@manager = PoolManager.new(self, @seeds)
|
181
|
-
|
180
|
+
ensure_manager
|
182
181
|
@manager.connect
|
183
182
|
end
|
184
183
|
ensure
|
@@ -210,6 +209,7 @@ module Mongo
|
|
210
209
|
end
|
211
210
|
|
212
211
|
log(:debug, "Checking replica set connection health...")
|
212
|
+
ensure_manager
|
213
213
|
@manager.check_connection_health
|
214
214
|
|
215
215
|
if @manager.refresh_required?
|
@@ -227,9 +227,7 @@ module Mongo
|
|
227
227
|
# to get the refresh lock.
|
228
228
|
def hard_refresh!
|
229
229
|
log(:info, "Initiating hard refresh...")
|
230
|
-
|
231
|
-
|
232
|
-
@refresh_version += 1
|
230
|
+
connect(true)
|
233
231
|
return true
|
234
232
|
end
|
235
233
|
|
@@ -420,6 +418,10 @@ module Mongo
|
|
420
418
|
local_manager ? local_manager.secondary_pools : []
|
421
419
|
end
|
422
420
|
|
421
|
+
def pools
|
422
|
+
local_manager ? local_manager.pools : []
|
423
|
+
end
|
424
|
+
|
423
425
|
def tag_map
|
424
426
|
local_manager ? local_manager.tag_map : {}
|
425
427
|
end
|
@@ -475,16 +477,8 @@ module Mongo
|
|
475
477
|
def sync_refresh
|
476
478
|
if @refresh_mode == :sync &&
|
477
479
|
((Time.now - @last_refresh) > @refresh_interval)
|
478
|
-
|
479
480
|
@last_refresh = Time.now
|
480
|
-
|
481
|
-
if @refresh_mutex.try_lock
|
482
|
-
begin
|
483
|
-
refresh
|
484
|
-
ensure
|
485
|
-
@refresh_mutex.unlock
|
486
|
-
end
|
487
|
-
end
|
481
|
+
refresh
|
488
482
|
end
|
489
483
|
end
|
490
484
|
end
|
@@ -41,14 +41,9 @@ module Mongo
|
|
41
41
|
# Lock for request ids.
|
42
42
|
@id_lock = Mutex.new
|
43
43
|
|
44
|
-
@pool_mutex = Mutex.new
|
45
44
|
@connected = false
|
46
45
|
|
47
|
-
@safe_mutex_lock = Mutex.new
|
48
|
-
@safe_mutexes = Hash.new {|hash, key| hash[key] = Mutex.new}
|
49
|
-
|
50
46
|
@connect_mutex = Mutex.new
|
51
|
-
@refresh_mutex = Mutex.new
|
52
47
|
|
53
48
|
@mongos = true
|
54
49
|
|
@@ -66,7 +61,7 @@ module Mongo
|
|
66
61
|
end
|
67
62
|
|
68
63
|
# Initiate a connection to the sharded cluster.
|
69
|
-
def connect(force =
|
64
|
+
def connect(force = !connected?)
|
70
65
|
return unless force
|
71
66
|
log(:info, "Connecting...")
|
72
67
|
|
@@ -79,10 +74,11 @@ module Mongo
|
|
79
74
|
begin
|
80
75
|
thread_local[:locks][:connecting] = true
|
81
76
|
if @manager
|
77
|
+
thread_local[:managers][self] = @manager
|
82
78
|
@manager.refresh! @seeds
|
83
79
|
else
|
84
80
|
@manager = ShardingPoolManager.new(self, @seeds)
|
85
|
-
|
81
|
+
ensure_manager
|
86
82
|
@manager.connect
|
87
83
|
end
|
88
84
|
ensure
|
@@ -108,7 +104,7 @@ module Mongo
|
|
108
104
|
end
|
109
105
|
|
110
106
|
def connected?
|
111
|
-
@connected && @manager.primary_pool
|
107
|
+
!!(@connected && @manager.primary_pool)
|
112
108
|
end
|
113
109
|
|
114
110
|
# Returns +true+ if it's okay to read from a secondary node.
|
@@ -140,7 +136,8 @@ module Mongo
|
|
140
136
|
# @param opts [ Hash ] Any of the options available for MongoShardedClient.new
|
141
137
|
#
|
142
138
|
# @return [ Mongo::MongoShardedClient ] The sharded client.
|
143
|
-
def self.from_uri(uri
|
139
|
+
def self.from_uri(uri, options = {})
|
140
|
+
uri ||= ENV['MONGODB_URI']
|
144
141
|
URIParser.new(uri).connection(options, false, true)
|
145
142
|
end
|
146
143
|
end
|
data/lib/mongo/util/node.rb
CHANGED
@@ -90,6 +90,7 @@ module Mongo
|
|
90
90
|
end
|
91
91
|
|
92
92
|
@config = @client['admin'].command({:ismaster => 1}, :socket => @socket)
|
93
|
+
update_max_sizes
|
93
94
|
|
94
95
|
if @config['msg']
|
95
96
|
@client.log(:warn, "#{config['msg']}")
|
@@ -106,7 +107,6 @@ module Mongo
|
|
106
107
|
close
|
107
108
|
end
|
108
109
|
end
|
109
|
-
@manager.update_max_sizes
|
110
110
|
end
|
111
111
|
|
112
112
|
# Return a list of replica set nodes from the config.
|
@@ -151,11 +151,11 @@ module Mongo
|
|
151
151
|
end
|
152
152
|
|
153
153
|
def max_bson_size
|
154
|
-
|
154
|
+
@max_bson_size || DEFAULT_MAX_BSON_SIZE
|
155
155
|
end
|
156
156
|
|
157
157
|
def max_message_size
|
158
|
-
|
158
|
+
@max_message_size || max_bson_size * MESSAGE_SIZE_FACTOR
|
159
159
|
end
|
160
160
|
|
161
161
|
protected
|
@@ -186,5 +186,12 @@ module Mongo
|
|
186
186
|
end
|
187
187
|
end
|
188
188
|
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
def update_max_sizes
|
193
|
+
@max_bson_size = config['maxBsonObjectSize'] || DEFAULT_MAX_BSON_SIZE
|
194
|
+
@max_message_size = config['maxMessageSizeBytes'] || @max_bson_size * MESSAGE_SIZE_FACTOR
|
195
|
+
end
|
189
196
|
end
|
190
197
|
end
|
@@ -9,8 +9,6 @@ module Mongo
|
|
9
9
|
:primary_pool,
|
10
10
|
:secondary_pools,
|
11
11
|
:hosts,
|
12
|
-
:nodes,
|
13
|
-
:members,
|
14
12
|
:seeds,
|
15
13
|
:pools,
|
16
14
|
:max_bson_size,
|
@@ -24,19 +22,21 @@ module Mongo
|
|
24
22
|
# time. The union of these lists will be used when attempting to connect,
|
25
23
|
# with the newly-discovered nodes being used first.
|
26
24
|
def initialize(client, seeds=[])
|
27
|
-
@client
|
28
|
-
@seeds
|
29
|
-
|
30
|
-
@pools
|
31
|
-
@primary
|
32
|
-
@primary_pool
|
33
|
-
@secondaries
|
34
|
-
@secondary_pools
|
35
|
-
@hosts
|
36
|
-
@members
|
37
|
-
@refresh_required
|
38
|
-
@max_bson_size
|
39
|
-
@max_message_size
|
25
|
+
@client = client
|
26
|
+
@seeds = seeds
|
27
|
+
|
28
|
+
@pools = Set.new
|
29
|
+
@primary = nil
|
30
|
+
@primary_pool = nil
|
31
|
+
@secondaries = Set.new
|
32
|
+
@secondary_pools = []
|
33
|
+
@hosts = Set.new
|
34
|
+
@members = Set.new
|
35
|
+
@refresh_required = false
|
36
|
+
@max_bson_size = DEFAULT_MAX_BSON_SIZE
|
37
|
+
@max_message_size = @max_bson_size * MESSAGE_SIZE_FACTOR
|
38
|
+
@connect_mutex = Mutex.new
|
39
|
+
thread_local[:locks][:connecting_manager] = false
|
40
40
|
end
|
41
41
|
|
42
42
|
def inspect
|
@@ -44,11 +44,19 @@ module Mongo
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def connect
|
47
|
-
@
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
@connect_mutex.synchronize do
|
48
|
+
begin
|
49
|
+
thread_local[:locks][:connecting_manager] = true
|
50
|
+
@refresh_required = false
|
51
|
+
disconnect_old_members
|
52
|
+
connect_to_members
|
53
|
+
initialize_pools(@members)
|
54
|
+
update_max_sizes
|
55
|
+
@seeds = discovered_seeds
|
56
|
+
ensure
|
57
|
+
thread_local[:locks][:connecting_manager] = false
|
58
|
+
end
|
59
|
+
end
|
52
60
|
end
|
53
61
|
|
54
62
|
def refresh!(additional_seeds)
|
@@ -61,6 +69,8 @@ module Mongo
|
|
61
69
|
# to our view. If any of these isn't the case,
|
62
70
|
# set @refresh_required to true, and return.
|
63
71
|
def check_connection_health
|
72
|
+
return if thread_local[:locks][:connecting_manager]
|
73
|
+
members = copy_members
|
64
74
|
begin
|
65
75
|
seed = get_valid_seed_node
|
66
76
|
rescue ConnectionFailure
|
@@ -74,14 +84,14 @@ module Mongo
|
|
74
84
|
return
|
75
85
|
end
|
76
86
|
|
77
|
-
if current_config['hosts'].length !=
|
87
|
+
if current_config['hosts'].length != members.length
|
78
88
|
@refresh_required = true
|
79
89
|
seed.close
|
80
90
|
return
|
81
91
|
end
|
82
92
|
|
83
93
|
current_config['hosts'].each do |host|
|
84
|
-
member =
|
94
|
+
member = members.detect do |m|
|
85
95
|
m.address == host
|
86
96
|
end
|
87
97
|
|
@@ -90,7 +100,7 @@ module Mongo
|
|
90
100
|
else
|
91
101
|
@refresh_required = true
|
92
102
|
seed.close
|
93
|
-
return
|
103
|
+
return
|
94
104
|
end
|
95
105
|
end
|
96
106
|
|
@@ -117,6 +127,8 @@ module Mongo
|
|
117
127
|
read_pool.host_port
|
118
128
|
end
|
119
129
|
|
130
|
+
private
|
131
|
+
|
120
132
|
def update_max_sizes
|
121
133
|
unless @members.size == 0
|
122
134
|
@max_bson_size = @members.map(&:max_bson_size).min
|
@@ -124,8 +136,6 @@ module Mongo
|
|
124
136
|
end
|
125
137
|
end
|
126
138
|
|
127
|
-
private
|
128
|
-
|
129
139
|
def validate_existing_member(current_config, member)
|
130
140
|
if current_config['ismaster'] && member.last_state != :primary
|
131
141
|
return false
|
@@ -152,13 +162,13 @@ module Mongo
|
|
152
162
|
existing.set_config
|
153
163
|
# If we are unhealthy after refreshing our config, drop from the set.
|
154
164
|
if !existing.healthy?
|
155
|
-
@members.delete
|
165
|
+
@members.delete(existing)
|
156
166
|
else
|
157
167
|
next
|
158
168
|
end
|
159
169
|
else
|
160
170
|
existing.close
|
161
|
-
@members.delete
|
171
|
+
@members.delete(existing)
|
162
172
|
end
|
163
173
|
end
|
164
174
|
|
@@ -242,11 +252,19 @@ module Mongo
|
|
242
252
|
"#{@seeds.map {|s| "#{s[0]}:#{s[1]}" }.join(', ')}"
|
243
253
|
end
|
244
254
|
|
245
|
-
private
|
246
|
-
|
247
255
|
def discovered_seeds
|
248
256
|
@members.map(&:host_port)
|
249
257
|
end
|
250
258
|
|
259
|
+
def copy_members
|
260
|
+
members = Set.new
|
261
|
+
@connect_mutex.synchronize do
|
262
|
+
@members.map do |m|
|
263
|
+
members << m.dup
|
264
|
+
end
|
265
|
+
end
|
266
|
+
members
|
267
|
+
end
|
268
|
+
|
251
269
|
end
|
252
270
|
end
|
@@ -9,11 +9,11 @@ module Mongo
|
|
9
9
|
]
|
10
10
|
|
11
11
|
MONGOS_MODES = {
|
12
|
-
:primary =>
|
13
|
-
:primary_preferred =>
|
14
|
-
:secondary =>
|
15
|
-
:secondary_preferred =>
|
16
|
-
:nearest =>
|
12
|
+
:primary => 'primary',
|
13
|
+
:primary_preferred => 'primaryPreferred',
|
14
|
+
:secondary => 'secondary',
|
15
|
+
:secondary_preferred => 'secondaryPreferred',
|
16
|
+
:nearest => 'nearest'
|
17
17
|
}
|
18
18
|
|
19
19
|
def self.mongos(mode, tag_sets)
|
@@ -77,7 +77,7 @@ module Mongo
|
|
77
77
|
when :secondary_preferred
|
78
78
|
select_secondary_pool(secondary_pools, read_pref) || primary_pool
|
79
79
|
when :nearest
|
80
|
-
|
80
|
+
select_near_pool(pools, read_pref)
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
@@ -12,30 +12,43 @@ module Mongo
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def connect
|
15
|
-
@
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
@connect_mutex.synchronize do
|
16
|
+
begin
|
17
|
+
thread_local[:locks][:connecting_manager] = true
|
18
|
+
@refresh_required = false
|
19
|
+
disconnect_old_members
|
20
|
+
connect_to_members
|
21
|
+
initialize_pools best(@members)
|
22
|
+
update_max_sizes
|
23
|
+
@seeds = discovered_seeds
|
24
|
+
ensure
|
25
|
+
thread_local[:locks][:connecting_manager] = false
|
26
|
+
end
|
27
|
+
end
|
20
28
|
end
|
21
29
|
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
30
|
+
# Checks that each node is healthy (via check_is_master) and that each
|
31
|
+
# node is in fact a mongos. If either criteria are not true, a refresh is
|
32
|
+
# set to be triggered and close() is called on the node.
|
33
|
+
#
|
34
|
+
# @return [Boolean] indicating if a refresh is required.
|
27
35
|
def check_connection_health
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
36
|
+
@refresh_required = false
|
37
|
+
@members.each do |member|
|
38
|
+
begin
|
39
|
+
config = @client.check_is_master([member.host, member.port])
|
40
|
+
unless config && config.has_key?('msg')
|
41
|
+
@refresh_required = true
|
42
|
+
member.close
|
43
|
+
end
|
44
|
+
rescue OperationTimeout
|
34
45
|
@refresh_required = true
|
46
|
+
member.close
|
35
47
|
end
|
36
|
-
|
37
|
-
@refresh_required = true
|
48
|
+
break if @refresh_required
|
38
49
|
end
|
50
|
+
@refresh_required
|
39
51
|
end
|
52
|
+
|
40
53
|
end
|
41
54
|
end
|
@@ -8,11 +8,8 @@ class ReplicaSetAuthenticationTest < Test::Unit::TestCase
|
|
8
8
|
def setup
|
9
9
|
ensure_cluster(:rs)
|
10
10
|
@client = MongoReplicaSetClient.new(@rs.repl_set_seeds, :name => @rs.repl_set_name, :connect_timeout => 60)
|
11
|
-
@db
|
12
|
-
|
13
|
-
|
14
|
-
def teardown
|
15
|
-
@db['system.users'].remove
|
11
|
+
@db = @client[MONGO_TEST_DB]
|
12
|
+
init_auth
|
16
13
|
end
|
17
14
|
|
18
15
|
def test_authenticate_with_connection_uri
|
@@ -11,6 +11,21 @@ class ClientTest < Test::Unit::TestCase
|
|
11
11
|
@client.close if @client
|
12
12
|
end
|
13
13
|
|
14
|
+
def test_reconnection
|
15
|
+
@client = MongoReplicaSetClient.new @rs.repl_set_seeds
|
16
|
+
assert @client.connected?
|
17
|
+
|
18
|
+
manager = @client.local_manager
|
19
|
+
|
20
|
+
@client.close
|
21
|
+
assert !@client.connected?
|
22
|
+
assert !@client.local_manager
|
23
|
+
|
24
|
+
@client.connect
|
25
|
+
assert @client.connected?
|
26
|
+
assert_equal @client.local_manager, manager
|
27
|
+
end
|
28
|
+
|
14
29
|
# TODO: test connect timeout.
|
15
30
|
|
16
31
|
def test_connect_with_deprecated_multi
|
@@ -7,6 +7,12 @@ class MaxValuesTest < Test::Unit::TestCase
|
|
7
7
|
def setup
|
8
8
|
ensure_cluster(:rs)
|
9
9
|
@client = MongoReplicaSetClient.new(@rs.repl_set_seeds, :name => @rs.repl_set_name)
|
10
|
+
@db = new_mock_db
|
11
|
+
@client.stubs(:[]).returns(@db)
|
12
|
+
@ismaster = {
|
13
|
+
'hosts' => @client.local_manager.hosts.to_a,
|
14
|
+
'arbiters' => @client.local_manager.arbiters
|
15
|
+
}
|
10
16
|
end
|
11
17
|
|
12
18
|
def test_initial_max_sizes
|
@@ -15,43 +21,52 @@ class MaxValuesTest < Test::Unit::TestCase
|
|
15
21
|
end
|
16
22
|
|
17
23
|
def test_updated_max_sizes_after_node_config_change
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
24
|
+
@db.stubs(:command).returns(
|
25
|
+
@ismaster.merge({'ismaster' => true}),
|
26
|
+
@ismaster.merge({'secondary' => true, 'maxMessageSizeBytes' => 1024 * MESSAGE_SIZE_FACTOR}),
|
27
|
+
@ismaster.merge({'secondary' => true, 'maxBsonObjectSize' => 1024})
|
28
|
+
)
|
29
|
+
@client.local_manager.stubs(:refresh_required?).returns(true)
|
30
|
+
@client.refresh
|
25
31
|
|
26
32
|
assert_equal 1024, @client.max_bson_size
|
27
33
|
assert_equal 1024 * MESSAGE_SIZE_FACTOR, @client.max_message_size
|
28
34
|
end
|
29
35
|
|
30
36
|
def test_neither_max_sizes_in_config
|
31
|
-
@
|
32
|
-
|
33
|
-
|
34
|
-
|
37
|
+
@db.stubs(:command).returns(
|
38
|
+
@ismaster.merge({'ismaster' => true}),
|
39
|
+
@ismaster.merge({'secondary' => true}),
|
40
|
+
@ismaster.merge({'secondary' => true})
|
41
|
+
)
|
42
|
+
@client.local_manager.stubs(:refresh_required?).returns(true)
|
43
|
+
@client.refresh
|
35
44
|
|
36
45
|
assert_equal DEFAULT_MAX_BSON_SIZE, @client.max_bson_size
|
37
46
|
assert_equal DEFAULT_MAX_BSON_SIZE * MESSAGE_SIZE_FACTOR, @client.max_message_size
|
38
47
|
end
|
39
48
|
|
40
49
|
def test_only_bson_size_in_config
|
41
|
-
@
|
42
|
-
|
43
|
-
|
44
|
-
|
50
|
+
@db.stubs(:command).returns(
|
51
|
+
@ismaster.merge({'ismaster' => true}),
|
52
|
+
@ismaster.merge({'secondary' => true}),
|
53
|
+
@ismaster.merge({'secondary' => true, 'maxBsonObjectSize' => 1024})
|
54
|
+
)
|
55
|
+
@client.local_manager.stubs(:refresh_required?).returns(true)
|
56
|
+
@client.refresh
|
57
|
+
|
45
58
|
assert_equal 1024, @client.max_bson_size
|
46
59
|
assert_equal 1024 * MESSAGE_SIZE_FACTOR, @client.max_message_size
|
47
60
|
end
|
48
61
|
|
49
62
|
def test_both_sizes_in_config
|
50
|
-
@
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
63
|
+
@db.stubs(:command).returns(
|
64
|
+
@ismaster.merge({'ismaster' => true, 'maxMessageSizeBytes' => 1024 * 2 * MESSAGE_SIZE_FACTOR, 'maxBsonObjectSize' => 1024}),
|
65
|
+
@ismaster.merge({'secondary' => true, 'maxMessageSizeBytes' => 1024 * 2 * MESSAGE_SIZE_FACTOR, 'maxBsonObjectSize' => 1024}),
|
66
|
+
@ismaster.merge({'secondary' => true, 'maxMessageSizeBytes' => 1024 * 2 * MESSAGE_SIZE_FACTOR, 'maxBsonObjectSize' => 1024})
|
67
|
+
)
|
68
|
+
@client.local_manager.stubs(:refresh_required?).returns(true)
|
69
|
+
@client.refresh
|
55
70
|
|
56
71
|
assert_equal 1024, @client.max_bson_size
|
57
72
|
assert_equal 1024 * 2 * MESSAGE_SIZE_FACTOR, @client.max_message_size
|
@@ -78,6 +78,37 @@ class ReplicaSetRefreshTest < Test::Unit::TestCase
|
|
78
78
|
"No secondaries have been added."
|
79
79
|
assert_equal num_secondaries, client.secondary_pools.size
|
80
80
|
end
|
81
|
+
|
82
|
+
def test_concurrent_refreshes
|
83
|
+
factor = 5
|
84
|
+
nthreads = factor * 10
|
85
|
+
threads = []
|
86
|
+
client = MongoReplicaSetClient.new(@rs.repl_set_seeds, :refresh_mode => :sync, :refresh_interval => 1)
|
87
|
+
|
88
|
+
nthreads.times do |i|
|
89
|
+
threads << Thread.new do
|
90
|
+
# force a connection failure every couple of threads that causes a refresh
|
91
|
+
if i % factor == 0
|
92
|
+
cursor = client['foo']['bar'].find
|
93
|
+
cursor.stubs(:checkout_socket_from_connection).raises(ConnectionFailure)
|
94
|
+
begin
|
95
|
+
cursor.next
|
96
|
+
rescue => ex
|
97
|
+
raise ex unless ex.class == ConnectionFailure
|
98
|
+
next
|
99
|
+
end
|
100
|
+
else
|
101
|
+
# synchronous refreshes will happen every couple of find_ones
|
102
|
+
cursor = client['foo']['bar'].find_one
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
threads.each do |t|
|
108
|
+
t.join
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
81
112
|
=begin
|
82
113
|
def test_automated_refresh_with_removed_node
|
83
114
|
client = MongoReplicaSetClient.new(@rs.repl_set_seeds,
|
@@ -36,7 +36,7 @@ class BasicTest < Test::Unit::TestCase
|
|
36
36
|
@client = MongoClient.new(host, port, {:read => :secondary, :tag_sets => tags})
|
37
37
|
assert @client.connected?
|
38
38
|
cursor = Cursor.new(@client[MONGO_TEST_DB]['whatever'], {})
|
39
|
-
assert_equal cursor.construct_query_spec['$readPreference'], {:mode =>
|
39
|
+
assert_equal cursor.construct_query_spec['$readPreference'], {:mode => 'secondary', :tags => tags}
|
40
40
|
end
|
41
41
|
|
42
42
|
def test_find_one_with_read_secondary
|
@@ -68,7 +68,7 @@ class BasicTest < Test::Unit::TestCase
|
|
68
68
|
@client = MongoShardedClient.new(@seeds, {:read => :secondary, :tag_sets => tags})
|
69
69
|
assert @client.connected?
|
70
70
|
cursor = Cursor.new(@client[MONGO_TEST_DB]['whatever'], {})
|
71
|
-
assert_equal cursor.construct_query_spec['$readPreference'], {:mode =>
|
71
|
+
assert_equal cursor.construct_query_spec['$readPreference'], {:mode => 'secondary', :tags => tags}
|
72
72
|
end
|
73
73
|
|
74
74
|
def test_hard_refresh
|
@@ -1,4 +1,21 @@
|
|
1
1
|
module AuthenticationTests
|
2
|
+
|
3
|
+
def init_auth
|
4
|
+
# enable authentication by creating and logging in as admin user
|
5
|
+
@admin = @client['admin']
|
6
|
+
@admin.add_user('admin', 'password')
|
7
|
+
@admin.authenticate('admin', 'password')
|
8
|
+
end
|
9
|
+
|
10
|
+
def teardown
|
11
|
+
@admin.logout
|
12
|
+
@admin.authenticate('admin','password')
|
13
|
+
@admin['system.users'].remove
|
14
|
+
@db['system.users'].remove
|
15
|
+
@db['test'].remove
|
16
|
+
@admin.logout
|
17
|
+
end
|
18
|
+
|
2
19
|
def test_add_user
|
3
20
|
@db.add_user('bob','user')
|
4
21
|
assert @db['system.users'].find_one({:user => 'bob'})
|
data/test/test_helper.rb
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
-
|
1
|
+
begin
|
2
|
+
require 'pry-rescue'
|
3
|
+
require 'pry-nav'
|
4
|
+
rescue LoadError
|
5
|
+
# failed to load, skipping pry
|
6
|
+
end
|
7
|
+
|
2
8
|
# SimpleCov must load before our code - A coverage report summary line will print after each test suite
|
3
9
|
if RUBY_VERSION >= '1.9.0' && RUBY_ENGINE == 'ruby'
|
4
10
|
if ENV.key?('COVERAGE')
|
data/test/tools/mongo_config.rb
CHANGED
@@ -35,7 +35,7 @@ module Mongo
|
|
35
35
|
MONGODS_OPT_KEYS = [:mongods]
|
36
36
|
CLUSTER_OPT_KEYS = SHARDING_OPT_KEYS + REPLICA_OPT_KEYS + MONGODS_OPT_KEYS
|
37
37
|
|
38
|
-
FLAGS = [:noprealloc, :smallfiles, :logappend, :configsvr, :shardsvr, :quiet, :fastsync]
|
38
|
+
FLAGS = [:noprealloc, :smallfiles, :logappend, :configsvr, :shardsvr, :quiet, :fastsync, :auth]
|
39
39
|
|
40
40
|
DEFAULT_VERIFIES = 60
|
41
41
|
BASE_PORT = 3000
|
@@ -108,18 +108,24 @@ module Mongo
|
|
108
108
|
:smallfiles => smallfiles,
|
109
109
|
:noprealloc => noprealloc,
|
110
110
|
:quiet => quiet,
|
111
|
-
:fastsync => fast_sync
|
111
|
+
:fastsync => fast_sync,
|
112
|
+
:auth => auth)
|
112
113
|
end
|
113
114
|
|
114
|
-
def self.make_replica(opts,
|
115
|
+
def self.make_replica(opts, id)
|
115
116
|
params = make_mongod('replicas', opts)
|
116
117
|
|
117
118
|
replSet = opts[:replSet] || 'ruby-driver-test'
|
118
|
-
|
119
|
+
oplogSize = opts[:oplog_size] || 5
|
120
|
+
keyFile = opts[:key_file] || '/test/tools/keyfile.txt'
|
119
121
|
|
120
|
-
|
122
|
+
keyFile = Dir.pwd << keyFile
|
123
|
+
system "chmod 600 #{keyFile}"
|
124
|
+
|
125
|
+
params.merge(:_id => id,
|
121
126
|
:replSet => replSet,
|
122
|
-
:oplogSize =>
|
127
|
+
:oplogSize => oplogSize,
|
128
|
+
:keyFile => keyFile)
|
123
129
|
end
|
124
130
|
|
125
131
|
def self.make_config(opts)
|
@@ -187,7 +193,7 @@ module Mongo
|
|
187
193
|
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
188
194
|
@pid = Process.spawn(*@cmd)
|
189
195
|
else
|
190
|
-
cmd_and_opts = [@cmd, {:out =>
|
196
|
+
cmd_and_opts = [@cmd, {:out => '/dev/null'}].flatten
|
191
197
|
@pid = Process.spawn(*cmd_and_opts)
|
192
198
|
end
|
193
199
|
verify(verifies) if verifies > 0
|
data/test/unit/cursor_test.rb
CHANGED
@@ -124,7 +124,7 @@ class CursorTest < Test::Unit::TestCase
|
|
124
124
|
|
125
125
|
spec = cursor.construct_query_spec
|
126
126
|
assert spec.has_key?('$readPreference')
|
127
|
-
assert_equal
|
127
|
+
assert_equal 'secondary', spec['$readPreference'][:mode]
|
128
128
|
assert !spec['$readPreference'].has_key?(:tags)
|
129
129
|
|
130
130
|
# secondary preferred with tags
|
@@ -132,7 +132,7 @@ class CursorTest < Test::Unit::TestCase
|
|
132
132
|
|
133
133
|
spec = cursor.construct_query_spec
|
134
134
|
assert spec.has_key?('$readPreference')
|
135
|
-
assert_equal
|
135
|
+
assert_equal 'secondaryPreferred', spec['$readPreference'][:mode]
|
136
136
|
assert_equal @tag_sets, spec['$readPreference'][:tags]
|
137
137
|
|
138
138
|
# primary preferred
|
@@ -140,7 +140,7 @@ class CursorTest < Test::Unit::TestCase
|
|
140
140
|
|
141
141
|
spec = cursor.construct_query_spec
|
142
142
|
assert spec.has_key?('$readPreference')
|
143
|
-
assert_equal
|
143
|
+
assert_equal 'primaryPreferred', spec['$readPreference'][:mode]
|
144
144
|
assert !spec['$readPreference'].has_key?(:tags)
|
145
145
|
|
146
146
|
# primary preferred with tags
|
@@ -148,7 +148,7 @@ class CursorTest < Test::Unit::TestCase
|
|
148
148
|
|
149
149
|
spec = cursor.construct_query_spec
|
150
150
|
assert spec.has_key?('$readPreference')
|
151
|
-
assert_equal
|
151
|
+
assert_equal 'primaryPreferred', spec['$readPreference'][:mode]
|
152
152
|
assert_equal @tag_sets, spec['$readPreference'][:tags]
|
153
153
|
|
154
154
|
# nearest
|
@@ -156,7 +156,7 @@ class CursorTest < Test::Unit::TestCase
|
|
156
156
|
|
157
157
|
spec = cursor.construct_query_spec
|
158
158
|
assert spec.has_key?('$readPreference')
|
159
|
-
assert_equal
|
159
|
+
assert_equal 'nearest', spec['$readPreference'][:mode]
|
160
160
|
assert !spec['$readPreference'].has_key?(:tags)
|
161
161
|
|
162
162
|
# nearest with tags
|
@@ -164,7 +164,7 @@ class CursorTest < Test::Unit::TestCase
|
|
164
164
|
|
165
165
|
spec = cursor.construct_query_spec
|
166
166
|
assert spec.has_key?('$readPreference')
|
167
|
-
assert_equal
|
167
|
+
assert_equal 'nearest', spec['$readPreference'][:mode]
|
168
168
|
assert_equal @tag_sets, spec['$readPreference'][:tags]
|
169
169
|
end
|
170
170
|
|
@@ -9,24 +9,24 @@ class MongoShardedClientTest < Test::Unit::TestCase
|
|
9
9
|
|
10
10
|
def test_initialize_with_single_mongos_uri
|
11
11
|
ENV["MONGODB_URI"] = "mongodb://localhost:27017"
|
12
|
-
client = MongoShardedClient.new
|
12
|
+
client = MongoShardedClient.new(:connect => false)
|
13
13
|
assert_equal [[ "localhost", 27017 ]], client.seeds
|
14
14
|
end
|
15
15
|
|
16
16
|
def test_initialize_with_multiple_mongos_uris
|
17
17
|
ENV["MONGODB_URI"] = "mongodb://localhost:27017,localhost:27018"
|
18
|
-
client = MongoShardedClient.new
|
18
|
+
client = MongoShardedClient.new(:connect => false)
|
19
19
|
assert_equal [[ "localhost", 27017 ], [ "localhost", 27018 ]], client.seeds
|
20
20
|
end
|
21
21
|
|
22
22
|
def test_from_uri_with_string
|
23
|
-
client = MongoShardedClient.from_uri("mongodb://localhost:27017,localhost:27018")
|
23
|
+
client = MongoShardedClient.from_uri("mongodb://localhost:27017,localhost:27018", :connect => false)
|
24
24
|
assert_equal [[ "localhost", 27017 ], [ "localhost", 27018 ]], client.seeds
|
25
25
|
end
|
26
26
|
|
27
27
|
def test_from_uri_with_env_variable
|
28
28
|
ENV["MONGODB_URI"] = "mongodb://localhost:27017,localhost:27018"
|
29
|
-
client = MongoShardedClient.from_uri
|
29
|
+
client = MongoShardedClient.from_uri(nil, :connect => false)
|
30
30
|
assert_equal [[ "localhost", 27017 ], [ "localhost", 27018 ]], client.seeds
|
31
31
|
end
|
32
32
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ReadPrefTest < Test::Unit::TestCase
|
4
|
+
include ReadPreference
|
5
|
+
|
6
|
+
def setup
|
7
|
+
mock_pool = mock()
|
8
|
+
mock_pool.stubs(:ping_time).returns(Pool::MAX_PING_TIME)
|
9
|
+
|
10
|
+
stubs(:primary_pool).returns(mock_pool)
|
11
|
+
stubs(:secondary_pools).returns([mock_pool])
|
12
|
+
stubs(:pools).returns([mock_pool])
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_select_pool
|
16
|
+
ReadPreference::READ_PREFERENCES.map do |rp|
|
17
|
+
assert select_pool({:mode => rp, :tags => [], :latency => 15})
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -59,30 +59,5 @@ class ShardingPoolManagerTest < Test::Unit::TestCase
|
|
59
59
|
assert_equal 500, manager.max_bson_size
|
60
60
|
assert_equal 700 , manager.max_message_size
|
61
61
|
end
|
62
|
-
|
63
|
-
should "maintain seed format when checking connection health" do
|
64
|
-
|
65
|
-
@db.stubs(:command).returns(
|
66
|
-
# First call to get a socket.
|
67
|
-
@ismaster.merge({'ismaster' => true}),
|
68
|
-
|
69
|
-
# Subsequent calls to configure pools.
|
70
|
-
@ismaster.merge({'ismaster' => true}),
|
71
|
-
@ismaster.merge({'secondary' => true, 'maxMessageSizeBytes' => 700}),
|
72
|
-
@ismaster.merge({'secondary' => true, 'maxBsonObjectSize' => 500}),
|
73
|
-
@ismaster.merge({'arbiterOnly' => true})
|
74
|
-
)
|
75
|
-
|
76
|
-
config_db = new_mock_db
|
77
|
-
mongos_coll = mock('collection')
|
78
|
-
mongos_coll.stubs(:find).returns(@hosts.map{|h| {'_id' => h}})
|
79
|
-
config_db.stubs(:[]).with('mongos').returns(mongos_coll)
|
80
|
-
@client.stubs(:[]).with('config').returns(config_db)
|
81
|
-
|
82
|
-
manager = Mongo::ShardingPoolManager.new(@client, @hosts)
|
83
|
-
manager.check_connection_health
|
84
|
-
|
85
|
-
assert manager.seeds.all? {|s| s.is_a?(Array) && s[0].is_a?(String) && s[1].is_a?(Integer)}
|
86
|
-
end
|
87
62
|
end
|
88
63
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.8.
|
4
|
+
version: 1.8.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tyler Brock
|
@@ -33,7 +33,7 @@ cert_chain:
|
|
33
33
|
8v7zLF2XliYbfurYIwkcXs8yPn8ggApBIy9bX6VJxRs/l2+UvqzaHIFaFy/F8/GP
|
34
34
|
RNTuXsVG5NDACo7Q
|
35
35
|
-----END CERTIFICATE-----
|
36
|
-
date: 2013-
|
36
|
+
date: 2013-05-16 00:00:00.000000000 Z
|
37
37
|
dependencies:
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
39
|
name: bson
|
@@ -41,14 +41,14 @@ dependencies:
|
|
41
41
|
requirements:
|
42
42
|
- - ~>
|
43
43
|
- !ruby/object:Gem::Version
|
44
|
-
version: 1.8.
|
44
|
+
version: 1.8.6
|
45
45
|
type: :runtime
|
46
46
|
prerelease: false
|
47
47
|
version_requirements: !ruby/object:Gem::Requirement
|
48
48
|
requirements:
|
49
49
|
- - ~>
|
50
50
|
- !ruby/object:Gem::Version
|
51
|
-
version: 1.8.
|
51
|
+
version: 1.8.6
|
52
52
|
description: A Ruby driver for MongoDB. For more information about Mongo, see http://www.mongodb.org.
|
53
53
|
email: mongodb-dev@googlegroups.com
|
54
54
|
executables:
|
@@ -143,6 +143,7 @@ files:
|
|
143
143
|
- test/unit/node_test.rb
|
144
144
|
- test/unit/pool_manager_test.rb
|
145
145
|
- test/unit/pool_test.rb
|
146
|
+
- test/unit/read_pref_test.rb
|
146
147
|
- test/unit/read_test.rb
|
147
148
|
- test/unit/safe_test.rb
|
148
149
|
- test/unit/sharding_pool_manager_test.rb
|
@@ -223,6 +224,7 @@ test_files:
|
|
223
224
|
- test/unit/node_test.rb
|
224
225
|
- test/unit/pool_manager_test.rb
|
225
226
|
- test/unit/pool_test.rb
|
227
|
+
- test/unit/read_pref_test.rb
|
226
228
|
- test/unit/read_test.rb
|
227
229
|
- test/unit/safe_test.rb
|
228
230
|
- test/unit/sharding_pool_manager_test.rb
|
metadata.gz.sig
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
;cJ�g����H�#~������Ç���������J�.�۞(T�.7{��<�!����:ݬ���T;�S���RJ��y��A��x��D8ʒ�=k���nL7�пk�Y�[>�����1*+>)5� ���i�qJ3��^*q&��f��
|