mongo 1.7.1 → 1.8.0

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 (138) hide show
  1. data/{LICENSE.txt → LICENSE} +0 -0
  2. data/README.md +124 -111
  3. data/Rakefile +9 -325
  4. data/VERSION +1 -0
  5. data/bin/mongo_console +4 -4
  6. data/examples/admin.rb +43 -0
  7. data/examples/capped.rb +22 -0
  8. data/examples/cursor.rb +48 -0
  9. data/examples/gridfs.rb +44 -0
  10. data/examples/index_test.rb +126 -0
  11. data/examples/info.rb +31 -0
  12. data/examples/queries.rb +74 -0
  13. data/examples/replica_set.rb +26 -0
  14. data/examples/simple.rb +25 -0
  15. data/examples/strict.rb +35 -0
  16. data/examples/types.rb +36 -0
  17. data/{test/load → examples/web}/thin/load.rb +3 -1
  18. data/{test/load → examples/web}/unicorn/load.rb +5 -3
  19. data/lib/mongo.rb +8 -10
  20. data/lib/mongo/collection.rb +134 -114
  21. data/lib/mongo/cursor.rb +21 -14
  22. data/lib/mongo/db.rb +30 -28
  23. data/lib/mongo/exceptions.rb +1 -1
  24. data/lib/mongo/gridfs/grid.rb +8 -7
  25. data/lib/mongo/gridfs/grid_ext.rb +1 -1
  26. data/lib/mongo/gridfs/grid_file_system.rb +6 -5
  27. data/lib/mongo/gridfs/grid_io.rb +22 -19
  28. data/lib/mongo/legacy.rb +82 -0
  29. data/lib/mongo/{connection.rb → mongo_client.rb} +82 -61
  30. data/lib/mongo/{repl_set_connection.rb → mongo_replica_set_client.rb} +54 -39
  31. data/lib/mongo/{sharded_connection.rb → mongo_sharded_client.rb} +9 -9
  32. data/lib/mongo/networking.rb +25 -20
  33. data/lib/mongo/util/conversions.rb +1 -1
  34. data/lib/mongo/util/core_ext.rb +1 -1
  35. data/lib/mongo/util/logging.rb +20 -4
  36. data/lib/mongo/util/node.rb +16 -16
  37. data/lib/mongo/util/pool.rb +56 -27
  38. data/lib/mongo/util/pool_manager.rb +28 -27
  39. data/lib/mongo/util/server_version.rb +1 -1
  40. data/lib/mongo/util/sharding_pool_manager.rb +8 -8
  41. data/lib/mongo/util/ssl_socket.rb +1 -5
  42. data/lib/mongo/util/support.rb +24 -8
  43. data/lib/mongo/util/tcp_socket.rb +0 -4
  44. data/lib/mongo/util/uri_parser.rb +54 -38
  45. data/lib/mongo/util/write_concern.rb +67 -0
  46. data/mongo.gemspec +21 -32
  47. data/test/auxillary/{1.4_features.rb → 1.4_feature_test.rb} +4 -5
  48. data/test/auxillary/authentication_test.rb +18 -20
  49. data/test/auxillary/autoreconnect_test.rb +3 -5
  50. data/test/auxillary/fork_test.rb +5 -7
  51. data/test/auxillary/repl_set_auth_test.rb +13 -15
  52. data/test/auxillary/slave_connection_test.rb +8 -7
  53. data/test/auxillary/threaded_authentication_test.rb +15 -17
  54. data/test/bson/binary_test.rb +1 -1
  55. data/test/bson/bson_test.rb +60 -36
  56. data/test/bson/byte_buffer_test.rb +1 -1
  57. data/test/bson/hash_with_indifferent_access_test.rb +2 -2
  58. data/test/bson/json_test.rb +1 -2
  59. data/test/bson/object_id_test.rb +1 -2
  60. data/test/bson/ordered_hash_test.rb +1 -1
  61. data/test/bson/timestamp_test.rb +1 -1
  62. data/test/{collection_test.rb → functional/collection_test.rb} +57 -57
  63. data/test/{connection_test.rb → functional/connection_test.rb} +75 -89
  64. data/test/{conversions_test.rb → functional/conversions_test.rb} +1 -1
  65. data/test/{cursor_fail_test.rb → functional/cursor_fail_test.rb} +3 -29
  66. data/test/{cursor_message_test.rb → functional/cursor_message_test.rb} +1 -1
  67. data/test/{cursor_test.rb → functional/cursor_test.rb} +5 -1
  68. data/test/{db_api_test.rb → functional/db_api_test.rb} +8 -9
  69. data/test/{db_connection_test.rb → functional/db_connection_test.rb} +3 -5
  70. data/test/{db_test.rb → functional/db_test.rb} +13 -13
  71. data/test/{grid_file_system_test.rb → functional/grid_file_system_test.rb} +2 -2
  72. data/test/{grid_io_test.rb → functional/grid_io_test.rb} +6 -6
  73. data/test/{grid_test.rb → functional/grid_test.rb} +4 -10
  74. data/test/{pool_test.rb → functional/pool_test.rb} +1 -1
  75. data/test/functional/safe_test.rb +84 -0
  76. data/test/{support_test.rb → functional/support_test.rb} +1 -1
  77. data/test/{threading_test.rb → functional/threading_test.rb} +9 -9
  78. data/test/{timeout_test.rb → functional/timeout_test.rb} +1 -1
  79. data/test/{uri_test.rb → functional/uri_test.rb} +1 -1
  80. data/test/functional/write_concern_test.rb +104 -0
  81. data/test/replica_set/basic_test.rb +139 -0
  82. data/test/replica_set/client_test.rb +255 -0
  83. data/test/replica_set/complex_connect_test.rb +62 -0
  84. data/test/replica_set/connection_test.rb +255 -0
  85. data/test/{replica_sets → replica_set}/count_test.rb +17 -14
  86. data/test/replica_set/cursor_test.rb +75 -0
  87. data/test/{replica_sets → replica_set}/insert_test.rb +19 -16
  88. data/test/replica_set/query_test.rb +64 -0
  89. data/test/replica_set/refresh_test.rb +153 -0
  90. data/test/{replica_sets → replica_set}/replication_ack_test.rb +21 -17
  91. data/test/sharded_cluster/basic_test.rb +31 -50
  92. data/test/support/hash_with_indifferent_access.rb +1 -1
  93. data/test/test_helper.rb +56 -9
  94. data/test/threading/threading_with_large_pool_test.rb +8 -8
  95. data/test/tools/mongo_config.rb +270 -58
  96. data/test/tools/mongo_config_test.rb +146 -0
  97. data/test/unit/client_test.rb +230 -0
  98. data/test/unit/collection_test.rb +45 -32
  99. data/test/unit/connection_test.rb +82 -74
  100. data/test/unit/cursor_test.rb +14 -6
  101. data/test/unit/db_test.rb +8 -8
  102. data/test/unit/grid_test.rb +11 -11
  103. data/test/unit/node_test.rb +24 -24
  104. data/test/unit/pool_manager_test.rb +13 -13
  105. data/test/unit/pool_test.rb +1 -1
  106. data/test/unit/read_test.rb +21 -26
  107. data/test/unit/safe_test.rb +52 -33
  108. data/test/unit/util_test.rb +55 -0
  109. data/test/unit/write_concern_test.rb +161 -0
  110. metadata +158 -171
  111. data/docs/CREDITS.md +0 -123
  112. data/docs/FAQ.md +0 -116
  113. data/docs/GRID_FS.md +0 -158
  114. data/docs/HISTORY.md +0 -392
  115. data/docs/READ_PREFERENCE.md +0 -99
  116. data/docs/RELEASES.md +0 -54
  117. data/docs/REPLICA_SETS.md +0 -113
  118. data/docs/TAILABLE_CURSORS.md +0 -51
  119. data/docs/TUTORIAL.md +0 -356
  120. data/docs/WRITE_CONCERN.md +0 -31
  121. data/lib/mongo/gridfs/grid_io_fix.rb +0 -38
  122. data/lib/mongo/version.rb +0 -3
  123. data/test/bson/test_helper.rb +0 -30
  124. data/test/replica_sets/basic_test.rb +0 -119
  125. data/test/replica_sets/complex_connect_test.rb +0 -57
  126. data/test/replica_sets/complex_read_preference_test.rb +0 -237
  127. data/test/replica_sets/connect_test.rb +0 -156
  128. data/test/replica_sets/cursor_test.rb +0 -70
  129. data/test/replica_sets/pooled_insert_test.rb +0 -57
  130. data/test/replica_sets/query_test.rb +0 -50
  131. data/test/replica_sets/read_preference_test.rb +0 -234
  132. data/test/replica_sets/refresh_test.rb +0 -156
  133. data/test/replica_sets/refresh_with_threads_test.rb +0 -60
  134. data/test/replica_sets/rs_test_helper.rb +0 -39
  135. data/test/safe_test.rb +0 -68
  136. data/test/sharded_cluster/mongo_config_test.rb +0 -126
  137. data/test/sharded_cluster/sc_test_helper.rb +0 -39
  138. data/test/tools/repl_set_manager.rb +0 -418
