mongo 1.2.4 → 1.3.0.rc0

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 (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,