mongo 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ require File.join((File.expand_path(File.dirname(__FILE__))), 'repl_set_manager')
2
+
3
+ class AuthReplSetManager < ReplSetManager
4
+ def initialize(opts={})
5
+ super(opts)
6
+
7
+ @key_path = opts[:key_path] || File.join(File.expand_path(File.dirname(__FILE__)), "keyfile.txt")
8
+ system("chmod 600 #{@key_path}")
9
+ end
10
+
11
+ def start_cmd(n)
12
+ super + " --keyFile #{@key_path}"
13
+ end
14
+ end
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'mongo'
3
+ require 'sharding_manager'
4
+
5
+ class MongoLoader
6
+
7
+ def initialize
8
+ @mongo = Mongo::Connection.new("localhost", 50000)
9
+ @data = BSON::Binary.new(File.open("tools.gz").read)
10
+ @count = 0
11
+ @manager = ShardingManager.new(:config_count => 3)
12
+ @manager.start_cluster
13
+ end
14
+
15
+ def kill
16
+ @manager.kill_random
17
+ end
18
+
19
+ def restart
20
+ @manager.restart_killed_nodes
21
+ end
22
+
23
+ def run
24
+ Thread.new do
25
+ ("a".."z").each do |p|
26
+ seed(p)
27
+ end
28
+ end
29
+ end
30
+
31
+ def seed(prefix)
32
+ @queue = []
33
+ 1000.times do |n|
34
+ id = BSON::OrderedHash.new
35
+ id[:p] = prefix
36
+ id[:c] = n
37
+ @queue << {:tid => id, :data => @data}
38
+ end
39
+
40
+ while @queue.length > 0 do
41
+ begin
42
+ doc = @queue.pop
43
+ @mongo['app']['photos'].insert(doc, :safe => {:w => 3})
44
+ @count += 1
45
+ p @count
46
+ rescue StandardError => e
47
+ p e
48
+ p @count
49
+ @queue.push(doc)
50
+ @count -= 1
51
+ sleep(10)
52
+ retry
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ @m = MongoLoader.new
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/ruby
2
2
 
3
+ require 'thread'
4
+
3
5
  STDOUT.sync = true
4
6
 
5
7
  unless defined? Mongo
@@ -17,6 +19,7 @@ class ReplSetManager
17
19
  @host = opts[:host] || 'localhost'
18
20
  @retries = opts[:retries] || 60
19
21
  @config = {"_id" => @name, "members" => []}
22
+ @durable = opts.fetch(:durable, false)
20
23
  @path = File.join(File.expand_path(File.dirname(__FILE__)), "data")
21
24
 
22
25
  @arbiter_count = opts[:arbiter_count] || 2
@@ -61,6 +64,13 @@ class ReplSetManager
61
64
  ensure_up
62
65
  end
63
66
 
67
+ def cleanup_set
68
+ system("killall mongod")
69
+ @count.times do |n|
70
+ system("rm -rf #{@mongods[n]['db_path']}")
71
+ end
72
+ end
73
+
64
74
  def init_node(n)
65
75
  @mongods[n] ||= {}
66
76
  port = @start_port + n
@@ -71,9 +81,7 @@ class ReplSetManager
71
81
  system("rm -rf #{@mongods[n]['db_path']}")
72
82
  system("mkdir -p #{@mongods[n]['db_path']}")
73
83
 
74
- @mongods[n]['start'] = "mongod --replSet #{@name} --logpath '#{@mongods[n]['log_path']}' " +
75
- " --dbpath #{@mongods[n]['db_path']} --port #{@mongods[n]['port']} --fork"
76
-
84
+ @mongods[n]['start'] = start_cmd(n)
77
85
  start(n)
78
86
 
79
87
  member = {'_id' => n, 'host' => "#{@host}:#{@mongods[n]['port']}"}
@@ -88,17 +96,24 @@ class ReplSetManager
88
96
  @config['members'] << member
89
97
  end
90
98
 
91
- def kill(node)
99
+ def start_cmd(n)
100
+ @mongods[n]['start'] = "mongod --replSet #{@name} --logpath '#{@mongods[n]['log_path']}' " +
101
+ " --dbpath #{@mongods[n]['db_path']} --port #{@mongods[n]['port']} --fork"
102
+ @mongods[n]['start'] += " --dur" if @durable
103
+ @mongods[n]['start']
104
+ end
105
+
106
+ def kill(node, signal=2)
92
107
  pid = @mongods[node]['pid']
93
108
  puts "** Killing node with pid #{pid} at port #{@mongods[node]['port']}"
94
- system("kill -2 #{@mongods[node]['pid']}")
109
+ system("kill -#{signal} #{@mongods[node]['pid']}")
95
110
  @mongods[node]['up'] = false
96
111
  sleep(1)
97
112
  end
98
113
 