@@ -183,4 +183,4 @@ module ActiveSupport
183
183
  end
184
184
  end
185
185
 
186
- HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
186
+ #HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
@@ -1,8 +1,52 @@
1
- $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
1
  require 'rubygems'
3
- require 'mongo'
4
- gem 'test-unit'
5
2
  require 'test/unit'
3
+ require 'tools/mongo_config'
4
+
5
+ class Test::Unit::TestCase
6
+
7
+ TEST_DATA = File.join(File.dirname(__FILE__), 'data')
8
+
9
+ def ensure_cluster(kind=nil)
10
+ if defined?(@@current_class) and @@current_class == self.class
11
+ @@cluster.start
12
+ else
13
+ @@current_class = self.class
14
+
15
+ if kind == :rs
16
+ opts = Mongo::Config::DEFAULT_REPLICA_SET
17
+ opts.merge!(:arbiters => 2)
18
+ else
19
+ opts = Mongo::Config::DEFAULT_SHARDED_SIMPLE
20
+ opts.merge!(:routers => 4)
21
+ end
22
+
23
+ dbpath = ENV['DBPATH'] || 'data'
24
+ opts.merge!(:dbpath => dbpath)
25
+
26
+ #debug 1, opts
27
+ config = Mongo::Config.cluster(opts)
28
+ #debug 1, config
29
+ @@cluster = Mongo::Config::ClusterManager.new(config)
30
+ @@cluster.start
31
+ end
32
+ instance_variable_set("@#{kind}", @@cluster)
33
+ end
34
+
35
+ # Generic code for rescuing connection failures and retrying operations.
36
+ # This could be combined with some timeout functionality.
37
+ def rescue_connection_failure(max_retries=30)
38
+ retries = 0
39
+ begin
40
+ yield
41
+ rescue Mongo::ConnectionFailure => ex
42
+ #puts "Rescue attempt #{retries}: from #{ex}"
43
+ retries += 1
44
+ raise ex if retries > max_retries
45
+ sleep(2)
46
+ retry
47
+ end
48
+ end
49
+ end
6
50
 
