mongo 1.2.4 → 1.3.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/README.md +13 -25
  2. data/Rakefile +9 -1
  3. data/docs/HISTORY.md +19 -0
  4. data/docs/RELEASES.md +33 -0
  5. data/docs/REPLICA_SETS.md +4 -3
  6. data/lib/mongo.rb +20 -2
  7. data/lib/mongo/collection.rb +15 -2
  8. data/lib/mongo/connection.rb +75 -14
  9. data/lib/mongo/cursor.rb +12 -4
  10. data/lib/mongo/db.rb +3 -3
  11. data/lib/mongo/exceptions.rb +3 -0
  12. data/lib/mongo/gridfs/grid_io.rb +88 -7
  13. data/lib/mongo/repl_set_connection.rb +29 -11
  14. data/lib/mongo/util/pool.rb +15 -6
  15. data/lib/mongo/util/timeout.rb +42 -0
  16. data/lib/mongo/util/uri_parser.rb +5 -1
  17. data/test/auxillary/fork_test.rb +30 -0
  18. data/test/bson/bson_test.rb +68 -27
  19. data/test/bson/byte_buffer_test.rb +11 -0
  20. data/test/bson/object_id_test.rb +14 -1
  21. data/test/bson/ordered_hash_test.rb +7 -0
  22. data/test/bson/timestamp_test.rb +24 -0
  23. data/test/collection_test.rb +41 -24
  24. data/test/connection_test.rb +33 -2
  25. data/test/conversions_test.rb +10 -11
  26. data/test/cursor_fail_test.rb +1 -1
  27. data/test/cursor_message_test.rb +1 -1
  28. data/test/cursor_test.rb +33 -4
  29. data/test/db_api_test.rb +13 -2
  30. data/test/db_test.rb +3 -3
  31. data/test/grid_file_system_test.rb +0 -1
  32. data/test/grid_io_test.rb +72 -1
  33. data/test/grid_test.rb +16 -16
  34. data/test/replica_sets/connect_test.rb +8 -0
  35. data/test/replica_sets/query_test.rb +10 -0
  36. data/test/support/hash_with_indifferent_access.rb +0 -13
  37. data/test/support_test.rb +0 -1
  38. data/test/test_helper.rb +27 -8
  39. data/test/timeout_test.rb +14 -0
  40. data/test/unit/collection_test.rb +1 -1
  41. data/test/unit/connection_test.rb +0 -13
  42. data/test/unit/cursor_test.rb +16 -6
  43. data/test/unit/db_test.rb +9 -11
  44. data/test/unit/repl_set_connection_test.rb +0 -13
  45. data/test/unit/safe_test.rb +1 -1
  46. metadata +15 -23