99
- def kill_primary
114
+ def kill_primary(signal=2)
100
115
  node = get_node_with_state(1)
101
- kill(node)
116
+ kill(node, signal)
102
117
  return node
103
118
  end
104
119
 
@@ -174,6 +189,16 @@ class ReplSetManager
174
189
  get_all_host_pairs_with_state(7)
175
190
  end
176
191
 
192
+ # String used for adding a shard via mongos
193
+ # using the addshard command.
194
+ def shard_string
195
+ str = "#{@name}/"
196
+ str << @mongods.map do |k, mongod|
197
+ "#{@host}:#{mongod['port']}"
198
+ end.join(',')
199
+ str
200
+ end
201
+
177
202
  private
178
203
 
179
204
  def initiate
@@ -229,13 +254,13 @@ class ReplSetManager
229
254
  while count < @retries do
230
255
  begin
231
256
  return yield
232
- rescue Mongo::OperationFailure, Mongo::ConnectionFailure
257
+ rescue Mongo::OperationFailure, Mongo::ConnectionFailure => ex
233
258
  sleep(1)
234
259
  count += 1
235
260
  end
236
261
  end
237
262
 
238
- raise exception
263
+ raise ex
239
264
  end
240
265
 
241
266
  end
@@ -0,0 +1,202 @@
1
+ require 'repl_set_manager'
2
+ require 'thread'
3
+
4
+ class ShardingManager
5
+
6
+ attr_accessor :shards
7
+
8
+ def initialize(opts={})
9
+ @durable = opts.fetch(:durable, true)
10
+ @host = "localhost"
11
+
12
+ @mongos_port = opts[:mongos_port] || 50000
13
+ @config_port = opts[:config_port] || 40000
14
+ @shard_start_port = opts[:start_shard_port] || 30000
15
+ @path = File.join(File.expand_path(File.dirname(__FILE__)), "data")
16
+ system("rm -rf #{@path}")
17
+
18
+ @shard_count = 2
19
+ @mongos_count = 1
20
+ @config_count = opts.fetch(:config_count, 1)
21
+ if ![1, 3].include?(@config_count)
22
+ raise ArgumentError, "Must specify 1 or 3 config servers."
23
+ end
24
+
25
+ @config_servers = {}
26
+ @mongos_servers = {}
27
+ @shards = []
28
+ @ports = []
29
+ end
30
+
31
+ def kill_random
32
+ shard_to_kill = rand(@shard_count)
33
+ @shards[shard_to_kill].kill_primary
34
+ end
35
+
36
+ def restart_killed
37
+ threads = []
38
+ @shards.each do |k, shard|
39
+ threads << Thread.new do
40
+ shard.restart_killed_nodes
41
+ end
42
+ end
43
+ end
44
+
45
+ def start_cluster
46
+ start_sharding_components
47
+ start_mongos_servers
48
+ configure_cluster
49
+ end
50
+
51
+ def configure_cluster
52
+ add_shards
53
+ enable_sharding
54
+ shard_collection
55
+ end
56
+
57
+ def enable_sharding
58
+ mongos['admin'].command({:enablesharding => "app"})
59
+ end
60
+
61
+ def shard_collection
62
+ cmd = BSON::OrderedHash.new
63
+ cmd[:shardcollection] = "app.photos"
64
+ cmd[:key] = {:tid => 1}
65
+ p mongos['admin'].command(cmd)
66
+ end
67
+
68
+ def add_shards
69
+ @shards.each do |shard|
70
+ cmd = {:addshard => shard.shard_string}
71
+ p cmd
72
+ p mongos['admin'].command(cmd)
73
+ end
74
+ p mongos['admin'].command({:listshards => 1})
75
+ end
76
+
77
+ def mongos
78
+ attempt do
79
+ @mongos ||= Mongo::Connection.new(@host, @mongos_servers[0]['port'])
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def start_sharding_components
86
+ system("killall mongos")
87
+
88
+ threads = []
89
+ threads << Thread.new do
90
+ start_shards
91
+ end
92
+
93
+ threads << Thread.new do
94
+ start_config_servers
95
+ end
96
+ threads.each {|t| t.join}
97
+ puts "\nShards and config servers up!"
98
+ end
99
+
100
+ def start_shards
101
+ threads = []
102
+ @shard_count.times do |n|
103
+ threads << Thread.new do
104
+ port = @shard_start_port + n * 100
105
+ shard = ReplSetManager.new(:arbiter_count => 0, :secondary_count => 2,
106
+ :passive_count => 0, :start_port => port, :durable => @durable,
107
+ :name => "shard-#{n}")
108
+ shard.start_set
109
+ shard.ensure_up
110
+ @shards << shard
111
+ end
112
+ end
113
+ threads.each {|t| t.join}
114
+ end
115
+
116
+ def start_config_servers
117
+ @config_count.times do |n|
118
+ @config_servers[n] ||= {}
119
+ port = @config_port + n
120
+ @ports << port
121
+ @config_servers[n]['port'] = port
122
+ @config_servers[n]['db_path'] = get_path("config-#{port}")
123
+ @config_servers[n]['log_path'] = get_path("log-config-#{port}")
124
+ system("rm -rf #{@config_servers[n]['db_path']}")
125
+ system("mkdir -p #{@config_servers[n]['db_path']}")
126
+
127
+ @config_servers[n]['start'] = start_config_cmd(n)
128
+
129
+ start(@config_servers, n)
130
+ end
131
+ end
132
+
133
+ def start_mongos_servers
134
+ @mongos_count.times do |n|
135
+ @mongos_servers[n] ||= {}
136
+ port = @mongos_port + n
137
+ @ports << port
138
+ @mongos_servers[n]['port'] = port
139
+ @mongos_servers[n]['db_path'] = get_path("mongos-#{port}")
140
+ @mongos_servers[n]['pidfile_path'] = File.join(@mongos_servers[n]['db_path'], "mongod.lock")
141
+ @mongos_servers[n]['log_path'] = get_path("log-mongos-#{port}")
142
+ system("rm -rf #{@mongos_servers[n]['db_path']}")
143
+ system("mkdir -p #{@mongos_servers[n]['db_path']}")
144
+
145
+ @mongos_servers[n]['start'] = start_mongos_cmd(n)
146
+
147
+ start(@mongos_servers, n)
148
+ end
149
+ end
150
+
151
+ def start_config_cmd(n)
152
+ cmd = "mongod --configsvr --logpath '#{@config_servers[n]['log_path']}' " +
153
+ " --dbpath #{@config_servers[n]['db_path']} --port #{@config_servers[n]['port']} --fork"
154
+ cmd += " --dur" if @durable
155
+ cmd
156
+ end
157
+
158
+ def start_mongos_cmd(n)
159
+ "mongos --configdb #{config_db_string} --logpath '#{@mongos_servers[n]['log_path']}' " +
160
+ "--pidfilepath #{@mongos_servers[n]['pidfile_path']} --port #{@mongos_servers[n]['port']} --fork"
161
+ end
162
+
163
+ def config_db_string
164
+ @config_servers.map do |k, v|
165
+ "#{@host}:#{v['port']}"
166
+ end.join(',')
167
+ end
168
+
169
+ def start(set, node)
170
+ system(set[node]['start'])
171
+ set[node]['up'] = true
172
+ sleep(0.5)
173
+ set[node]['pid'] = File.open(File.join(set[node]['db_path'], 'mongod.lock')).read.strip
174
+ end
175
+ alias :restart :start
176
+
177
+ private
178
+
179
+ def cleanup_config
180
+ end
181
+
182
+ def get_path(name)
183
+ File.join(@path, name)
184
+ end
185
+
186
+ # TODO: put this into a shared module
187
+ def attempt
188
+ raise "No block given!" unless block_given?
189
+ count = 0
190
+
191
+ while count < 50 do
192
+ begin
193
+ return yield
194
+ rescue Mongo::OperationFailure, Mongo::ConnectionFailure
195
+ sleep(1)
196
+ count += 1
197
+ end
198
+ end
199
+
200
+ raise exception
201
+ end
202
+ end
data/test/tools/test.rb CHANGED
@@ -1,13 +1,4 @@
1
- require 'repl_set_manager'
1
+ require 'sharding_manager'
2
2
 