7
51
  def silently
8
52
  warn_level = $VERBOSE
@@ -16,7 +60,6 @@ def silently
16
60
  end
17
61
 
18
62
  begin
19
- require 'rubygems' if RUBY_VERSION < "1.9.0" && !ENV['C_EXT']
20
63
  silently { require 'shoulda' }
21
64
  silently { require 'mocha' }
22
65
  rescue LoadError
@@ -38,7 +81,7 @@ unless defined? MONGO_TEST_DB
38
81
  end
39
82
 
40
83
  unless defined? TEST_PORT
41
- TEST_PORT = ENV['MONGO_RUBY_DRIVER_PORT'] ? ENV['MONGO_RUBY_DRIVER_PORT'].to_i : Mongo::Connection::DEFAULT_PORT
84
+ TEST_PORT = ENV['MONGO_RUBY_DRIVER_PORT'] ? ENV['MONGO_RUBY_DRIVER_PORT'].to_i : Mongo::MongoClient::DEFAULT_PORT
42
85
  end
43
86
 
44
87
  unless defined? TEST_HOST
@@ -49,12 +92,16 @@ class Test::Unit::TestCase
49
92
  include Mongo
50
93
  include BSON
51
94
 
52
- def self.standard_connection(options={})
53
- Connection.new(TEST_HOST, TEST_PORT, options)
95
+ def self.standard_connection(options={}, legacy=false)
96
+ if legacy
97
+ Connection.new(TEST_HOST, TEST_PORT, options)
98
+ else
99
+ MongoClient.new(TEST_HOST, TEST_PORT, options)
100
+ end
54
101
  end
55
102
 
56
- def standard_connection(options={})
57
- self.class.standard_connection(options)
103
+ def standard_connection(options={}, legacy=false)
104
+ self.class.standard_connection(options, legacy)
58
105
  end
59
106
 
60
107
  def self.host_port
@@ -1,4 +1,4 @@
1
- require File.expand_path("../../test_helper", __FILE__)
1
+ require 'test_helper'
2
2
 
3
3
  # Essentialy the same as test_threading.rb but with an expanded pool for
4
4
  # testing multiple connections.
@@ -6,7 +6,8 @@ class TestThreadingLargePool < Test::Unit::TestCase
6
6
 
7
7
  include Mongo
8
8
 