data/README.md CHANGED
@@ -10,6 +10,7 @@ This documentation includes other articles of interest, include:
10
10
  4. [GridFS in Ruby](http://api.mongodb.org/ruby/current/file.GridFS.html).
11
11
  5. [Frequently Asked Questions](http://api.mongodb.org/ruby/current/file.FAQ.html).
12
12
  6. [History](http://api.mongodb.org/ruby/current/file.HISTORY.html).
13
+ 6. [Release plan](http://api.mongodb.org/ruby/current/file.RELEASES.html).
13
14
  7. [Credits](http://api.mongodb.org/ruby/current/file.CREDITS.html).
14
15
 
15
16
  Here's a quick code sample. Again, see the [MongoDB Ruby Tutorial](http://api.mongodb.org/ruby/current/file.TUTORIAL.html)
@@ -141,32 +142,11 @@ To set up a pooled connection to a single MongoDB instance:
141
142
  Though the pooling architecture will undoubtedly evolve, it currently owes much credit
142
143
  to the connection pooling implementations in ActiveRecord and PyMongo.
143
144
 
144
- ## Using with Phusion Passenger and Unicorn
145
-
146
- When Passenger and Unicorn are in smart spawning mode you need to be sure that child
147
- processes will create a new connection to the database. In Passenger, this can be handled like so:
148
-
149
- if defined?(PhusionPassenger)
150
- PhusionPassenger.on_event(:starting_worker_process) do |forked|
151
- if forked
152
- # Reset all connection objects. How you do this depends on where
153
- # you keep your connection object. In any case, call the #connect
154
- # method on the connection object. For example:
155
- # CONN.connect
156
- #
157
- # If you're using MongoMapper:
158
- # MongoMapper.connection.connect
159
- end
160
- end
161
- end
145
+ ## Forking
162
146
 
163
- In Unicorn, add this to your unicorn.rb file:
164
-
165
- after_fork do |server, worker|
166
- # Handle reconnection
167
- end
168
-
169
- The above code should be put into a Rails initializer or similar initialization script.
147
+ Certain Ruby application servers work by forking, and it has long been necessary to
148
+ re-establish the child process's connection to the database after fork. But with the release
149
+ of v1.3.0, the Ruby driver detects forking and reconnects automatically.
170
150
 
171
151
  ## String Encoding
172
152
 
@@ -271,6 +251,14 @@ Notes:
271
251
  * Cursors will timeout on the server after 10 minutes. If you need to keep a cursor
272
252
  open for more than 10 minutes, specify `:timeout => false` when you create the cursor.
273
253
 
254
+ ## Socket timeouts
255
+
256
+ The Ruby driver support timeouts on socket read operations. To enable them, set the
257
+ `:op_timeout` option when you create a `Mongo::Connection` object.
258
+
259
+ If implementing higher-level timeouts, using tools like `Rack::Timeout`, it's very important
260
+ to call `Mongo::Connection#close` to prevent the subsequent operation from receiving the previous
261
+ request.
274
262
 
275
263
  # Testing
276
264
 
data/Rakefile CHANGED
@@ -77,41 +77,49 @@ namespace :test do
77
77
  Rake::TestTask.new(:rs) do |t|
78
78
  t.test_files = FileList['test/replica_sets/*_test.rb']
79
79
  t.verbose = true
80
+ t.ruby_opts << '-w'
80
81
  end
81
82
 
82
83
  Rake::TestTask.new(:unit) do |t|
83
84
  t.test_files = FileList['test/unit/*_test.rb']
84
85
  t.verbose = true
86
+ t.ruby_opts << '-w'
85
87
  end
86
88
 
87
89
  Rake::TestTask.new(:functional) do |t|
88
90
  t.test_files = FileList['test/*_test.rb']
89
91
  t.verbose = true
92
+ t.ruby_opts << '-w'
90
93
  end
91
94
 
92
95
  Rake::TestTask.new(:pooled_threading) do |t|
93
96
  t.test_files = FileList['test/threading/*_test.rb']
94
97
  t.verbose = true
98
+ t.ruby_opts << '-w'
95
99
  end
96
100
 
97
101
  Rake::TestTask.new(:auto_reconnect) do |t|
98
102
  t.test_files = FileList['test/auxillary/autoreconnect_test.rb']
99
103
  t.verbose = true
104
+ t.ruby_opts << '-w'
100
105
  end
101
106
 
102
107
  Rake::TestTask.new(:authentication) do |t|
103
108
  t.test_files = FileList['test/auxillary/authentication_test.rb']
104
109
  t.verbose = true
110
+ t.ruby_opts << '-w'
105
111
  end
106
112
 
107
113
  Rake::TestTask.new(:new_features) do |t|
108
114
  t.test_files = FileList['test/auxillary/1.4_features.rb']
109
115
  t.verbose = true
116
+ t.ruby_opts << '-w'
110
117
  end
111
118
 
112
119
  Rake::TestTask.new(:bson) do |t|
113
120
  t.test_files = FileList['test/bson/*_test.rb']
114
121
  t.verbose = true
122
+ t.ruby_opts << '-w'
115
123
  end
116
124
 
117
125
  task :drop_databases do |t|
@@ -138,7 +146,7 @@ task :ydoc do
138
146
  require File.join(File.dirname(__FILE__), 'lib', 'mongo')
139
147
  out = File.join('ydoc', Mongo::VERSION)
140
148
  FileUtils.rm_rf('ydoc')
141
- system "yardoc lib/**/*.rb lib/mongo/**/*.rb lib/bson/**/*.rb -e yard/yard_ext.rb -p yard/templates -o #{out} --title MongoRuby-#{Mongo::VERSION} --files docs/TUTORIAL.md,docs/GridFS.md,docs/FAQ.md,docs/REPLICA_SETS.md,docs/WRITE_CONCERN.md,docs/HISTORY.md,docs/CREDITS.md,docs/1.0_UPGRADE.md"
149
+ system "yardoc lib/**/*.rb lib/mongo/**/*.rb lib/bson/**/*.rb -e yard/yard_ext.rb -p yard/templates -o #{out} --title MongoRuby-#{Mongo::VERSION} --files docs/TUTORIAL.md,docs/GridFS.md,docs/FAQ.md,docs/REPLICA_SETS.md,docs/WRITE_CONCERN.md,docs/HISTORY.md,docs/CREDITS.md,docs/1.0_UPGRADE.md,docs/RELEASES.md"
142
150
  end
143
151
 
144
152
  namespace :bamboo do
@@ -1,5 +1,24 @@
1
1
  # MongoDB Ruby Driver History
2
2
 
3
+ ### 1.3.0.rc0
4
+ 2011-3-29
5
+
6
+ * Add option to set timeouts on socket read calls. Use
7
+ the :op_timeout option when creating a new connection.
8
+ * Add StringIO methods to GridIO objects
9
+ * Support for BSON timestamp type with BSON::Timestamp
10
+ * Change the BSON binary subtype from 2 to 0
11
+ * Remove private method Connection#reset_conection
12
+ and deprecate public method ReplSetConnection#reset_connection
13
+ * ByteBuffer#== and OrderedHash#dup (FooBarWidget)
14
+ * Better check for UTF8 validity in Ruby 1.9
15
+ * Added previously removed Connection#host and Connection#port
16
+ * Added transformers to allow Mongo::Cursor to allow instantiated objects (jnunemaker)
17
+ * Automated reconnection on fork
18
+ * Added Cursor#next alias for Cursor#next_document
19
+ * Audit tests after enabling warnings (wpiekutowski)
20
+ * Various bug fixes thanks to FooBarWidget, Datanoise, Malitogeek
21
+
3
22
  ### 1.2.4
4
23
  2011-2-23
5
24
 
@@ -0,0 +1,33 @@
1
+ # MongoDB Ruby Driver Release Plan
2
+
3
+ This is a description of a formalized release plan that will take effect
4
+ with version 1.3.0.
5
+
6
+ ## Semantic versioning
7
+
8
+ The most significant difference is that releases will now adhere to the conventions of
9
+ [semantic versioning](http://semver.org). In particular, we will strictly abide by the
10
+ following release rules:
11
+
12
+ 1. Patch versions of the driver (Z in x.y.Z) will be released only when backward-compatible bug fixes are introduced. A bug fix is defined as an internal change that fixes incorrect behavior.
13
+
14
+ 2. Minor versions (Y in x.Y.z) will be released if new, backward-compatible functionality is introduced to the public API.
15
+
16
+ 3. Major versions (X in X.y.z) will be incremented if any backward-incompatibl changes are introduced to the public API.
17
+
18
+ This policy will clearly indicate to users when an upgrade may affect their code. As a side effect, version numbers will climb more quickly than before.
19
+
20
+
21
+ ## Release checklist
22
+
23
+ Before each relese to Rubygems.org, the following steps will be taken:
24
+
25
+ 1. All driver tests will be run on Linux, OS X, and Windows via continuous integration system.
26
+
27
+ 2. HISTORY file will document all significant commits.
28
+
29
+ 3. Version number will be incremented per the semantic version spec described above.
30
+
31
+ 4. Appropriate branches and tags will be created in Git repository, as necessary.
32
+
33
+ 5. Docs will be updated to the latest version of the driver and posted [online](http://api.mongodb.org/ruby/current/index.html).
@@ -6,12 +6,13 @@ Here follow a few considerations for those using the MongoDB Ruby driver with [r
6
6
 
7
7
  First, make sure that you've configured and initialized a replica set.
8
8
 
9
- Use `ReplSetConnection.new` to connect to a replica set:
9
+ Use `ReplSetConnection.new` to connect to a replica set. This method, which accepts a variable number of arugments,
10
+ takes a list of seed nodes followed by any connection options. You'll want to specify at least two seed nodes. This gives
11
+ the driver more chances to connect in the event that any one seed node is offline. Once the driver connects, it will
12
+ cache the replica set topology as reported by the given seed node and use that information if a failover is later required.
10
13
 
11
14
  @connection = ReplSetConnection.new(['n1.mydb.net', 27017], ['n2.mydb.net', 27017], ['n3.mydb.net', 27017])
12
15
 
13
- The driver will attempt to connect to a master node and, when found, will replace all seed nodes with known members of the replica set.
14
-
15
16
  ### Read slaves
16
17
 
17
18
  If you want to read from a seconday node, you can pass :read_secondary => true to ReplSetConnection#new.
@@ -19,7 +19,7 @@
19
19
  $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
20
20
 
21
21
  module Mongo
22
- VERSION = "1.2.4"
22
+ VERSION = "1.3.0.rc0"
23
23
  end
24
24
 
25
25
  module Mongo
@@ -76,4 +76,22 @@ if RUBY_PLATFORM =~ /java/
76
76
  end
77
77
  require 'mongo/gridfs/grid_file_system'
78
78
 
79
-
79
+ # Use SystemTimer on Ruby 1.8
80
+ if !defined?(RUBY_ENGINE) || (RUBY_ENGINE == 'ruby' && RUBY_VERSION < '1.9.0')
81
+ begin
82
+ require 'system_timer'
83
+ if SystemTimer.method(:timeout).arity.abs != 2
84
+ raise LoadError
85
+ end
86
+ Mongo::TimeoutHandler = SystemTimer
87
+ rescue LoadError
88
+ warn "Could not load SystemTimer >= v1.2.0. Falling back to timeout.rb. " +
89
+ "SystemTimer is STRONGLY recommended for timeouts in Ruby 1.8.7. " +
90
+ "See http://ph7spot.com/blog/system-timer-1-2-release for details."
91
+ require 'timeout'
92
+ Mongo::TimeoutHandler = Timeout
93
+ end
94
+ else
95
+ require 'timeout'
96
+ Mongo::TimeoutHandler = Timeout
97
+ end
@@ -157,6 +157,8 @@ module Mongo
157
157
  # the normal cursor timeout behavior of the mongod process. When +false+, the returned cursor will never timeout. Note
158
158
  # that disabling timeout will only work when #find is invoked with a block. This is to prevent any inadvertant failure to
159
159
  # close the cursor, as the cursor is explicitly closed when block code finishes.
160
+ # @option opts [Block] :transformer (nil) a block for tranforming returned documents.
161
+ # This is normally used by object mappers to convert each returned document to an instance of a class.
160
162
  #
161
163
  # @raise [ArgumentError]
162
164
  # if timeout is set to false and find is not invoked in a block
@@ -175,6 +177,7 @@ module Mongo
175
177
  snapshot = opts.delete(:snapshot)
176
178
  batch_size = opts.delete(:batch_size)
177
179
  timeout = (opts.delete(:timeout) == false) ? false : true
180
+ transformer = opts.delete(:transformer)
178
181
 
179
182
  if timeout == false && !block_given?
180
183
  raise ArgumentError, "Collection#find must be invoked with a block when timeout is disabled."
@@ -188,8 +191,18 @@ module Mongo
188
191
 
189
192
  raise RuntimeError, "Unknown options [#{opts.inspect}]" unless opts.empty?
190
193
 
191
- cursor = Cursor.new(self, :selector => selector, :fields => fields, :skip => skip, :limit => limit,
192
- :order => sort, :hint => hint, :snapshot => snapshot, :timeout => timeout, :batch_size => batch_size)
194
+ cursor = Cursor.new(self, {
195
+ :selector => selector,
196
+ :fields => fields,
197
+ :skip => skip,
198
+ :limit => limit,
199
+ :order => sort,
200
+ :hint => hint,
201
+ :snapshot => snapshot,
202
+ :timeout => timeout,
203
+ :batch_size => batch_size,
204
+ :transformer => transformer,
205
+ })
193
206
 
194
207
  if block_given?
195
208
  yield cursor
@@ -68,6 +68,8 @@ module Mongo
68
68
  # @option opts [Float] :timeout (5.0) When all of the connections a pool are checked out,
69
69
  # this is the number of seconds to wait for a new connection to be released before throwing an exception.
70
70
  # Note: this setting is relevant only for multi-threaded applications (which in Ruby are rare).
71
+ # @option opts [Float] :op_timeout (nil) The number of seconds to wait for a read operation to time out.
72
+ # Disabled by default.
71
73
  #
72
74
  # @example localhost, 27017
73
75
  # Connection.new
@@ -157,6 +159,20 @@ module Mongo
157
159
  end
158
160
  end
159
161
 
162
+ # The host name used for this connection.
163
+ #
164
+ # @return [String]
165
+ def host
166
+ @primary_pool.host
167
+ end
168
+
169
+ # The port used for this connection.
170
+ #
171
+ # @return [Integer]
172
+ def port
173
+ @primary_pool.port
174
+ end
175
+
160
176
  # Fsync, then lock the mongod process against writes. Use this to get
161
177
  # the datafiles in a state safe for snapshotting, backing up, etc.
162
178
  #
@@ -329,13 +345,22 @@ module Mongo
329
345
  self["admin"].command(oh)
330
346
  end
331
347
 
332
- # Get the build information for the current connection.
348
+ # Checks if a server is alive. This command will return immediately
349
+ # even if the server is in a lock.
350
+ #
351
+ # @return [Hash]
352
+ def ping
353
+ self["admin"].command({:ping => 1})
354
+ end
355
+
356
+ # Get the build information for the current connection.
333
357
  #
334
358
  # @return [Hash]
335
359
  def server_info
336
360
  self["admin"].command({:buildinfo => 1})
337
361
  end
338
362
 
363
+
339
364
  # Get the build version of the current server.
340
365
  #
341
366
  # @return [Mongo::ServerVersion]
@@ -450,7 +475,7 @@ module Mongo
450
475
  #
451
476
  # @raise [ConnectionFailure] if unable to connect to any host or port.
452
477
  def connect
453
- reset_connection
478
+ close
454
479
 
455
480
  config = check_is_master(@host_to_try)
456
481
  if config
@@ -482,6 +507,22 @@ module Mongo
482
507
  @primary_pool && @primary_pool.host && @primary_pool.port
483
508
  end
484
509
 
510
+ # Determine if the connection is active. In a normal case the *server_info* operation
511
+ # will be performed without issues, but if the connection was dropped by the server or
512
+ # for some reason the sockets are unsynchronized, a ConnectionFailure will be raised and
513
+ # the return will be false.
514
+ #
515
+ # @return [Boolean]
516
+ def active?
517
+ return false unless connected?
518
+
519
+ ping
520
+ true
521
+
522
+ rescue ConnectionFailure
523
+ false
524
+ end
525
+
485
526
  # Determine whether we're reading from a primary node. If false,
486
527
  # this connection connects to a secondary node and @slave_ok is true.
487
528
  #
@@ -495,6 +536,7 @@ module Mongo
495
536
  def close
496
537
  @primary_pool.close if @primary_pool
497
538
  @primary_pool = nil
539
+ @primary = nil
498
540
  end
499
541
 
500
542
  # Returns the maximum BSON object size as returned by the core server.
@@ -559,6 +601,9 @@ module Mongo
559
601
  @pool_size = opts[:pool_size] || 1
560
602
  @timeout = opts[:timeout] || 5.0
561
603
 
604
+ # Timeout on socket read operation.
605
+ @op_timeout = opts[:op_timeout] || nil
606
+
562
607
  # Mutex for synchronizing pool access
563
608
  @connection_mutex = Mutex.new
564
609
 
@@ -611,7 +656,7 @@ module Mongo
611
656
  msg += payload.values_at(:selector, :document, :documents, :fields ).compact.map(&:inspect).join(', ') + ")"
612
657
  msg += ".skip(#{payload[:skip]})" if payload[:skip]
613
658
  msg += ".limit(#{payload[:limit]})" if payload[:limit]
614
- msg += ".sort(#{payload[:sort]})" if payload[:sort]
659
+ msg += ".sort(#{payload[:order]})" if payload[:order]
615
660
  @logger.debug "MONGODB #{msg}"
616
661
  end
617
662
 
@@ -624,7 +669,6 @@ module Mongo
624
669
  # TODO: evaluate whether this method is actually necessary
625
670
  def reset_connection
626
671
  close
627
- @primary = nil
628
672
  end
629
673
 
630
674
  def check_is_master(node)
@@ -793,20 +837,37 @@ module Mongo
793
837
  # Requires length and an available socket.
794
838
  def receive_message_on_socket(length, socket)
795
839
  begin
796
- message = new_binary_string
797
- socket.read(length, message)
798
- raise ConnectionFailure, "connection closed" unless message && message.length > 0
799
- if message.length < length
800
- chunk = new_binary_string
801
- while message.length < length
802
- socket.read(length - message.length, chunk)
803
- raise ConnectionFailure, "connection closed" unless chunk.length > 0
804
- message << chunk
840
+ if @op_timeout
841
+ message = nil
842
+ Mongo::TimeoutHandler.timeout(@op_timeout, OperationTimeout) do
843
+ message = receive_data(length, socket)
805
844
  end
845
+ else
846
+ message = receive_data(length, socket)
806
847
  end
807
848
  rescue => ex
808
849
  close
809
- raise ConnectionFailure, "Operation failed with the following exception: #{ex}"
850
+
851
+ if ex.class == OperationTimeout
852
+ raise OperationTimeout, "Timed out waiting on socket read."
853
+ else
854
+ raise ConnectionFailure, "Operation failed with the following exception: #{ex}"
855
+ end
856
+ end
857
+ message
858
+ end
859
+
860
+ def receive_data(length, socket)
861
+ message = new_binary_string
862
+ socket.read(length, message)
863
+ raise ConnectionFailure, "connection closed" unless message && message.length > 0
864
+ if message.length < length
865
+ chunk = new_binary_string
866
+ while message.length < length
867
+ socket.read(length - message.length, chunk)
868
+ raise ConnectionFailure, "connection closed" unless chunk.length > 0
869
+ message << chunk
870
+ end
810
871
  end
811
872
  message
812
873
  end
@@ -23,7 +23,7 @@ module Mongo
23
23
 
24
24
  attr_reader :collection, :selector, :fields,
25
25
  :order, :hint, :snapshot, :timeout,
26
- :full_collection_name
26
+ :full_collection_name, :transformer
27
27
 
28
28
  # Create a new cursor.
29
29
  #
@@ -34,6 +34,8 @@ module Mongo
34
34
  #
35
35
  # @core cursors constructor_details
36
36
  def initialize(collection, opts={})
37
+ @cursor_id = nil
38
+
37
39
  @db = collection.db
38
40
  @collection = collection
39
41
  @connection = @db.connection
@@ -52,6 +54,7 @@ module Mongo
52
54
  @tailable = opts[:tailable] || false
53
55
  @closed = false
54
56
  @query_run = false
57
+ @transformer = opts[:transformer]
55
58
  batch_size(opts[:batch_size] || 0)
56
59
 
57
60
  @full_collection_name = "#{@collection.db.name}.#{@collection.name}"
@@ -86,8 +89,13 @@ module Mongo
86
89
  raise OperationFailure, err
87
90
  end
88
91
 
89
- doc
92
+ if @transformer.nil?
93
+ doc
94
+ else
95
+ @transformer.call(doc) if doc
96
+ end
90
97
  end
98
+ alias :next :next_document
91
99
 
92
100
  # Reset this cursor on the server. Cursor options, such as the
93
101
  # query string and the values for skip and limit, are preserved.
@@ -307,8 +315,8 @@ module Mongo
307
315
  def query_options_hash
308
316
  { :selector => @selector,
309
317
  :fields => @fields,
310
- :skip => @skip_num,
311
- :limit => @limit_num,
318
+ :skip => @skip,
319
+ :limit => @limit,
312
320
  :order => @order,
313
321
  :hint => @hint,
314
322
  :snapshot => @snapshot,