3
- m = ReplSetManager.new
4
- m.start_set
5
-
6
- node = m.kill_secondary
7
- m.ensure_up
8
-
9
- puts "Pausing..."
10
- gets
11
-
12
- m.start(node)
13
- m.ensure_up
3
+ m = ShardingManager.new(:config_count => 3)
4
+ m.start_cluster
@@ -5,6 +5,7 @@ class CollectionTest < Test::Unit::TestCase
5
5
  context "Basic operations: " do
6
6
  setup do
7
7
  @logger = mock()
8
+ @logger.expects(:debug)
8
9
  end
9
10
 
10
11
  should "send update message" do
@@ -14,7 +15,7 @@ class CollectionTest < Test::Unit::TestCase
14
15
  @conn.expects(:send_message).with do |op, msg, log|
15
16
  op == 2001
16
17
  end
17
- @logger.stubs(:debug)
18
+ @conn.stubs(:log_operation)
18
19
  @coll.update({}, {:title => 'Moby Dick'})
19
20
  end
20
21
 
@@ -25,8 +26,8 @@ class CollectionTest < Test::Unit::TestCase
25
26
  @conn.expects(:send_message).with do |op, msg, log|
26
27
  op == 2002
27
28
  end
28
- @logger.expects(:debug).with do |msg|
29
- msg.include?("Moby")
29
+ @conn.expects(:log_operation).with do |name, payload|
30
+ (name == :insert) && payload[:documents][0][:title].include?('Moby')
30
31
  end