9
- @@db = standard_connection(:pool_size => 50, :pool_timeout => 60).db(MONGO_TEST_DB)
9
+ @@client = standard_connection(:pool_size => 50, :pool_timeout => 60)
10
+ @@db = @@client.db(MONGO_TEST_DB)
10
11
  @@coll = @@db.collection('thread-test-collection')
11
12
 
12
13
  def set_up_safe_data
@@ -29,10 +30,10 @@ class TestThreadingLargePool < Test::Unit::TestCase
29
30
  threads[i] = Thread.new do
30
31
  if i % 2 == 0
31
32
  assert_raise Mongo::OperationFailure do
32
- @unique.update({"test" => "insert"}, {"$set" => {"test" => "update"}}, :safe => true)
33
+ @unique.update({"test" => "insert"}, {"$set" => {"test" => "update"}})
33
34
  end
34
35
  else
35
- @duplicate.update({"test" => "insert"}, {"$set" => {"test" => "update"}}, :safe => true)
36
+ @duplicate.update({"test" => "insert"}, {"$set" => {"test" => "update"}})
36
37
  end
37
38
  end
38
39
  end
@@ -49,10 +50,10 @@ class TestThreadingLargePool < Test::Unit::TestCase
49
50
  threads[i] = Thread.new do
50
51
  if i % 2 == 0
51
52
  assert_raise Mongo::OperationFailure do
52
- @unique.insert({"test" => "insert"}, :safe => true)
53
+ @unique.insert({"test" => "insert"})
53
54
  end
54
55
  else
55
- @duplicate.insert({"test" => "insert"}, :safe => true)
56
+ @duplicate.insert({"test" => "insert"})
56
57
  end
57
58
  end
58
59
  end
@@ -67,7 +68,7 @@ class TestThreadingLargePool < Test::Unit::TestCase
67
68
  @@coll = @@db.collection('thread-test-collection')
68
69
 
69
70
  1000.times do |i|
70
- @@coll.insert("x" => i, :safe => true)
71
+ @@coll.insert({ "x" => i })
71
72
  end
72
73
 
73
74
  threads = []
@@ -86,5 +87,4 @@ class TestThreadingLargePool < Test::Unit::TestCase
86
87
  threads[i].join
87
88
  end
88
89
  end
89
-
90
90
  end
@@ -1,15 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
-
3
2
  require 'socket'
4
3
  require 'fileutils'
4
+ require 'mongo'
5
+ require 'sfl'
5
6
 
6
7
  $debug_level = 2
7
8
  STDOUT.sync = true
8
9
 
9
- unless defined? Mongo
10
- require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'mongo')
11
- end
12
-
13
10
  def debug(level, arg)
14
11
  if level <= $debug_level
15
12
  file_line = caller[0][/(.*:\d+):/, 1]
@@ -18,19 +15,28 @@ def debug(level, arg)
18
15
  end
19
16
  end
20
17
 
18
+ #
19
+ # Design Notes
20
+ # Configuration and Cluster Management are modularized with the concept that the Cluster Manager
21
+ # can be supplied with any configuration to run.
22
+ # A configuration can be edited, modified, copied into a test file, and supplied to a cluster manager
23
+ # as a parameter.
24
+ #
21
25
  module Mongo
22
26
  class Config
23
- DEFAULT_BASE_OPTS = { :host => 'localhost', :logpath => 'data/log', :dbpath => 'data' }
24
- DEFAULT_REPLICA_SET = DEFAULT_BASE_OPTS.merge( :replicas => 3 )
27
+ DEFAULT_BASE_OPTS = { :host => 'localhost', :dbpath => 'data', :logpath => 'data/log'}
28
+ DEFAULT_REPLICA_SET = DEFAULT_BASE_OPTS.merge( :replicas => 3, :arbiters => 0 )
25
29
  DEFAULT_SHARDED_SIMPLE = DEFAULT_BASE_OPTS.merge( :shards => 2, :configs => 1, :routers => 2 )
26
- DEFAULT_SHARDED_REPLICA = DEFAULT_SHARDED_SIMPLE.merge( :replicas => 3 )
30
+ DEFAULT_SHARDED_REPLICA = DEFAULT_SHARDED_SIMPLE.merge( :replicas => 3, :arbiters => 0)
27
31
 
28
- SERVER_PRELUDE_KEYS = [:host, :command]
32
+ IGNORE_KEYS = [:host, :command, :_id]
29
33
  SHARDING_OPT_KEYS = [:shards, :configs, :routers]
30
- REPLICA_OPT_KEYS = [:replicas]
34
+ REPLICA_OPT_KEYS = [:replicas, :arbiters]
31
35
  MONGODS_OPT_KEYS = [:mongods]
32
36
  CLUSTER_OPT_KEYS = SHARDING_OPT_KEYS + REPLICA_OPT_KEYS + MONGODS_OPT_KEYS
33
37
 
38
+ FLAGS = [:noprealloc, :smallfiles, :logappend, :configsvr, :shardsvr, :quiet]
39
+
34
40
  DEFAULT_VERIFIES = 60
35
41
  BASE_PORT = 3000
36
42
  @@port = BASE_PORT
@@ -41,31 +47,99 @@ module Mongo
41
47
 
42
48
  def self.cluster(opts = DEFAULT_SHARDED_SIMPLE)
43
49
  raise "missing required option" if [:host, :dbpath].any?{|k| !opts[k]}
44
- config = opts.reject{|k,v| CLUSTER_OPT_KEYS.include?(k)}
45
- keys = SHARDING_OPT_KEYS.any?{|k| opts[k]} ? SHARDING_OPT_KEYS : nil
46
- keys ||= REPLICA_OPT_KEYS.any?{|k| opts[k]} ? REPLICA_OPT_KEYS : nil
47
- keys ||= MONGODS_OPT_KEYS
48
- keys.each do |key|
49
- config[key] = opts.fetch(key,1).times.collect do |i| #default to 1 of whatever
50
- server_base = key.to_s.chop
51
- dbpath = "#{opts[:dbpath]}/#{server_base}#{i}"
52
- logpath = "#{dbpath}/#{server_base}.log"
53
- if key == :shards && opts[:replicas]
54
- self.cluster(opts.reject{|k,v| SHARDING_OPT_KEYS.include?(k)}.merge(:dbpath => dbpath))
50
+
51
+ config = opts.reject {|k,v| CLUSTER_OPT_KEYS.include?(k)}
52
+ kinds = CLUSTER_OPT_KEYS.select{|key| opts.has_key?(key)} # order is significant
53
+
54
+ replica_count = 0
55
+
56
+ kinds.each do |kind|
57
+ config[kind] = opts.fetch(kind,1).times.collect do |i| #default to 1 of whatever
58
+ if kind == :shards && opts[:replicas]
59
+ self.cluster(opts.reject{|k,v| SHARDING_OPT_KEYS.include?(k)}.merge(:dbpath => path))
55
60
  else
56
- server_params = { :host => opts[:host], :port => self.get_available_port, :logpath => logpath }
57
- case key
58
- when :replicas; server_params.merge!( :command => 'mongod', :dbpath => dbpath, :replSet => File.basename(opts[:dbpath]) )
59
- when :configs; server_params.merge!( :command => 'mongod', :dbpath => dbpath, :configsvr => nil )
60
- when :routers; server_params.merge!( :command => 'mongos', :configdb => self.configdb(config) ) # mongos, NO dbpath
61
- else server_params.merge!( :command => 'mongod', :dbpath => dbpath ) # :mongods, :shards
61
+ node = case kind
62
+ when :replicas
63
+ make_replica(opts, replica_count)
64
+ when :arbiters
65
+ make_replica(opts, replica_count)
66
+ when :configs
67
+ make_config(opts)
68
+ when :routers
69
+ make_router(config, opts)
70
+ else
71
+ make_mongod(kind, opts)
62
72
  end
73
+
74
+ replica_count += 1 if [:replicas, :arbiters].member?(kind)
75
+ node
63
76
  end
64
77
  end
65
78
  end
66
79
  config
67
80
  end
68
81
 
82
+ def self.make_mongo(kind, opts)
83
+ dbpath = opts[:dbpath]
84
+ port = self.get_available_port
85
+ path = "#{dbpath}/#{kind}-#{port}"
86
+ logpath = "#{path}/#{kind}.log"
87
+
88
+ {
89
+ :host => opts[:host],
90
+ :port => port,
91
+ :logpath => logpath,
92
+ :logappend => true
93
+ }
94
+ end
95
+
96
+ def self.make_mongod(kind, opts)
97
+ params = make_mongo('mongods', opts)
98
+
99
+ mongod = ENV['MONGOD'] || 'mongod'
100
+ path = File.dirname(params[:logpath])
101
+
102
+ noprealloc = opts[:noprealloc] || true
103
+ smallfiles = opts[:smallfiles] || true
104
+ quiet = opts[:quiet] || true
105
+
106
+ params.merge(
107
+ :command => mongod,
108
+ :dbpath => path,
109
+ :smallfiles => smallfiles,
110
+ :noprealloc => noprealloc,
111
+ :quiet => quiet
112
+ )
113
+ end
114
+
115
+ def self.make_replica(opts, count)
116
+ params = make_mongod('replicas', opts)
117
+
118
+ replSet = opts[:replSet] || 'ruby-driver-test'
119
+ oplog_size = opts[:oplog_size] || 10
120
+
121
+ params.merge(
122
+ :_id => count,
123
+ :replSet => replSet,
124
+ :oplogSize => oplog_size
125
+ )
126
+ end
127
+
128
+ def self.make_config(opts)
129
+ params = make_mongod('configs', opts)
130
+ params.merge(:configsvr => nil)
131
+ end
132
+
133
+ def self.make_router(config, opts)
134
+ params = make_mongo('routers', opts)
135
+ mongos = ENV['MONGOS'] || 'mongos'
136
+
137
+ params.merge(
138
+ :command => mongos,
139
+ :configdb => self.configdb(config)
140
+ )
141
+ end
142
+
69
143
  def self.port_available?(port)