31
32
  @coll.insert({:title => 'Moby Dick'})
32
33
  end
@@ -38,8 +39,8 @@ class CollectionTest < Test::Unit::TestCase
38
39
  @conn.expects(:receive_message).with do |op, msg, log, sock|
39
40
  op == 2004
40
41
  end.returns([[], 0, 0])
41
- @logger.expects(:debug).with do |msg|
42
- msg.include?('Moby')
42
+ @conn.expects(:log_operation).with do |name, payload|
43
+ (name == :find) && payload[:selector][:title].include?('Moby')
43
44
  end
44
45
  @coll.find({:title => 'Moby Dick'}).sort([['title', 1], ['author', 1]]).next_document
45
46
  end
@@ -52,8 +53,8 @@ class CollectionTest < Test::Unit::TestCase
52
53
  @conn.expects(:send_message).with do |op, msg, log|
53
54
  op == 2002
54
55
  end
55
- @logger.expects(:debug).with do |msg|
56
- msg.include?("Binary")
56
+ @conn.expects(:log_operation).with do |name, payload|
57
+ (name == :insert) && payload[:documents][0][:data].inspect.include?('Binary')
57
58
  end
58
59
  @coll.insert({:data => data})
59
60
  end
@@ -65,8 +66,8 @@ class CollectionTest < Test::Unit::TestCase
65
66
  @conn.expects(:send_message_with_safe_check).with do |op, msg, db_name, log|
66
67
  op == 2001
67
68
  end
68
- @logger.expects(:debug).with do |msg|
69
- msg.include?("testing['books'].update")
69
+ @conn.expects(:log_operation).with do |name, payload|
70
+ (name == :update) && payload[:document][:title].include?('Moby')
70
71
  end
71
72
  @coll.update({}, {:title => 'Moby Dick'}, :safe => true)
72
73
  end
@@ -78,43 +79,42 @@ class CollectionTest < Test::Unit::TestCase
78
79
  @conn.expects(:send_message_with_safe_check).with do |op, msg, db_name, log|
79
80
  op == 2001
80
81
  end
81
- @logger.stubs(:debug)
82
+ @conn.stubs(:log_operation)
82
83
  @coll.update({}, {:title => 'Moby Dick'}, :safe => true)
83
84
  end
84
-
85
+
85
86
  should "not call insert for each ensure_index call" do
86
87
  @conn = Connection.new('localhost', 27017, :logger => @logger, :connect => false)
87
88
  @db = @conn['testing']
88
89
  @coll = @db.collection('books')
89
90
  @coll.expects(:generate_indexes).once
90
-
91
+
91
92
  @coll.ensure_index [["x", Mongo::DESCENDING]]
92
93
  @coll.ensure_index [["x", Mongo::DESCENDING]]
93
-
94
94
  end
95
+
95
96
  should "call generate_indexes for a new direction on the same field for ensure_index" do
96
97
  @conn = Connection.new('localhost', 27017, :logger => @logger, :connect => false)
97
98
  @db = @conn['testing']
98
99
  @coll = @db.collection('books')
99
100
  @coll.expects(:generate_indexes).twice
100
-
101
+
101
102
  @coll.ensure_index [["x", Mongo::DESCENDING]]
102
103
  @coll.ensure_index [["x", Mongo::ASCENDING]]
103
-
104
+
104
105
  end
105
-
106
+
106
107
  should "call generate_indexes twice because the cache time is 0 seconds" do
107
108
  @conn = Connection.new('localhost', 27017, :logger => @logger, :connect => false)
108
109
  @db = @conn['testing']
109
110
  @db.cache_time = 0
110
111
  @coll = @db.collection('books')
111
112
  @coll.expects(:generate_indexes).twice
112
-
113
113
 
114
114
  @coll.ensure_index [["x", Mongo::DESCENDING]]
115
115
  @coll.ensure_index [["x", Mongo::DESCENDING]]
116
116
  end
117
-
117
+
118
118
  should "call generate_indexes for each key when calling ensure_indexes" do
119
119
  @conn = Connection.new('localhost', 27017, :logger => @logger, :connect => false)
120
120
  @db = @conn['testing']
@@ -123,11 +123,8 @@ class CollectionTest < Test::Unit::TestCase
123
123
  @coll.expects(:generate_indexes).once.with do |a, b, c|
124
124
  a == {"x"=>-1, "y"=>-1}
125
125
  end
126
-
126
+
127
127
  @coll.ensure_index [["x", Mongo::DESCENDING], ["y", Mongo::DESCENDING]]
128
128
  end
129
-
130
-
131
-
132
129
  end
133
130
  end