70
144
  ret = false
71
145
  socket = Socket.new(Socket::Constants::AF_INET, Socket::Constants::SOCK_STREAM, 0)
@@ -96,15 +170,23 @@ module Mongo
96
170
  @cmd = cmd
97
171
  end
98
172
 
173
+ def clear_zombie
174
+ if @pid
175
+ begin
176
+ pid = Process.waitpid(@pid, Process::WNOHANG)
177
+ rescue Errno::ECHILD
178
+ # JVM might have already reaped the exit status
179
+ end
180
+ @pid = nil if pid && pid > 0
181
+ end
182
+ end
183
+
99
184
  def start(verifies = 0)
185
+ clear_zombie
100
186
  return @pid if running?
101
187
  begin
102
- @pid = fork do
103
- STDIN.reopen '/dev/null'
104
- STDOUT.reopen '/dev/null', 'a'
105
- STDERR.reopen STDOUT
106
- exec cmd # spawn(@cmd, [:in, :out, :err] => :close) #
107
- end
188
+ @pid = Process.spawn(*@cmd) # redirection not supported in jruby
189
+ sleep 1
108
190
  verify(verifies) if verifies > 0
109
191
  @pid
110
192
  end
@@ -115,16 +197,20 @@ module Mongo
115
197
  wait
116
198
  end
117
199
 
118
- def kill
200
+ def kill(signal_no = 2)
119
201
  begin
120
- @pid && Process.kill(3, @pid) && true
202
+ @pid && Process.kill(signal_no, @pid) && true
121
203
  rescue Errno::ESRCH
122
204
  false
123
205
  end
124
206
  end
125
207
 
126
208
  def wait
127
- Process.wait(@pid) if @pid
209
+ begin
210
+ Process.waitpid(@pid) if @pid
211
+ rescue Errno::ECHILD
212
+ # JVM might have already reaped the exit status
213
+ end
128
214
  @pid = nil
129
215
  end
130
216
 
@@ -153,17 +239,34 @@ module Mongo
153
239
  @host = host
154
240
  @port = port
155
241
  end
242
+
243
+ def host_port
244
+ [@host, @port].join(':')
245
+ end
246
+
247
+ def host_port_a # for old format
248
+ [@host, @port]
249
+ end
156
250
  end
157
251
 
158
252
  class DbServer < Server
159
253
  attr_accessor :config
254
+
160
255
  def initialize(config)
161
256
  @config = config
162
257
  dbpath = @config[:dbpath]
163
258
  [dbpath, File.dirname(@config[:logpath])].compact.each{|dir| FileUtils.mkdir_p(dir) unless File.directory?(dir) }
164
259
  command = @config[:command] || 'mongod'
165
- arguments = @config.reject{|k,v| SERVER_PRELUDE_KEYS.include?(k)}
166
- cmd = [command, arguments.collect{|k,v| ['--' + k.to_s, v ]}].flatten.join(' ')
260
+ params = @config.reject{|k,v| IGNORE_KEYS.include?(k)}
261
+ arguments = params.sort{|a, b| a[0].to_s <=> b[0].to_s}.collect do |arg, value| # sort block is needed for 1.8.7 which lacks Symbol#<=>
262
+ argument = '--' + arg.to_s
263
+ if FLAGS.member?(arg)
264
+ [argument]
265
+ else
266
+ [argument, value.to_s]
267
+ end
268
+ end
269
+ cmd = [command, arguments].flatten.compact
167
270
  super(cmd, @config[:host], @config[:port])
168
271
  end
169
272
 
@@ -172,19 +275,20 @@ module Mongo
172
275
  verify(verifies)
173
276
  end
174
277
 
175
- def verify(verifies = 10)
278
+ def verify(verifies = 600)
176
279
  verifies.times do |i|
177
- #puts "DbServer.verify - port: #{@port} iteration: #{i}"
280
+ #puts "DbServer.verify via connection probe - port:#{@port.inspect} iteration:#{i} @pid:#{@pid.inspect} kill:#{Process.kill(0, @pid).inspect} running?:#{running?.inspect} cmd:#{cmd.inspect}"
178
281
  begin
179
282
  raise Mongo::ConnectionFailure unless running?
180
- Mongo::Connection.new(@host, @port).close
283
+ Mongo::MongoClient.new(@host, @port).close
181
284
  #puts "DbServer.verified via connection - port: #{@port} iteration: #{i}"
182
285
  return @pid
183
286
  rescue Mongo::ConnectionFailure
184
287
  sleep 1
185
288
  end
186
289
  end
187
- raise Mongo::ConnectionFailure, "DbServer.start verification via connection failed - port: #{@port}"
290
+ system "ps -fp #{@pid}; cat #{@config[:logpath]}"
291
+ raise Mongo::ConnectionFailure, "DbServer.start verify via connection probe failed - port:#{@port.inspect} @pid:#{@pid.inspect} kill:#{Process.kill(0, @pid).inspect} running?:#{running?.inspect} cmd:#{cmd.inspect}"
188
292
  end
189
293
 
190
294
  end
@@ -210,15 +314,16 @@ module Mongo
210
314
  cmd_servers = cmd_servers.class == Array ? cmd_servers : [cmd_servers]
211
315
  cmd_servers.each do |cmd_server|
212
316
  debug 3, cmd_server.inspect
213
- conn = Mongo::Connection.new(cmd_server[:host], cmd_server[:port])
317
+ cmd_server = cmd_server.config if cmd_server.is_a?(DbServer)
318
+ client = Mongo::MongoClient.new(cmd_server[:host], cmd_server[:port])
214
319
  cmd.each do |c|
215
320
  debug 3, "ClusterManager.command c:#{c.inspect}"
216
- response = conn[db_name].command( c, opts )
321
+ response = client[db_name].command( c, opts )
217
322
  debug 3, "ClusterManager.command response:#{response.inspect}"
218
323
  raise Mongo::OperationFailure, "c:#{c.inspect} opts:#{opts.inspect} failed" unless response["ok"] == 1.0 || opts.fetch(:check_response, true) == false
219
324
  ret << response
220
325
  end
221
- conn.close
326
+ client.close
222
327
  end
223
328
  debug 3, "command ret:#{ret.inspect}"
224
329
  ret.size == 1 ? ret.first : ret
@@ -228,26 +333,130 @@ module Mongo
228
333
  command( @config[:replicas].first, 'admin', { :replSetGetStatus => 1 }, {:check_response => false } )
229
334
  end
230
335
 
231
- def repl_set_initiate( cfg = nil )
232
- cfg ||= {
233
- :_id => @config[:replicas].first[:replSet],
234
- :members => @config[:replicas].each_with_index.collect{|s, i| { :_id => i, :host => "#{s[:host]}:#{s[:port]}" } },
336
+ def repl_set_get_config
337
+ host, port = primary_name.split(":")
338
+ client = Mongo::MongoClient.new(host, port)
339
+ client['local']['system.replset'].find_one
340
+ end
341
+
342
+ def repl_set_config
343
+ members = []
344
+ @config[:replicas].each{|s| members << { :_id => s[:_id], :host => "#{s[:host]}:#{s[:port]}" } }
345
+ @config[:arbiters].each{|s| members << { :_id => s[:_id], :host => "#{s[:host]}:#{s[:port]}", :arbiterOnly => true } }
346
+ {
347
+ :_id => @config[:replicas].first[:replSet],
348
+ :members => members
235
349
  }
236
- command( @config[:replicas].first, 'admin', { :replSetInitiate => cfg } )
350
+ end
351
+
352
+ def repl_set_initiate( cfg = nil )
353
+ command( @config[:replicas].first, 'admin', { :replSetInitiate => cfg || repl_set_config } )
237
354
  end
238
355
 
239
356
  def repl_set_startup
240
357
  response = nil
241
358
  60.times do |i|
242
- break if (response = repl_set_get_status)['ok'] == 1.0
359
+ response = repl_set_get_status
360
+ members = response['members']
361
+ if response['ok'] == 1.0 && members.collect{|m| m['state']}.all?{|state| [1,2,7].index(state)}
362
+ return response if members.any?{|m| m['state'] == 1}
363
+ end
243
364
  sleep 1
244
365
  end
245
- raise Mongo::OperationFailure, "replSet startup failed - status: #{repsonse.inspect}" unless response && response['ok'] == 1.0
246
- response
366
+ raise Mongo::OperationFailure, "replSet startup failed - status: #{response.inspect}"
367
+ end
368
+
369
+ def repl_set_seeds
370
+ @config[:replicas].collect{|node| "#{node[:host]}:#{node[:port]}"}
371
+ end
372
+
373
+ def repl_set_seeds_old
374
+ @config[:replicas].collect{|node| [node[:host], node[:port]]}
375
+ end
376
+
377
+ def repl_set_name
378
+ @config[:replicas].first[:replSet]
379
+ end
380
+
381
+ def member_names_by_state(state)
382
+ states = Array(state)
383
+ status = repl_set_get_status
384
+ status['members'].find_all{|member| states.index(member['state']) }.collect{|member| member['name']}
385
+ end
386
+
387
+ def primary_name
388
+ member_names_by_state(1).first
389
+ end
390
+
391
+ def secondary_names
392
+ member_names_by_state(2)
393
+ end
394
+
395
+ def replica_names
396
+ member_names_by_state([1,2])
397
+ end
398
+
399
+ def arbiter_names
400
+ member_names_by_state(7)
401
+ end
402
+
403
+ def members_by_name(names)
404
+ names.collect do |name|
405
+ servers.find{|server| server.host_port == name}
406
+ end.compact
407
+ end
408
+
409
+ def primary
410
+ members_by_name([primary_name]).first
411
+ end
412
+
413
+ def secondaries
414
+ members_by_name(secondary_names)
415
+ end
416
+
417
+ def replicas
418
+ members_by_name(replica_names)
419
+ end
420
+
421
+ def arbiters
422
+ members_by_name(arbiter_names)
423
+ end
424
+
425
+ def config_names_by_kind(kind)
426
+ @config[kind].collect{|conf| "#{conf[:host]}:#{conf[:port]}"}
427
+ end
428
+
429
+ def shards
430
+ members_by_name(config_names_by_kind(:shards))
431
+ end
432
+
433
+ def repl_set_reconfig(new_config)
434
+ new_config['version'] = repl_set_get_config['version'] + 1
435
+ command( primary, 'admin', { :replSetReconfig => new_config } )
436
+ repl_set_startup
437
+ end
438
+
439
+ def repl_set_remove_node(state = [1,2])
440
+ names = member_names_by_state(state)
441
+ name = names[rand(names.length)]
442
+
443
+ @config[:replicas].delete_if{|node| "#{node[:host]}:#{node[:port]}" == name}
444
+ repl_set_reconfig(repl_set_config)
445
+ end
446
+
447
+ def repl_set_add_node
448
+ end
449
+
450
+ def configs
451
+ members_by_name(config_names_by_kind(:configs))
452
+ end
453
+
454
+ def routers
455
+ members_by_name(config_names_by_kind(:routers))
247
456
  end
248
457
 
249
458
  def mongos_seeds
250
- @config[:routers].collect{|router| "#{router[:host]}:#{router[:port]}"}
459
+ config_names_by_kind(:routers)
251
460
  end
252
461
 
253
462
  def ismaster
@@ -272,14 +481,16 @@ module Mongo
272
481
 
273
482
  def mongos_discover # can also do @config[:routers] find but only want mongos for connections
274
483
  (@config[:configs]).collect do |cmd_server|
275
- conn = Mongo::Connection.new(cmd_server[:host], cmd_server[:port])
276
- result = conn['config']['mongos'].find.to_a
277
- conn.close
484
+ client = Mongo::MongoClient.new(cmd_server[:host], cmd_server[:port])
485
+ result = client['config']['mongos'].find.to_a
486
+ client.close
278
487
  result
279
488
  end
280
489
  end
281
490
 
282
491
  def start
492
+ # Must start configs before mongos -- hash order not guaranteed on 1.8.X
493
+ servers(:configs).each{|server| server.start}
283
494
  servers.each{|server| server.start}
284
495
  # TODO - sharded replica sets - pending
285
496
  if @config[:replicas]
@@ -291,6 +502,7 @@ module Mongo
291
502
  end
292
503
  self
293
504
  end
505
+ alias :restart :start
294
506
 
295
507
  def stop
296
508
  servers.each{|server| server.stop}
@@ -298,7 +510,7 @@ module Mongo
298
510
  end
299
511
 
300
512
  def clobber
301
- system "rm -fr #{@config[:dbpath]}"
513
+ system "rm -fr #{@config[:dbpath]}/*"
302
514
  self
303
515
  end
304
516
  end