mongo 0.17.1 → 0.18
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.
- data/README.rdoc +69 -47
 - data/Rakefile +26 -0
 - data/lib/mongo.rb +2 -2
 - data/lib/mongo/admin.rb +3 -3
 - data/lib/mongo/collection.rb +51 -29
 - data/lib/mongo/connection.rb +476 -94
 - data/lib/mongo/cursor.rb +19 -17
 - data/lib/mongo/db.rb +52 -324
 - data/lib/mongo/errors.rb +9 -0
 - data/lib/mongo/gridfs/grid_store.rb +1 -1
 - data/lib/mongo/util/conversions.rb +4 -36
 - data/test/replica/count_test.rb +34 -0
 - data/test/replica/insert_test.rb +50 -0
 - data/test/replica/pooled_insert_test.rb +54 -0
 - data/test/replica/query_test.rb +39 -0
 - data/test/test_collection.rb +23 -10
 - data/test/test_connection.rb +19 -20
 - data/test/test_conversions.rb +2 -2
 - data/test/test_cursor.rb +18 -18
 - data/test/test_db.rb +26 -35
 - data/test/test_db_api.rb +9 -30
 - data/test/test_helper.rb +15 -0
 - data/test/test_slave_connection.rb +5 -4
 - data/test/test_threading.rb +2 -2
 - data/test/threading/test_threading_large_pool.rb +90 -0
 - data/test/unit/collection_test.rb +13 -15
 - data/test/unit/connection_test.rb +122 -0
 - data/test/unit/cursor_test.rb +4 -32
 - data/test/unit/db_test.rb +4 -32
 - metadata +8 -2
 
    
        data/README.rdoc
    CHANGED
    
    | 
         @@ -1,34 +1,33 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            = Introduction
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            This is  
     | 
| 
      
 3 
     | 
    
         
            +
            This is the 10gen-supported Ruby driver for MongoDB[http://www.mongodb.org].
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
            Here is a quick code sample. See the  
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
      
 5 
     | 
    
         
            +
            Here is a quick code sample. See the MongoDB Ruby Tutorial
         
     | 
| 
      
 6 
     | 
    
         
            +
            (http://www.mongodb.org/display/DOCS/Ruby+Tutorial) for much more.
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
      
 8 
     | 
    
         
            +
              require 'rubygems'
         
     | 
| 
       8 
9 
     | 
    
         
             
              require 'mongo'
         
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
10 
     | 
    
         
             
              include Mongo
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
              db 
     | 
| 
       13 
     | 
    
         
            -
              coll = db.collection('test')
         
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
              coll.remove
         
     | 
| 
       16 
     | 
    
         
            -
              3.times { |i| coll.insert({'a' => i+1}) }
         
     | 
| 
       17 
     | 
    
         
            -
              puts "There are #{coll.count()} records. Here they are:"
         
     | 
| 
       18 
     | 
    
         
            -
              coll.find().each { |doc| puts doc.inspect }
         
     | 
| 
      
 12 
     | 
    
         
            +
              @db   = Connection.new.db('sample-db')
         
     | 
| 
      
 13 
     | 
    
         
            +
              @coll = db.collection('test')
         
     | 
| 
       19 
14 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
      
 15 
     | 
    
         
            +
              @coll.remove
         
     | 
| 
      
 16 
     | 
    
         
            +
              3.times do |i| 
         
     | 
| 
      
 17 
     | 
    
         
            +
                @coll.insert({'a' => i+1})
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
              puts "There are #{@coll.count()} records. Here they are:"
         
     | 
| 
      
 20 
     | 
    
         
            +
              @coll.find().each { |doc| puts doc.inspect }
         
     | 
| 
       22 
21 
     | 
    
         | 
| 
       23 
22 
     | 
    
         
             
            = Installation
         
     | 
| 
       24 
23 
     | 
    
         | 
| 
       25 
24 
     | 
    
         
             
            The driver's gems are hosted on Gemcutter[http://gemcutter.org]. If you haven't
         
     | 
| 
       26 
     | 
    
         
            -
            installed a gem from Gemcutter before you'll need to set up Gemcutter first
         
     | 
| 
      
 25 
     | 
    
         
            +
            installed a gem from Gemcutter before, you'll need to set up Gemcutter first:
         
     | 
| 
       27 
26 
     | 
    
         | 
| 
       28 
27 
     | 
    
         
             
              $ gem install gemcutter
         
     | 
| 
       29 
28 
     | 
    
         
             
              $ gem tumble
         
     | 
| 
       30 
29 
     | 
    
         | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
      
 30 
     | 
    
         
            +
            Once you've installed Gemcutter, install the mongo gem as follows:
         
     | 
| 
       32 
31 
     | 
    
         | 
| 
       33 
32 
     | 
    
         
             
              $ gem install mongo
         
     | 
| 
       34 
33 
     | 
    
         | 
| 
         @@ -54,11 +53,14 @@ That's all there is to it! 
     | 
|
| 
       54 
53 
     | 
    
         | 
| 
       55 
54 
     | 
    
         
             
            = Examples
         
     | 
| 
       56 
55 
     | 
    
         | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
      
 56 
     | 
    
         
            +
            For extensive examples, see the MongoDB Ruby Tutorial
         
     | 
| 
      
 57 
     | 
    
         
            +
            (http://www.mongodb.org/display/DOCS/Ruby+Tutorial).
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
            Bundled with the dirver are many examples in the "examples" subdirectory. Samples include using
         
     | 
| 
      
 60 
     | 
    
         
            +
            the driver and using the GridFS class GridStore. MongoDB must be running for
         
     | 
| 
       59 
61 
     | 
    
         
             
            these examples to work, of course.
         
     | 
| 
       60 
62 
     | 
    
         | 
| 
       61 
     | 
    
         
            -
            Here's how to start  
     | 
| 
      
 63 
     | 
    
         
            +
            Here's how to start MongoDB and run the "simple.rb" example:
         
     | 
| 
       62 
64 
     | 
    
         | 
| 
       63 
65 
     | 
    
         
             
              $ cd path/to/mongo
         
     | 
| 
       64 
66 
     | 
    
         
             
              $ ./mongod run
         
     | 
| 
         @@ -68,33 +70,15 @@ Here's how to start mongo and run the "simple.rb" example: 
     | 
|
| 
       68 
70 
     | 
    
         | 
| 
       69 
71 
     | 
    
         
             
            See also the test code, especially test/test_db_api.rb.
         
     | 
| 
       70 
72 
     | 
    
         | 
| 
       71 
     | 
    
         
            -
            = The Driver
         
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
            Here is some simple example code:
         
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
              require 'rubygems'        # not required for Ruby 1.9
         
     | 
| 
       76 
     | 
    
         
            -
              require 'mongo'
         
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
              include Mongo
         
     | 
| 
       79 
     | 
    
         
            -
              db = Connection.new.db('my-db-name')
         
     | 
| 
       80 
     | 
    
         
            -
              things = db.collection('things')
         
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
              things.remove
         
     | 
| 
       83 
     | 
    
         
            -
              things.insert('a' => 42)
         
     | 
| 
       84 
     | 
    
         
            -
              things.insert('a' => 99, 'b' => Time.now)
         
     | 
| 
       85 
     | 
    
         
            -
              puts things.count                               # => 2
         
     | 
| 
       86 
     | 
    
         
            -
              puts things.find('a' => 42).next_object.inspect # {"a"=>42}
         
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
73 
     | 
    
         
             
            = GridStore
         
     | 
| 
       90 
74 
     | 
    
         | 
| 
       91 
     | 
    
         
            -
            The GridStore class is a Ruby implementation of  
     | 
| 
       92 
     | 
    
         
            -
            system. An instance of GridStore is like an IO object. See the  
     | 
| 
      
 75 
     | 
    
         
            +
            The GridStore class is a Ruby implementation of MongoDB's GridFS file storage
         
     | 
| 
      
 76 
     | 
    
         
            +
            system. An instance of GridStore is like an IO object. See the RDocs for
         
     | 
| 
       93 
77 
     | 
    
         
             
            details, and see examples/gridfs.rb for code that uses many of the GridStore
         
     | 
| 
       94 
     | 
    
         
            -
            features  
     | 
| 
      
 78 
     | 
    
         
            +
            features (metadata, content type, rewind/seek/tell, etc).
         
     | 
| 
       95 
79 
     | 
    
         | 
| 
       96 
80 
     | 
    
         
             
            Note that the GridStore class is not automatically required when you require
         
     | 
| 
       97 
     | 
    
         
            -
            'mongo'. You need to require 'mongo/gridfs' 
     | 
| 
      
 81 
     | 
    
         
            +
            'mongo'. You also need to require 'mongo/gridfs'
         
     | 
| 
       98 
82 
     | 
    
         | 
| 
       99 
83 
     | 
    
         
             
            Example code:
         
     | 
| 
       100 
84 
     | 
    
         | 
| 
         @@ -112,12 +96,31 @@ Example code: 
     | 
|
| 
       112 
96 
     | 
    
         
             
              }
         
     | 
| 
       113 
97 
     | 
    
         | 
| 
       114 
98 
     | 
    
         | 
| 
       115 
     | 
    
         
            -
             
     | 
| 
       116 
99 
     | 
    
         
             
            = Notes
         
     | 
| 
       117 
100 
     | 
    
         | 
| 
       118 
101 
     | 
    
         
             
            == Thread Safety
         
     | 
| 
       119 
102 
     | 
    
         | 
| 
       120 
     | 
    
         
            -
             
     | 
| 
      
 103 
     | 
    
         
            +
            The driver is thread safe.
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
            == Connection Pooling
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
            As of 0.18, the driver implements connection pooling. By default, only one
         
     | 
| 
      
 108 
     | 
    
         
            +
            socket connection will be opened to MongoDB. However, if you're running a
         
     | 
| 
      
 109 
     | 
    
         
            +
            multi-threaded application, you can specify a maximum pool size and a maximum
         
     | 
| 
      
 110 
     | 
    
         
            +
            timeout for waiting for old connections to be released to the pool.
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
            To set up a pooled connection to a single MongoDB instance:
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
              @conn = Connection.new("localhost", 27017, :pool_size => 5, :timeout => 5)
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
            A pooled connection to a paired instance would look like this:
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
              @conn = Connection.new({:left  => ["db1.example.com", 27017],
         
     | 
| 
      
 119 
     | 
    
         
            +
                                :right => ["db2.example.com", 27017]}, nil,
         
     | 
| 
      
 120 
     | 
    
         
            +
                                :pool_size => 20, :timeout => 5)
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
            Though the pooling architecure will undoubtedly evolve, it owes much credit
         
     | 
| 
      
 123 
     | 
    
         
            +
            to the connection pooling implementations in ActiveRecord and PyMongo.
         
     | 
| 
       121 
124 
     | 
    
         | 
| 
       122 
125 
     | 
    
         
             
            == Using with Phusion Passenger
         
     | 
| 
       123 
126 
     | 
    
         | 
| 
         @@ -255,7 +258,26 @@ If you have the source code, you can run the tests. 
     | 
|
| 
       255 
258 
     | 
    
         | 
| 
       256 
259 
     | 
    
         
             
              $ rake test
         
     | 
| 
       257 
260 
     | 
    
         | 
| 
       258 
     | 
    
         
            -
             
     | 
| 
      
 261 
     | 
    
         
            +
            This will run both unit and functional tests. If you want to run these
         
     | 
| 
      
 262 
     | 
    
         
            +
            individually:
         
     | 
| 
      
 263 
     | 
    
         
            +
             
     | 
| 
      
 264 
     | 
    
         
            +
              $ rake test:unit
         
     | 
| 
      
 265 
     | 
    
         
            +
              $ rake test:functional
         
     | 
| 
      
 266 
     | 
    
         
            +
             
     | 
| 
      
 267 
     | 
    
         
            +
             
     | 
| 
      
 268 
     | 
    
         
            +
            If you want to test replica pairs, you can run the following tests
         
     | 
| 
      
 269 
     | 
    
         
            +
            individually:
         
     | 
| 
      
 270 
     | 
    
         
            +
             
     | 
| 
      
 271 
     | 
    
         
            +
              $ rake test:pair_count
         
     | 
| 
      
 272 
     | 
    
         
            +
              $ rake test:pair_insert
         
     | 
| 
      
 273 
     | 
    
         
            +
              $ rake test:pair_query
         
     | 
| 
      
 274 
     | 
    
         
            +
             
     | 
| 
      
 275 
     | 
    
         
            +
            It's also possible to test replica pairs with connection pooling:
         
     | 
| 
      
 276 
     | 
    
         
            +
             
     | 
| 
      
 277 
     | 
    
         
            +
              $ rake test:pooled_pair_insert
         
     | 
| 
      
 278 
     | 
    
         
            +
             
     | 
| 
      
 279 
     | 
    
         
            +
             
     | 
| 
      
 280 
     | 
    
         
            +
            All tests now require shoulda and mocha.  You can install these gems as
         
     | 
| 
       259 
281 
     | 
    
         
             
            follows:
         
     | 
| 
       260 
282 
     | 
    
         | 
| 
       261 
283 
     | 
    
         
             
              $ gem install shoulda
         
     | 
| 
         @@ -295,7 +317,7 @@ Then open the file html/index.html. 
     | 
|
| 
       295 
317 
     | 
    
         | 
| 
       296 
318 
     | 
    
         
             
            = Release Notes
         
     | 
| 
       297 
319 
     | 
    
         | 
| 
       298 
     | 
    
         
            -
            See  
     | 
| 
      
 320 
     | 
    
         
            +
            See HISTORY.
         
     | 
| 
       299 
321 
     | 
    
         | 
| 
       300 
322 
     | 
    
         
             
            = Credits
         
     | 
| 
       301 
323 
     | 
    
         | 
| 
         @@ -338,9 +360,6 @@ Cyril Mougel, cyril.mougel@gmail.com 
     | 
|
| 
       338 
360 
     | 
    
         
             
            Jack Chen, chendo on github
         
     | 
| 
       339 
361 
     | 
    
         
             
            * Test case + fix for deserializing pre-epoch Time instances
         
     | 
| 
       340 
362 
     | 
    
         | 
| 
       341 
     | 
    
         
            -
            Kyle Banker, banker on github
         
     | 
| 
       342 
     | 
    
         
            -
            * #limit and #skip methods for Cursor instances
         
     | 
| 
       343 
     | 
    
         
            -
             
     | 
| 
       344 
363 
     | 
    
         
             
            Michael Bernstein, mrb on github
         
     | 
| 
       345 
364 
     | 
    
         
             
            * #sort method for Cursor instances
         
     | 
| 
       346 
365 
     | 
    
         | 
| 
         @@ -357,6 +376,9 @@ Sunny Hirai 
     | 
|
| 
       357 
376 
     | 
    
         
             
            * Suggested hashcode fix for Mongo::ObjectID
         
     | 
| 
       358 
377 
     | 
    
         
             
            * Noted index ordering bug.
         
     | 
| 
       359 
378 
     | 
    
         | 
| 
      
 379 
     | 
    
         
            +
            Christos Trochalakis
         
     | 
| 
      
 380 
     | 
    
         
            +
            * Added map/reduce helper
         
     | 
| 
      
 381 
     | 
    
         
            +
             
     | 
| 
       360 
382 
     | 
    
         
             
            = License
         
     | 
| 
       361 
383 
     | 
    
         | 
| 
       362 
384 
     | 
    
         
             
             Copyright 2008-2009 10gen Inc.
         
     | 
    
        data/Rakefile
    CHANGED
    
    | 
         @@ -19,6 +19,7 @@ desc "Test the MongoDB Ruby driver." 
     | 
|
| 
       19 
19 
     | 
    
         
             
            task :test do
         
     | 
| 
       20 
20 
     | 
    
         
             
              Rake::Task['test:unit'].invoke
         
     | 
| 
       21 
21 
     | 
    
         
             
              Rake::Task['test:functional'].invoke
         
     | 
| 
      
 22 
     | 
    
         
            +
              Rake::Task['test:pooled_threading'].invoke
         
     | 
| 
       22 
23 
     | 
    
         
             
            end
         
     | 
| 
       23 
24 
     | 
    
         | 
| 
       24 
25 
     | 
    
         
             
            namespace :test do 
         
     | 
| 
         @@ -31,6 +32,31 @@ namespace :test do 
     | 
|
| 
       31 
32 
     | 
    
         
             
                t.test_files = FileList['test/test*.rb']
         
     | 
| 
       32 
33 
     | 
    
         
             
                t.verbose    = true
         
     | 
| 
       33 
34 
     | 
    
         
             
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              Rake::TestTask.new(:pooled_threading) do |t|
         
     | 
| 
      
 37 
     | 
    
         
            +
                t.test_files = FileList['test/threading/*.rb']
         
     | 
| 
      
 38 
     | 
    
         
            +
                t.verbose    = true
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
              Rake::TestTask.new(:pair_count) do |t|
         
     | 
| 
      
 42 
     | 
    
         
            +
                t.test_files = FileList['test/replica/count_test.rb']
         
     | 
| 
      
 43 
     | 
    
         
            +
                t.verbose    = true
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              Rake::TestTask.new(:pair_insert) do |t|
         
     | 
| 
      
 47 
     | 
    
         
            +
                t.test_files = FileList['test/replica/insert_test.rb']
         
     | 
| 
      
 48 
     | 
    
         
            +
                t.verbose    = true
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
              Rake::TestTask.new(:pooled_pair_insert) do |t|
         
     | 
| 
      
 52 
     | 
    
         
            +
                t.test_files = FileList['test/replica/pooled_insert_test.rb']
         
     | 
| 
      
 53 
     | 
    
         
            +
                t.verbose    = true
         
     | 
| 
      
 54 
     | 
    
         
            +
              end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
              Rake::TestTask.new(:pair_query) do |t|
         
     | 
| 
      
 57 
     | 
    
         
            +
                t.test_files = FileList['test/replica/query_test.rb']
         
     | 
| 
      
 58 
     | 
    
         
            +
                t.verbose    = true
         
     | 
| 
      
 59 
     | 
    
         
            +
              end
         
     | 
| 
       34 
60 
     | 
    
         
             
            end
         
     | 
| 
       35 
61 
     | 
    
         | 
| 
       36 
62 
     | 
    
         
             
            desc "Generate documentation"
         
     | 
    
        data/lib/mongo.rb
    CHANGED
    
    | 
         @@ -1,4 +1,4 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            require 'mongo/types/binary'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'mongo/types/code'
         
     | 
| 
         @@ -31,5 +31,5 @@ module Mongo 
     | 
|
| 
       31 
31 
     | 
    
         
             
              ASCENDING = 1
         
     | 
| 
       32 
32 
     | 
    
         
             
              DESCENDING = -1
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
       34 
     | 
    
         
            -
              VERSION = "0. 
     | 
| 
      
 34 
     | 
    
         
            +
              VERSION = "0.18"
         
     | 
| 
       35 
35 
     | 
    
         
             
            end
         
     | 
    
        data/lib/mongo/admin.rb
    CHANGED
    
    | 
         @@ -30,7 +30,7 @@ module Mongo 
     | 
|
| 
       30 
30 
     | 
    
         
             
                def profiling_level
         
     | 
| 
       31 
31 
     | 
    
         
             
                  oh = OrderedHash.new
         
     | 
| 
       32 
32 
     | 
    
         
             
                  oh[:profile] = -1
         
     | 
| 
       33 
     | 
    
         
            -
                  doc = @db. 
     | 
| 
      
 33 
     | 
    
         
            +
                  doc = @db.command(oh)
         
     | 
| 
       34 
34 
     | 
    
         
             
                  raise "Error with profile command: #{doc.inspect}" unless @db.ok?(doc) && doc['was'].kind_of?(Numeric)
         
     | 
| 
       35 
35 
     | 
    
         
             
                  case doc['was'].to_i
         
     | 
| 
       36 
36 
     | 
    
         
             
                  when 0
         
     | 
| 
         @@ -57,7 +57,7 @@ module Mongo 
     | 
|
| 
       57 
57 
     | 
    
         
             
                                 else
         
     | 
| 
       58 
58 
     | 
    
         
             
                                   raise "Error: illegal profiling level value #{level}"
         
     | 
| 
       59 
59 
     | 
    
         
             
                                 end
         
     | 
| 
       60 
     | 
    
         
            -
                  doc = @db. 
     | 
| 
      
 60 
     | 
    
         
            +
                  doc = @db.command(oh)
         
     | 
| 
       61 
61 
     | 
    
         
             
                  raise "Error with profile command: #{doc.inspect}" unless @db.ok?(doc)
         
     | 
| 
       62 
62 
     | 
    
         
             
                end
         
     | 
| 
       63 
63 
     | 
    
         | 
| 
         @@ -71,7 +71,7 @@ module Mongo 
     | 
|
| 
       71 
71 
     | 
    
         
             
                # problem or returning an interesting hash (see especially the
         
     | 
| 
       72 
72 
     | 
    
         
             
                # 'result' string value) if all is well.
         
     | 
| 
       73 
73 
     | 
    
         
             
                def validate_collection(name)
         
     | 
| 
       74 
     | 
    
         
            -
                  doc = @db. 
     | 
| 
      
 74 
     | 
    
         
            +
                  doc = @db.command(:validate => name)
         
     | 
| 
       75 
75 
     | 
    
         
             
                  raise "Error with validate command: #{doc.inspect}" unless @db.ok?(doc)
         
     | 
| 
       76 
76 
     | 
    
         
             
                  result = doc['result']
         
     | 
| 
       77 
77 
     | 
    
         
             
                  raise "Error with validation data: #{doc.inspect}" unless result.kind_of?(String)
         
     | 
    
        data/lib/mongo/collection.rb
    CHANGED
    
    | 
         @@ -41,6 +41,7 @@ module Mongo 
     | 
|
| 
       41 
41 
     | 
    
         
             
                  end
         
     | 
| 
       42 
42 
     | 
    
         | 
| 
       43 
43 
     | 
    
         
             
                  @db, @name  = db, name
         
     | 
| 
      
 44 
     | 
    
         
            +
                  @connection = @db.connection
         
     | 
| 
       44 
45 
     | 
    
         
             
                  @pk_factory = pk_factory || ObjectID
         
     | 
| 
       45 
46 
     | 
    
         
             
                  @hint = nil
         
     | 
| 
       46 
47 
     | 
    
         
             
                end
         
     | 
| 
         @@ -109,14 +110,10 @@ module Mongo 
     | 
|
| 
       109 
110 
     | 
    
         
             
                def find(selector={}, options={})
         
     | 
| 
       110 
111 
     | 
    
         
             
                  fields = options.delete(:fields)
         
     | 
| 
       111 
112 
     | 
    
         
             
                  fields = ["_id"] if fields && fields.empty?
         
     | 
| 
       112 
     | 
    
         
            -
                  skip 
     | 
| 
       113 
     | 
    
         
            -
                   
     | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
       115 
     | 
    
         
            -
                   
     | 
| 
       116 
     | 
    
         
            -
                  skip = options.delete(:skip) || skip || 0
         
     | 
| 
       117 
     | 
    
         
            -
                  limit = options.delete(:limit) || 0
         
     | 
| 
       118 
     | 
    
         
            -
                  sort = options.delete(:sort)
         
     | 
| 
       119 
     | 
    
         
            -
                  hint = options.delete(:hint)
         
     | 
| 
      
 113 
     | 
    
         
            +
                  skip   = options.delete(:skip) || skip || 0
         
     | 
| 
      
 114 
     | 
    
         
            +
                  limit  = options.delete(:limit) || 0
         
     | 
| 
      
 115 
     | 
    
         
            +
                  sort   = options.delete(:sort)
         
     | 
| 
      
 116 
     | 
    
         
            +
                  hint   = options.delete(:hint)
         
     | 
| 
       120 
117 
     | 
    
         
             
                  snapshot = options.delete(:snapshot)
         
     | 
| 
       121 
118 
     | 
    
         
             
                  if options[:timeout] == false && !block_given?
         
     | 
| 
       122 
119 
     | 
    
         
             
                    raise ArgumentError, "Timeout can be set to false only when #find is invoked with a block." 
         
     | 
| 
         @@ -222,17 +219,10 @@ module Mongo 
     | 
|
| 
       222 
219 
     | 
    
         
             
                  BSON.serialize_cstr(message, "#{@db.name}.#{@name}")
         
     | 
| 
       223 
220 
     | 
    
         
             
                  message.put_int(0)
         
     | 
| 
       224 
221 
     | 
    
         
             
                  message.put_array(BSON_SERIALIZER.serialize(selector, false).unpack("C*"))
         
     | 
| 
       225 
     | 
    
         
            -
                  @ 
     | 
| 
      
 222 
     | 
    
         
            +
                  @connection.send_message(Mongo::Constants::OP_DELETE, message,
         
     | 
| 
       226 
223 
     | 
    
         
             
                    "db.#{@db.name}.remove(#{selector.inspect})")
         
     | 
| 
       227 
224 
     | 
    
         
             
                end
         
     | 
| 
       228 
225 
     | 
    
         | 
| 
       229 
     | 
    
         
            -
                # Remove all records.
         
     | 
| 
       230 
     | 
    
         
            -
                # DEPRECATED: please use Collection#remove instead.
         
     | 
| 
       231 
     | 
    
         
            -
                def clear
         
     | 
| 
       232 
     | 
    
         
            -
                  warn "Collection#clear is deprecated. Please use Collection#remove instead."
         
     | 
| 
       233 
     | 
    
         
            -
                  remove({})
         
     | 
| 
       234 
     | 
    
         
            -
                end
         
     | 
| 
       235 
     | 
    
         
            -
             
     | 
| 
       236 
226 
     | 
    
         
             
                # Update a single document in this collection.
         
     | 
| 
       237 
227 
     | 
    
         
             
                #
         
     | 
| 
       238 
228 
     | 
    
         
             
                # :selector :: a hash specifying elements which must be present for a document to be updated. Note: 
         
     | 
| 
         @@ -261,10 +251,10 @@ module Mongo 
     | 
|
| 
       261 
251 
     | 
    
         
             
                  message.put_array(BSON_SERIALIZER.serialize(selector, false).unpack("C*"))
         
     | 
| 
       262 
252 
     | 
    
         
             
                  message.put_array(BSON_SERIALIZER.serialize(document, false).unpack("C*"))
         
     | 
| 
       263 
253 
     | 
    
         
             
                  if options[:safe]
         
     | 
| 
       264 
     | 
    
         
            -
                    @ 
     | 
| 
      
 254 
     | 
    
         
            +
                    @connection.send_message_with_safe_check(Mongo::Constants::OP_UPDATE, message, @db.name,
         
     | 
| 
       265 
255 
     | 
    
         
             
                      "db.#{@name}.update(#{selector.inspect}, #{document.inspect})")
         
     | 
| 
       266 
256 
     | 
    
         
             
                  else
         
     | 
| 
       267 
     | 
    
         
            -
                    @ 
     | 
| 
      
 257 
     | 
    
         
            +
                    @connection.send_message(Mongo::Constants::OP_UPDATE, message, 
         
     | 
| 
       268 
258 
     | 
    
         
             
                      "db.#{@name}.update(#{selector.inspect}, #{document.inspect})")
         
     | 
| 
       269 
259 
     | 
    
         
             
                  end
         
     | 
| 
       270 
260 
     | 
    
         
             
                end
         
     | 
| 
         @@ -308,8 +298,44 @@ module Mongo 
     | 
|
| 
       308 
298 
     | 
    
         
             
                  @db.drop_collection(@name)
         
     | 
| 
       309 
299 
     | 
    
         
             
                end
         
     | 
| 
       310 
300 
     | 
    
         | 
| 
       311 
     | 
    
         
            -
                #  
     | 
| 
      
 301 
     | 
    
         
            +
                # Performs a map/reduce operation on the current collection. Returns a new
         
     | 
| 
      
 302 
     | 
    
         
            +
                # collection containing the results of the operation.
         
     | 
| 
      
 303 
     | 
    
         
            +
                #
         
     | 
| 
      
 304 
     | 
    
         
            +
                # Required:
         
     | 
| 
      
 305 
     | 
    
         
            +
                # +map+    :: a map function, written in javascript.
         
     | 
| 
      
 306 
     | 
    
         
            +
                # +reduce+ :: a reduce function, written in javascript.
         
     | 
| 
      
 307 
     | 
    
         
            +
                #
         
     | 
| 
      
 308 
     | 
    
         
            +
                # Optional:
         
     | 
| 
      
 309 
     | 
    
         
            +
                # :query    :: a query selector document, like what's passed to #find, to limit
         
     | 
| 
      
 310 
     | 
    
         
            +
                #              the operation to a subset of the collection.
         
     | 
| 
      
 311 
     | 
    
         
            +
                # :sort     :: sort parameters passed to the query.
         
     | 
| 
      
 312 
     | 
    
         
            +
                # :limit    :: number of objects to return from the collection.
         
     | 
| 
      
 313 
     | 
    
         
            +
                # :finalize :: a javascript function to apply to the result set after the
         
     | 
| 
      
 314 
     | 
    
         
            +
                #              map/reduce operation has finished.
         
     | 
| 
      
 315 
     | 
    
         
            +
                # :out      :: the name of the output collection. if specified, the collection will not be treated as temporary.
         
     | 
| 
      
 316 
     | 
    
         
            +
                # :keeptemp :: if true, the generated collection will be persisted. default is false.
         
     | 
| 
      
 317 
     | 
    
         
            +
                # :verbose  :: if true, provides statistics on job execution time.
         
     | 
| 
       312 
318 
     | 
    
         
             
                #
         
     | 
| 
      
 319 
     | 
    
         
            +
                # For more information on using map/reduce, see http://www.mongodb.org/display/DOCS/MapReduce
         
     | 
| 
      
 320 
     | 
    
         
            +
                def map_reduce(map, reduce, options={})
         
     | 
| 
      
 321 
     | 
    
         
            +
                  map    = Code.new(map) unless map.is_a?(Code)
         
     | 
| 
      
 322 
     | 
    
         
            +
                  reduce = Code.new(reduce) unless reduce.is_a?(Code)
         
     | 
| 
      
 323 
     | 
    
         
            +
             
     | 
| 
      
 324 
     | 
    
         
            +
                  hash = OrderedHash.new
         
     | 
| 
      
 325 
     | 
    
         
            +
                  hash['mapreduce'] = self.name
         
     | 
| 
      
 326 
     | 
    
         
            +
                  hash['map'] = map
         
     | 
| 
      
 327 
     | 
    
         
            +
                  hash['reduce'] = reduce
         
     | 
| 
      
 328 
     | 
    
         
            +
                  hash.merge! options
         
     | 
| 
      
 329 
     | 
    
         
            +
             
     | 
| 
      
 330 
     | 
    
         
            +
                  result = @db.command(hash)
         
     | 
| 
      
 331 
     | 
    
         
            +
                  unless result["ok"] == 1
         
     | 
| 
      
 332 
     | 
    
         
            +
                    raise Mongo::OperationFailure, "map-reduce failed: #{result['errmsg']}" 
         
     | 
| 
      
 333 
     | 
    
         
            +
                  end
         
     | 
| 
      
 334 
     | 
    
         
            +
                  @db[result["result"]]
         
     | 
| 
      
 335 
     | 
    
         
            +
                end
         
     | 
| 
      
 336 
     | 
    
         
            +
                alias :mapreduce :map_reduce
         
     | 
| 
      
 337 
     | 
    
         
            +
             
     | 
| 
      
 338 
     | 
    
         
            +
                # Performs a group query, similar to the 'SQL GROUP BY' operation.
         
     | 
| 
       313 
339 
     | 
    
         
             
                # Returns an array of grouped items.
         
     | 
| 
       314 
340 
     | 
    
         
             
                #
         
     | 
| 
       315 
341 
     | 
    
         
             
                # :keys :: Array of fields to group by
         
     | 
| 
         @@ -327,13 +353,9 @@ module Mongo 
     | 
|
| 
       327 
353 
     | 
    
         
             
                      hash[k] = 1
         
     | 
| 
       328 
354 
     | 
    
         
             
                    end
         
     | 
| 
       329 
355 
     | 
    
         | 
| 
       330 
     | 
    
         
            -
                     
     | 
| 
       331 
     | 
    
         
            -
                    when Code
         
     | 
| 
       332 
     | 
    
         
            -
                    else
         
     | 
| 
       333 
     | 
    
         
            -
                      reduce = Code.new(reduce)
         
     | 
| 
       334 
     | 
    
         
            -
                    end
         
     | 
| 
      
 356 
     | 
    
         
            +
                    reduce = Code.new(reduce) unless reduce.is_a?(Code)
         
     | 
| 
       335 
357 
     | 
    
         | 
| 
       336 
     | 
    
         
            -
                    result = @db. 
     | 
| 
      
 358 
     | 
    
         
            +
                    result = @db.command({"group" =>
         
     | 
| 
       337 
359 
     | 
    
         
             
                                              {
         
     | 
| 
       338 
360 
     | 
    
         
             
                                                "ns" => @name,
         
     | 
| 
       339 
361 
     | 
    
         
             
                                                "$reduce" => reduce,
         
     | 
| 
         @@ -384,7 +406,7 @@ function () { 
     | 
|
| 
       384 
406 
     | 
    
         
             
                return {"result": map.values()};
         
     | 
| 
       385 
407 
     | 
    
         
             
            }
         
     | 
| 
       386 
408 
     | 
    
         
             
            EOS
         
     | 
| 
       387 
     | 
    
         
            -
                   
     | 
| 
      
 409 
     | 
    
         
            +
                  @db.eval(Code.new(group_function, scope))["result"]
         
     | 
| 
       388 
410 
     | 
    
         
             
                end
         
     | 
| 
       389 
411 
     | 
    
         | 
| 
       390 
412 
     | 
    
         
             
                # Returns a list of distinct values for +key+ across all
         
     | 
| 
         @@ -406,7 +428,7 @@ EOS 
     | 
|
| 
       406 
428 
     | 
    
         
             
                  command[:distinct] = @name
         
     | 
| 
       407 
429 
     | 
    
         
             
                  command[:key]        = key.to_s
         
     | 
| 
       408 
430 
     | 
    
         | 
| 
       409 
     | 
    
         
            -
                  @db. 
     | 
| 
      
 431 
     | 
    
         
            +
                  @db.command(command)["values"]
         
     | 
| 
       410 
432 
     | 
    
         
             
                end
         
     | 
| 
       411 
433 
     | 
    
         | 
| 
       412 
434 
     | 
    
         
             
                # Rename this collection.
         
     | 
| 
         @@ -488,10 +510,10 @@ EOS 
     | 
|
| 
       488 
510 
     | 
    
         
             
                  BSON.serialize_cstr(message, "#{@db.name}.#{collection_name}")
         
     | 
| 
       489 
511 
     | 
    
         
             
                  documents.each { |doc| message.put_array(BSON_SERIALIZER.serialize(doc, check_keys).unpack("C*")) }
         
     | 
| 
       490 
512 
     | 
    
         
             
                  if safe
         
     | 
| 
       491 
     | 
    
         
            -
                    @ 
     | 
| 
      
 513 
     | 
    
         
            +
                    @connection.send_message_with_safe_check(Mongo::Constants::OP_INSERT, message, @db.name,
         
     | 
| 
       492 
514 
     | 
    
         
             
                      "db.#{collection_name}.insert(#{documents.inspect})")
         
     | 
| 
       493 
515 
     | 
    
         
             
                  else
         
     | 
| 
       494 
     | 
    
         
            -
                    @ 
     | 
| 
      
 516 
     | 
    
         
            +
                    @connection.send_message(Mongo::Constants::OP_INSERT, message,
         
     | 
| 
       495 
517 
     | 
    
         
             
                      "db.#{collection_name}.insert(#{documents.inspect})")
         
     | 
| 
       496 
518 
     | 
    
         
             
                  end
         
     | 
| 
       497 
519 
     | 
    
         
             
                  documents.collect { |o| o[:_id] || o['_id'] }
         
     | 
    
        data/lib/mongo/connection.rb
    CHANGED
    
    | 
         @@ -14,98 +14,132 @@ 
     | 
|
| 
       14 
14 
     | 
    
         
             
            # limitations under the License.
         
     | 
| 
       15 
15 
     | 
    
         
             
            # ++
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
      
 17 
     | 
    
         
            +
            require 'set'
         
     | 
| 
      
 18 
     | 
    
         
            +
            require 'socket'
         
     | 
| 
      
 19 
     | 
    
         
            +
            require 'monitor'
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
       17 
21 
     | 
    
         
             
            module Mongo
         
     | 
| 
       18 
22 
     | 
    
         | 
| 
       19 
23 
     | 
    
         
             
              # A connection to MongoDB.
         
     | 
| 
       20 
24 
     | 
    
         
             
              class Connection
         
     | 
| 
       21 
25 
     | 
    
         | 
| 
      
 26 
     | 
    
         
            +
                # We need to make sure that all connection abort when
         
     | 
| 
      
 27 
     | 
    
         
            +
                # a ConnectionError is raised.
         
     | 
| 
      
 28 
     | 
    
         
            +
                Thread.abort_on_exception = true
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
       22 
30 
     | 
    
         
             
                DEFAULT_PORT = 27017
         
     | 
| 
      
 31 
     | 
    
         
            +
                STANDARD_HEADER_SIZE = 16
         
     | 
| 
      
 32 
     | 
    
         
            +
                RESPONSE_HEADER_SIZE = 20
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                attr_reader :logger, :size, :host, :port, :nodes, :sockets, :checked_out, :reserved_connections
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def slave_ok? 
         
     | 
| 
      
 37 
     | 
    
         
            +
                  @slave_ok
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
                
         
     | 
| 
      
 40 
     | 
    
         
            +
                # Counter for generating unique request ids.
         
     | 
| 
      
 41 
     | 
    
         
            +
                @@current_request_id = 0
         
     | 
| 
       23 
42 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
                #  
     | 
| 
       25 
     | 
    
         
            -
                #  
     | 
| 
       26 
     | 
    
         
            -
                # 
     | 
| 
       27 
     | 
    
         
            -
                #  
     | 
| 
       28 
     | 
    
         
            -
                #
         
     | 
| 
       29 
     | 
    
         
            -
                #  
     | 
| 
       30 
     | 
    
         
            -
                # and  
     | 
| 
       31 
     | 
    
         
            -
                # 
     | 
| 
       32 
     | 
    
         
            -
                #  
     | 
| 
       33 
     | 
    
         
            -
                # *  
     | 
| 
       34 
     | 
    
         
            -
                #
         
     | 
| 
       35 
     | 
    
         
            -
                #  
     | 
| 
       36 
     | 
    
         
            -
                #
         
     | 
| 
       37 
     | 
    
         
            -
                #  
     | 
| 
       38 
     | 
    
         
            -
                # 
     | 
| 
       39 
     | 
    
         
            -
                # 
     | 
| 
       40 
     | 
    
         
            -
                #               
     | 
| 
       41 
     | 
    
         
            -
                #
         
     | 
| 
       42 
     | 
    
         
            -
                # :auto_reconnect :: If a DB connection gets closed (for example, we
         
     | 
| 
       43 
     | 
    
         
            -
                #                    have a server pair and saw the "not master"
         
     | 
| 
       44 
     | 
    
         
            -
                #                    error, which closes the connection), then
         
     | 
| 
       45 
     | 
    
         
            -
                #                    automatically try to reconnect to the master or
         
     | 
| 
       46 
     | 
    
         
            -
                #                    to the single server we have been given. Defaults
         
     | 
| 
       47 
     | 
    
         
            -
                #                    to +false+.
         
     | 
| 
      
 43 
     | 
    
         
            +
                # Creates a connection to MongoDB. Specify either one or a pair of servers,
         
     | 
| 
      
 44 
     | 
    
         
            +
                # along with a maximum connection pool size and timeout.
         
     | 
| 
      
 45 
     | 
    
         
            +
                #
         
     | 
| 
      
 46 
     | 
    
         
            +
                # == Connecting
         
     | 
| 
      
 47 
     | 
    
         
            +
                # If connecting to just one server, you may specify whether connection to slave is permitted.
         
     | 
| 
      
 48 
     | 
    
         
            +
                # 
         
     | 
| 
      
 49 
     | 
    
         
            +
                # In all cases, the default host is "localhost" and the default port, is 27017.
         
     | 
| 
      
 50 
     | 
    
         
            +
                #
         
     | 
| 
      
 51 
     | 
    
         
            +
                # When specifying a pair, pair_or_host, is a hash with two keys: :left and :right. Each key maps to either
         
     | 
| 
      
 52 
     | 
    
         
            +
                # * a server name, in which case port is 27017,
         
     | 
| 
      
 53 
     | 
    
         
            +
                # * a port number, in which case the server is "localhost", or
         
     | 
| 
      
 54 
     | 
    
         
            +
                # * an array containing [server_name, port_number]
         
     | 
| 
      
 55 
     | 
    
         
            +
                #
         
     | 
| 
      
 56 
     | 
    
         
            +
                # === Options
         
     | 
| 
      
 57 
     | 
    
         
            +
                #
         
     | 
| 
      
 58 
     | 
    
         
            +
                # :slave_ok :: Defaults to +false+. Must be set to +true+ when connecting
         
     | 
| 
      
 59 
     | 
    
         
            +
                #              to a single, slave node.
         
     | 
| 
      
 60 
     | 
    
         
            +
                #
         
     | 
| 
       48 
61 
     | 
    
         
             
                # :logger :: Optional Logger instance to which driver usage information
         
     | 
| 
       49 
62 
     | 
    
         
             
                #            will be logged.
         
     | 
| 
       50 
63 
     | 
    
         
             
                #
         
     | 
| 
       51 
     | 
    
         
            -
                #  
     | 
| 
      
 64 
     | 
    
         
            +
                # :auto_reconnect :: DEPRECATED. See http://www.mongodb.org/display/DOCS/Replica+Pairs+in+Ruby
         
     | 
| 
       52 
65 
     | 
    
         
             
                #
         
     | 
| 
       53 
     | 
    
         
            -
                # 
     | 
| 
       54 
     | 
    
         
            -
                # 
     | 
| 
       55 
     | 
    
         
            -
                # 
     | 
| 
       56 
     | 
    
         
            -
                # 
     | 
| 
       57 
     | 
    
         
            -
                # 
     | 
| 
       58 
     | 
    
         
            -
                # 
     | 
| 
       59 
     | 
    
         
            -
                # 
     | 
| 
      
 66 
     | 
    
         
            +
                # :pool_size :: The maximum number of socket connections that can be opened
         
     | 
| 
      
 67 
     | 
    
         
            +
                #               that can be opened to the database.
         
     | 
| 
      
 68 
     | 
    
         
            +
                #
         
     | 
| 
      
 69 
     | 
    
         
            +
                # :timeout   :: When all of the connections to the pool are checked out,
         
     | 
| 
      
 70 
     | 
    
         
            +
                #               this is the number of seconds to wait for a new connection
         
     | 
| 
      
 71 
     | 
    
         
            +
                #               to be released before throwing an exception.
         
     | 
| 
      
 72 
     | 
    
         
            +
                #                
         
     | 
| 
      
 73 
     | 
    
         
            +
                #
         
     | 
| 
      
 74 
     | 
    
         
            +
                # === Examples:
         
     | 
| 
      
 75 
     | 
    
         
            +
                #
         
     | 
| 
      
 76 
     | 
    
         
            +
                #  # localhost, 27017
         
     | 
| 
      
 77 
     | 
    
         
            +
                #  Connection.new
         
     | 
| 
      
 78 
     | 
    
         
            +
                #   
         
     | 
| 
      
 79 
     | 
    
         
            +
                #  # localhost, 27017
         
     | 
| 
      
 80 
     | 
    
         
            +
                #  Connection.new("localhost")
         
     | 
| 
      
 81 
     | 
    
         
            +
                #
         
     | 
| 
      
 82 
     | 
    
         
            +
                #  # localhost, 3000, max 5 connections, with max 5 seconds of wait time.
         
     | 
| 
      
 83 
     | 
    
         
            +
                #  Connection.new("localhost", 3000, :pool_size => 5, :timeout => 5)
         
     | 
| 
       60 
84 
     | 
    
         
             
                #
         
     | 
| 
       61 
     | 
    
         
            -
                #  #  
     | 
| 
       62 
     | 
    
         
            -
                #   
     | 
| 
       63 
     | 
    
         
            -
                #  # current master.
         
     | 
| 
       64 
     | 
    
         
            -
                #  Connection.new({:left  => ["db1.example.com", 3000],
         
     | 
| 
       65 
     | 
    
         
            -
                #             :right => "db2.example.com"}, # DEFAULT_PORT
         
     | 
| 
       66 
     | 
    
         
            -
                #            nil, :auto_reconnect => true)
         
     | 
| 
      
 85 
     | 
    
         
            +
                #  # localhost, 3000, where this node may be a slave
         
     | 
| 
      
 86 
     | 
    
         
            +
                #  Connection.new("localhost", 3000, :slave_ok => true)
         
     | 
| 
       67 
87 
     | 
    
         
             
                #
         
     | 
| 
       68 
     | 
    
         
            -
                #  #  
     | 
| 
       69 
     | 
    
         
            -
                #   
     | 
| 
      
 88 
     | 
    
         
            +
                #  # A pair of servers. The driver will always talk to master. 
         
     | 
| 
      
 89 
     | 
    
         
            +
                #  # On connection errors, Mongo::ConnectionFailure will be raised.
         
     | 
| 
      
 90 
     | 
    
         
            +
                #  # See http://www.mongodb.org/display/DOCS/Replica+Pairs+in+Ruby 
         
     | 
| 
      
 91 
     | 
    
         
            +
                #  Connection.new({:left  => ["db1.example.com", 27017],
         
     | 
| 
      
 92 
     | 
    
         
            +
                #                  :right => ["db2.example.com", 27017]})
         
     | 
| 
       70 
93 
     | 
    
         
             
                #
         
     | 
| 
       71 
     | 
    
         
            -
                #  
     | 
| 
       72 
     | 
    
         
            -
                #  
     | 
| 
      
 94 
     | 
    
         
            +
                #  A pair of servers, with connection pooling. Not the nil param placeholder for port.
         
     | 
| 
      
 95 
     | 
    
         
            +
                #  Connection.new({:left  => ["db1.example.com", 27017],
         
     | 
| 
      
 96 
     | 
    
         
            +
                #                  :right => ["db2.example.com", 27017]}, nil,
         
     | 
| 
      
 97 
     | 
    
         
            +
                #                  :pool_size => 20, :timeout => 5)
         
     | 
| 
       73 
98 
     | 
    
         
             
                def initialize(pair_or_host=nil, port=nil, options={})
         
     | 
| 
       74 
     | 
    
         
            -
                  @ 
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
             
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
             
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
       84 
     | 
    
         
            -
             
     | 
| 
       85 
     | 
    
         
            -
             
     | 
| 
       86 
     | 
    
         
            -
                   
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
             
     | 
| 
       90 
     | 
    
         
            -
             
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
       93 
     | 
    
         
            -
                   
     | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
       95 
     | 
    
         
            -
             
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
       97 
     | 
    
         
            -
             
     | 
| 
      
 99 
     | 
    
         
            +
                  @nodes = format_pair(pair_or_host)
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                  # Host and port of current master.
         
     | 
| 
      
 102 
     | 
    
         
            +
                  @host = @port = nil
         
     | 
| 
      
 103 
     | 
    
         
            +
                  
         
     | 
| 
      
 104 
     | 
    
         
            +
                  # Lock for request ids.
         
     | 
| 
      
 105 
     | 
    
         
            +
                  @id_lock = Mutex.new
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                  # Pool size and timeout.
         
     | 
| 
      
 108 
     | 
    
         
            +
                  @size      = options[:pool_size] || 1
         
     | 
| 
      
 109 
     | 
    
         
            +
                  @timeout   = options[:timeout]   || 1.0
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                  # Cache of reserved sockets mapped to threads
         
     | 
| 
      
 112 
     | 
    
         
            +
                  @reserved_connections = {}
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                  # Mutex for synchronizing pool access
         
     | 
| 
      
 115 
     | 
    
         
            +
                  @connection_mutex = Monitor.new
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                  # Condition variable for signal and wait
         
     | 
| 
      
 118 
     | 
    
         
            +
                  @queue = @connection_mutex.new_cond
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                  @sockets      = []
         
     | 
| 
      
 121 
     | 
    
         
            +
                  @checked_out  = []
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                  if options[:auto_reconnect]
         
     | 
| 
      
 124 
     | 
    
         
            +
                    warn(":auto_reconnect is deprecated. see http://www.mongodb.org/display/DOCS/Replica+Pairs+in+Ruby")
         
     | 
| 
      
 125 
     | 
    
         
            +
                  end
         
     | 
| 
      
 126 
     | 
    
         
            +
                  
         
     | 
| 
      
 127 
     | 
    
         
            +
                  # Slave ok can be true only if one node is specified
         
     | 
| 
      
 128 
     | 
    
         
            +
                  @slave_ok = options[:slave_ok] && @nodes.length == 1
         
     | 
| 
      
 129 
     | 
    
         
            +
                  @logger   = options[:logger] || nil
         
     | 
| 
      
 130 
     | 
    
         
            +
                  @options  = options
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                  should_connect = options[:connect].nil? ? true : options[:connect]
         
     | 
| 
      
 133 
     | 
    
         
            +
                  connect_to_master if should_connect
         
     | 
| 
       98 
134 
     | 
    
         
             
                end
         
     | 
| 
       99 
135 
     | 
    
         | 
| 
       100 
     | 
    
         
            -
                # Returns a hash  
     | 
| 
       101 
     | 
    
         
            -
                #  
     | 
| 
      
 136 
     | 
    
         
            +
                # Returns a hash with all database names and their respective sizes on
         
     | 
| 
      
 137 
     | 
    
         
            +
                # disk.
         
     | 
| 
       102 
138 
     | 
    
         
             
                def database_info
         
     | 
| 
       103 
     | 
    
         
            -
                  doc =  
     | 
| 
       104 
     | 
    
         
            -
                   
     | 
| 
       105 
     | 
    
         
            -
             
     | 
| 
       106 
     | 
    
         
            -
             
     | 
| 
       107 
     | 
    
         
            -
                  }
         
     | 
| 
       108 
     | 
    
         
            -
                  h
         
     | 
| 
      
 139 
     | 
    
         
            +
                  doc = self['admin'].command(:listDatabases => 1)
         
     | 
| 
      
 140 
     | 
    
         
            +
                  returning({}) do |info|
         
     | 
| 
      
 141 
     | 
    
         
            +
                    doc['databases'].each { |db| info[db['name']] = db['sizeOnDisk'].to_i }
         
     | 
| 
      
 142 
     | 
    
         
            +
                  end
         
     | 
| 
       109 
143 
     | 
    
         
             
                end
         
     | 
| 
       110 
144 
     | 
    
         | 
| 
       111 
145 
     | 
    
         
             
                # Returns an array of database names.
         
     | 
| 
         @@ -113,9 +147,20 @@ module Mongo 
     | 
|
| 
       113 
147 
     | 
    
         
             
                  database_info.keys
         
     | 
| 
       114 
148 
     | 
    
         
             
                end
         
     | 
| 
       115 
149 
     | 
    
         | 
| 
      
 150 
     | 
    
         
            +
                # Returns the database named +db_name+. The slave_ok and
         
     | 
| 
      
 151 
     | 
    
         
            +
                # See DB#new for other options you can pass in.
         
     | 
| 
      
 152 
     | 
    
         
            +
                def db(db_name, options={})
         
     | 
| 
      
 153 
     | 
    
         
            +
                  DB.new(db_name, self, options.merge(:logger => @logger))
         
     | 
| 
      
 154 
     | 
    
         
            +
                end
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
                # Returns the database named +db_name+.
         
     | 
| 
      
 157 
     | 
    
         
            +
                def [](db_name)
         
     | 
| 
      
 158 
     | 
    
         
            +
                  DB.new(db_name, self, :logger => @logger)
         
     | 
| 
      
 159 
     | 
    
         
            +
                end
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
       116 
161 
     | 
    
         
             
                # Drops the database +name+.
         
     | 
| 
       117 
162 
     | 
    
         
             
                def drop_database(name)
         
     | 
| 
       118 
     | 
    
         
            -
                   
     | 
| 
      
 163 
     | 
    
         
            +
                  self[name].command(:dropDatabase => 1)
         
     | 
| 
       119 
164 
     | 
    
         
             
                end
         
     | 
| 
       120 
165 
     | 
    
         | 
| 
       121 
166 
     | 
    
         
             
                # Copies the database +from+ on the local server to +to+ on the specified +host+.
         
     | 
| 
         @@ -126,22 +171,373 @@ module Mongo 
     | 
|
| 
       126 
171 
     | 
    
         
             
                  oh[:fromhost] = host
         
     | 
| 
       127 
172 
     | 
    
         
             
                  oh[:fromdb]   = from
         
     | 
| 
       128 
173 
     | 
    
         
             
                  oh[:todb]     = to
         
     | 
| 
       129 
     | 
    
         
            -
                   
     | 
| 
      
 174 
     | 
    
         
            +
                  self["admin"].command(oh)
         
     | 
| 
      
 175 
     | 
    
         
            +
                end
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                # Increments and returns the next available request id.
         
     | 
| 
      
 178 
     | 
    
         
            +
                def get_request_id
         
     | 
| 
      
 179 
     | 
    
         
            +
                  request_id = ''
         
     | 
| 
      
 180 
     | 
    
         
            +
                  @id_lock.synchronize do 
         
     | 
| 
      
 181 
     | 
    
         
            +
                    request_id = @@current_request_id += 1
         
     | 
| 
      
 182 
     | 
    
         
            +
                  end
         
     | 
| 
      
 183 
     | 
    
         
            +
                  request_id
         
     | 
| 
       130 
184 
     | 
    
         
             
                end
         
     | 
| 
       131 
185 
     | 
    
         | 
| 
       132 
     | 
    
         
            -
                #  
     | 
| 
      
 186 
     | 
    
         
            +
                # Returns the build information for the current connection.
         
     | 
| 
       133 
187 
     | 
    
         
             
                def server_info
         
     | 
| 
       134 
188 
     | 
    
         
             
                  db("admin").command({:buildinfo => 1}, {:admin => true, :check_response => true})
         
     | 
| 
       135 
189 
     | 
    
         
             
                end
         
     | 
| 
       136 
190 
     | 
    
         | 
| 
       137 
     | 
    
         
            -
                #  
     | 
| 
       138 
     | 
    
         
            -
                # a ServerVersion object for comparability.
         
     | 
| 
      
 191 
     | 
    
         
            +
                # Gets the build version of the current server.
         
     | 
| 
      
 192 
     | 
    
         
            +
                # Returns a ServerVersion object for comparability.
         
     | 
| 
       139 
193 
     | 
    
         
             
                def server_version
         
     | 
| 
       140 
194 
     | 
    
         
             
                  ServerVersion.new(server_info["version"])
         
     | 
| 
       141 
195 
     | 
    
         
             
                end
         
     | 
| 
       142 
196 
     | 
    
         | 
| 
       143 
     | 
    
         
            -
                protected
         
     | 
| 
       144 
197 
     | 
    
         | 
| 
      
 198 
     | 
    
         
            +
                ## Connections and pooling ##
         
     | 
| 
      
 199 
     | 
    
         
            +
                
         
     | 
| 
      
 200 
     | 
    
         
            +
                # Sends a message to MongoDB.
         
     | 
| 
      
 201 
     | 
    
         
            +
                #
         
     | 
| 
      
 202 
     | 
    
         
            +
                # Takes a MongoDB opcode, +operation+, a message of class ByteBuffer,
         
     | 
| 
      
 203 
     | 
    
         
            +
                # +message+, and an optional formatted +log_message+.
         
     | 
| 
      
 204 
     | 
    
         
            +
                # Sends the message to the databse, adding the necessary headers.
         
     | 
| 
      
 205 
     | 
    
         
            +
                def send_message(operation, message, log_message=nil)
         
     | 
| 
      
 206 
     | 
    
         
            +
                  @logger.debug("  MONGODB #{log_message || message}") if @logger
         
     | 
| 
      
 207 
     | 
    
         
            +
                  packed_message = add_message_headers(operation, message).to_s
         
     | 
| 
      
 208 
     | 
    
         
            +
                  socket = checkout
         
     | 
| 
      
 209 
     | 
    
         
            +
                  send_message_on_socket(packed_message, socket)
         
     | 
| 
      
 210 
     | 
    
         
            +
                  checkin(socket)
         
     | 
| 
      
 211 
     | 
    
         
            +
                end
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
                # Sends a message to the database, waits for a response, and raises
         
     | 
| 
      
 214 
     | 
    
         
            +
                # and exception if the operation has failed.
         
     | 
| 
      
 215 
     | 
    
         
            +
                #
         
     | 
| 
      
 216 
     | 
    
         
            +
                # Takes a MongoDB opcode, +operation+, a message of class ByteBuffer,
         
     | 
| 
      
 217 
     | 
    
         
            +
                # +message+, the +db_name+, and an optional formatted +log_message+.
         
     | 
| 
      
 218 
     | 
    
         
            +
                # Sends the message to the databse, adding the necessary headers.
         
     | 
| 
      
 219 
     | 
    
         
            +
                def send_message_with_safe_check(operation, message, db_name, log_message=nil)
         
     | 
| 
      
 220 
     | 
    
         
            +
                  message_with_headers = add_message_headers(operation, message)
         
     | 
| 
      
 221 
     | 
    
         
            +
                  message_with_check   = last_error_message(db_name)
         
     | 
| 
      
 222 
     | 
    
         
            +
                  @logger.debug("  MONGODB #{log_message || message}") if @logger
         
     | 
| 
      
 223 
     | 
    
         
            +
                  sock = checkout
         
     | 
| 
      
 224 
     | 
    
         
            +
                  packed_message = message_with_headers.append!(message_with_check).to_s
         
     | 
| 
      
 225 
     | 
    
         
            +
                  send_message_on_socket(packed_message, sock)
         
     | 
| 
      
 226 
     | 
    
         
            +
                  docs, num_received, cursor_id = receive(sock)
         
     | 
| 
      
 227 
     | 
    
         
            +
                  checkin(sock)
         
     | 
| 
      
 228 
     | 
    
         
            +
                  if num_received == 1 && error = docs[0]['err']
         
     | 
| 
      
 229 
     | 
    
         
            +
                    raise Mongo::OperationFailure, error
         
     | 
| 
      
 230 
     | 
    
         
            +
                  end
         
     | 
| 
      
 231 
     | 
    
         
            +
                  [docs, num_received, cursor_id]
         
     | 
| 
      
 232 
     | 
    
         
            +
                end
         
     | 
| 
      
 233 
     | 
    
         
            +
             
     | 
| 
      
 234 
     | 
    
         
            +
                # Sends a message to the database and waits for the response.
         
     | 
| 
      
 235 
     | 
    
         
            +
                #
         
     | 
| 
      
 236 
     | 
    
         
            +
                # Takes a MongoDB opcode, +operation+, a message of class ByteBuffer,
         
     | 
| 
      
 237 
     | 
    
         
            +
                # +message+, and an optional formatted +log_message+. This method 
         
     | 
| 
      
 238 
     | 
    
         
            +
                # also takes an options socket for internal use with #connect_to_master.
         
     | 
| 
      
 239 
     | 
    
         
            +
                def receive_message(operation, message, log_message=nil, socket=nil)
         
     | 
| 
      
 240 
     | 
    
         
            +
                  packed_message = add_message_headers(operation, message).to_s
         
     | 
| 
      
 241 
     | 
    
         
            +
                  @logger.debug("  MONGODB #{log_message || message}") if @logger
         
     | 
| 
      
 242 
     | 
    
         
            +
                  sock = socket || checkout
         
     | 
| 
      
 243 
     | 
    
         
            +
             
     | 
| 
      
 244 
     | 
    
         
            +
                  send_message_on_socket(packed_message, sock)
         
     | 
| 
      
 245 
     | 
    
         
            +
                  result = receive(sock)
         
     | 
| 
      
 246 
     | 
    
         
            +
                  checkin(sock)
         
     | 
| 
      
 247 
     | 
    
         
            +
                  result
         
     | 
| 
      
 248 
     | 
    
         
            +
                end
         
     | 
| 
      
 249 
     | 
    
         
            +
             
     | 
| 
      
 250 
     | 
    
         
            +
                # Creates a new socket and tries to connect to master.
         
     | 
| 
      
 251 
     | 
    
         
            +
                # If successful, sets @host and @port to master and returns the socket.
         
     | 
| 
      
 252 
     | 
    
         
            +
                def connect_to_master
         
     | 
| 
      
 253 
     | 
    
         
            +
                  close
         
     | 
| 
      
 254 
     | 
    
         
            +
                  @host = @port = nil
         
     | 
| 
      
 255 
     | 
    
         
            +
                  for node_pair in @nodes
         
     | 
| 
      
 256 
     | 
    
         
            +
                    host, port = *node_pair
         
     | 
| 
      
 257 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 258 
     | 
    
         
            +
                      socket = TCPSocket.new(host, port)
         
     | 
| 
      
 259 
     | 
    
         
            +
                      socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
         
     | 
| 
      
 260 
     | 
    
         
            +
             
     | 
| 
      
 261 
     | 
    
         
            +
                      # If we're connected to master, set the @host and @port
         
     | 
| 
      
 262 
     | 
    
         
            +
                      result = self['admin'].command({:ismaster => 1}, false, false, socket)
         
     | 
| 
      
 263 
     | 
    
         
            +
                      if result['ok'] == 1 && ((is_master = result['ismaster'] == 1) || @slave_ok)
         
     | 
| 
      
 264 
     | 
    
         
            +
                        @host, @port = host, port
         
     | 
| 
      
 265 
     | 
    
         
            +
                      end
         
     | 
| 
      
 266 
     | 
    
         
            +
             
     | 
| 
      
 267 
     | 
    
         
            +
                      # Note: slave_ok can be true only when connecting to a single node.
         
     | 
| 
      
 268 
     | 
    
         
            +
                      if @nodes.length == 1 && !is_master && !@slave_ok
         
     | 
| 
      
 269 
     | 
    
         
            +
                        raise ConfigurationError, "Trying to connect directly to slave; " +
         
     | 
| 
      
 270 
     | 
    
         
            +
                          "if this is what you want, specify :slave_ok => true."
         
     | 
| 
      
 271 
     | 
    
         
            +
                      end
         
     | 
| 
      
 272 
     | 
    
         
            +
             
     | 
| 
      
 273 
     | 
    
         
            +
                      break if is_master || @slave_ok
         
     | 
| 
      
 274 
     | 
    
         
            +
                    rescue SocketError, SystemCallError, IOError => ex
         
     | 
| 
      
 275 
     | 
    
         
            +
                      socket.close if socket
         
     | 
| 
      
 276 
     | 
    
         
            +
                      false
         
     | 
| 
      
 277 
     | 
    
         
            +
                    end
         
     | 
| 
      
 278 
     | 
    
         
            +
                  end 
         
     | 
| 
      
 279 
     | 
    
         
            +
                  raise ConnectionFailure, "failed to connect to any given host:port" unless socket
         
     | 
| 
      
 280 
     | 
    
         
            +
                end
         
     | 
| 
      
 281 
     | 
    
         
            +
             
     | 
| 
      
 282 
     | 
    
         
            +
                # Are we connected to MongoDB? This is determined by checking whether
         
     | 
| 
      
 283 
     | 
    
         
            +
                # @host and @port have values, since they're set to nil on calls to #close.
         
     | 
| 
      
 284 
     | 
    
         
            +
                def connected?
         
     | 
| 
      
 285 
     | 
    
         
            +
                  @host && @port
         
     | 
| 
      
 286 
     | 
    
         
            +
                end
         
     | 
| 
      
 287 
     | 
    
         
            +
             
     | 
| 
      
 288 
     | 
    
         
            +
                # Close the connection to the database.
         
     | 
| 
      
 289 
     | 
    
         
            +
                def close
         
     | 
| 
      
 290 
     | 
    
         
            +
                  @sockets.each do |sock|
         
     | 
| 
      
 291 
     | 
    
         
            +
                    sock.close
         
     | 
| 
      
 292 
     | 
    
         
            +
                  end
         
     | 
| 
      
 293 
     | 
    
         
            +
                  @host = @port = nil
         
     | 
| 
      
 294 
     | 
    
         
            +
                  @sockets.clear   
         
     | 
| 
      
 295 
     | 
    
         
            +
                  @checked_out.clear
         
     | 
| 
      
 296 
     | 
    
         
            +
                  @reserved_connections.clear
         
     | 
| 
      
 297 
     | 
    
         
            +
                end
         
     | 
| 
      
 298 
     | 
    
         
            +
             
     | 
| 
      
 299 
     | 
    
         
            +
                private
         
     | 
| 
      
 300 
     | 
    
         
            +
             
     | 
| 
      
 301 
     | 
    
         
            +
                # Get a socket from the pool, mapped to the current thread.
         
     | 
| 
      
 302 
     | 
    
         
            +
                def checkout
         
     | 
| 
      
 303 
     | 
    
         
            +
                  #return @socket ||= checkout_new_socket if @size == 1
         
     | 
| 
      
 304 
     | 
    
         
            +
                  if sock = @reserved_connections[Thread.current.object_id]
         
     | 
| 
      
 305 
     | 
    
         
            +
                    sock
         
     | 
| 
      
 306 
     | 
    
         
            +
                  else
         
     | 
| 
      
 307 
     | 
    
         
            +
                    sock = obtain_socket
         
     | 
| 
      
 308 
     | 
    
         
            +
                    @reserved_connections[Thread.current.object_id] = sock
         
     | 
| 
      
 309 
     | 
    
         
            +
                  end
         
     | 
| 
      
 310 
     | 
    
         
            +
                  sock
         
     | 
| 
      
 311 
     | 
    
         
            +
                end
         
     | 
| 
      
 312 
     | 
    
         
            +
             
     | 
| 
      
 313 
     | 
    
         
            +
                # Return a socket to the pool.
         
     | 
| 
      
 314 
     | 
    
         
            +
                def checkin(socket)
         
     | 
| 
      
 315 
     | 
    
         
            +
                  @connection_mutex.synchronize do 
         
     | 
| 
      
 316 
     | 
    
         
            +
                    @reserved_connections.delete Thread.current.object_id
         
     | 
| 
      
 317 
     | 
    
         
            +
                    @checked_out.delete(socket)
         
     | 
| 
      
 318 
     | 
    
         
            +
                    @queue.signal
         
     | 
| 
      
 319 
     | 
    
         
            +
                  end
         
     | 
| 
      
 320 
     | 
    
         
            +
                  true
         
     | 
| 
      
 321 
     | 
    
         
            +
                end
         
     | 
| 
      
 322 
     | 
    
         
            +
             
     | 
| 
      
 323 
     | 
    
         
            +
                # Releases the connection for any dead threads.
         
     | 
| 
      
 324 
     | 
    
         
            +
                # Called when the connection pool grows too large to free up more sockets.
         
     | 
| 
      
 325 
     | 
    
         
            +
                def clear_stale_cached_connections!
         
     | 
| 
      
 326 
     | 
    
         
            +
                  keys = @reserved_connections.keys
         
     | 
| 
      
 327 
     | 
    
         
            +
             
     | 
| 
      
 328 
     | 
    
         
            +
                  Thread.list.each do |thread|
         
     | 
| 
      
 329 
     | 
    
         
            +
                    keys.delete(thread.object_id) if thread.alive?
         
     | 
| 
      
 330 
     | 
    
         
            +
                  end
         
     | 
| 
      
 331 
     | 
    
         
            +
                  
         
     | 
| 
      
 332 
     | 
    
         
            +
                  keys.each do |key|
         
     | 
| 
      
 333 
     | 
    
         
            +
                    next unless @reserved_connections.has_key?(key)
         
     | 
| 
      
 334 
     | 
    
         
            +
                    checkin(@reserved_connections[key])
         
     | 
| 
      
 335 
     | 
    
         
            +
                    @reserved_connections.delete(key)
         
     | 
| 
      
 336 
     | 
    
         
            +
                  end
         
     | 
| 
      
 337 
     | 
    
         
            +
                end
         
     | 
| 
      
 338 
     | 
    
         
            +
             
     | 
| 
      
 339 
     | 
    
         
            +
                # Adds a new socket to the pool and checks it out.
         
     | 
| 
      
 340 
     | 
    
         
            +
                #
         
     | 
| 
      
 341 
     | 
    
         
            +
                # This method is called exclusively from #obtain_socket;
         
     | 
| 
      
 342 
     | 
    
         
            +
                # therefore, it runs within a mutex, as it must.
         
     | 
| 
      
 343 
     | 
    
         
            +
                def checkout_new_socket
         
     | 
| 
      
 344 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 345 
     | 
    
         
            +
                  socket = TCPSocket.new(@host, @port)
         
     | 
| 
      
 346 
     | 
    
         
            +
                  socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
         
     | 
| 
      
 347 
     | 
    
         
            +
                  rescue => ex
         
     | 
| 
      
 348 
     | 
    
         
            +
                    raise ConnectionFailure, "Failed to connect socket: #{ex}"
         
     | 
| 
      
 349 
     | 
    
         
            +
                  end
         
     | 
| 
      
 350 
     | 
    
         
            +
                  @sockets << socket
         
     | 
| 
      
 351 
     | 
    
         
            +
                  @checked_out << socket
         
     | 
| 
      
 352 
     | 
    
         
            +
                  socket
         
     | 
| 
      
 353 
     | 
    
         
            +
                end
         
     | 
| 
      
 354 
     | 
    
         
            +
             
     | 
| 
      
 355 
     | 
    
         
            +
                # Checks out the first available socket from the pool.
         
     | 
| 
      
 356 
     | 
    
         
            +
                #
         
     | 
| 
      
 357 
     | 
    
         
            +
                # This method is called exclusively from #obtain_socket;
         
     | 
| 
      
 358 
     | 
    
         
            +
                # therefore, it runs within a mutex, as it must.
         
     | 
| 
      
 359 
     | 
    
         
            +
                def checkout_existing_socket
         
     | 
| 
      
 360 
     | 
    
         
            +
                  socket = (@sockets - @checked_out).first
         
     | 
| 
      
 361 
     | 
    
         
            +
                  @checked_out << socket
         
     | 
| 
      
 362 
     | 
    
         
            +
                  socket
         
     | 
| 
      
 363 
     | 
    
         
            +
                end
         
     | 
| 
      
 364 
     | 
    
         
            +
             
     | 
| 
      
 365 
     | 
    
         
            +
                # Check out an existing socket or create a new socket if the maximum
         
     | 
| 
      
 366 
     | 
    
         
            +
                # pool size has not been exceeded. Otherwise, wait for the next
         
     | 
| 
      
 367 
     | 
    
         
            +
                # available socket.
         
     | 
| 
      
 368 
     | 
    
         
            +
                def obtain_socket
         
     | 
| 
      
 369 
     | 
    
         
            +
                  @connection_mutex.synchronize do 
         
     | 
| 
      
 370 
     | 
    
         
            +
                    connect_to_master if !connected?
         
     | 
| 
      
 371 
     | 
    
         
            +
             
     | 
| 
      
 372 
     | 
    
         
            +
                    loop do 
         
     | 
| 
      
 373 
     | 
    
         
            +
                      socket = if @checked_out.size < @sockets.size
         
     | 
| 
      
 374 
     | 
    
         
            +
                                 checkout_existing_socket
         
     | 
| 
      
 375 
     | 
    
         
            +
                               elsif @sockets.size < @size
         
     | 
| 
      
 376 
     | 
    
         
            +
                                 checkout_new_socket
         
     | 
| 
      
 377 
     | 
    
         
            +
                               end
         
     | 
| 
      
 378 
     | 
    
         
            +
             
     | 
| 
      
 379 
     | 
    
         
            +
                      return socket if socket
         
     | 
| 
      
 380 
     | 
    
         
            +
             
     | 
| 
      
 381 
     | 
    
         
            +
                      # Try to clear out any stale threads to free up some connections
         
     | 
| 
      
 382 
     | 
    
         
            +
                      clear_stale_cached_connections!
         
     | 
| 
      
 383 
     | 
    
         
            +
                      next if @checked_out.size < @sockets.size
         
     | 
| 
      
 384 
     | 
    
         
            +
             
     | 
| 
      
 385 
     | 
    
         
            +
                      # Otherwise, wait.
         
     | 
| 
      
 386 
     | 
    
         
            +
                      if wait
         
     | 
| 
      
 387 
     | 
    
         
            +
                        next
         
     | 
| 
      
 388 
     | 
    
         
            +
                      else
         
     | 
| 
      
 389 
     | 
    
         
            +
             
     | 
| 
      
 390 
     | 
    
         
            +
                        # Try to clear stale threads once more before failing.
         
     | 
| 
      
 391 
     | 
    
         
            +
                        clear_stale_cached_connections!
         
     | 
| 
      
 392 
     | 
    
         
            +
                        if @size == @sockets.size
         
     | 
| 
      
 393 
     | 
    
         
            +
                          raise ConnectionTimeoutError, "could not obtain connection within " +
         
     | 
| 
      
 394 
     | 
    
         
            +
                            "#{@timeout} seconds. The max pool size is currently #{@size}; " +
         
     | 
| 
      
 395 
     | 
    
         
            +
                            "consider increasing it."
         
     | 
| 
      
 396 
     | 
    
         
            +
                        end
         
     | 
| 
      
 397 
     | 
    
         
            +
                      end  # if
         
     | 
| 
      
 398 
     | 
    
         
            +
                    end # loop
         
     | 
| 
      
 399 
     | 
    
         
            +
                  end # synchronize
         
     | 
| 
      
 400 
     | 
    
         
            +
                end
         
     | 
| 
      
 401 
     | 
    
         
            +
             
     | 
| 
      
 402 
     | 
    
         
            +
                if RUBY_VERSION >= '1.9'
         
     | 
| 
      
 403 
     | 
    
         
            +
                  # Ruby 1.9's Condition Variables don't support timeouts yet;
         
     | 
| 
      
 404 
     | 
    
         
            +
                  # until they do, we'll make do with this hack. 
         
     | 
| 
      
 405 
     | 
    
         
            +
                  def wait
         
     | 
| 
      
 406 
     | 
    
         
            +
                    Timeout.timeout(@timeout) do 
         
     | 
| 
      
 407 
     | 
    
         
            +
                      @queue.wait
         
     | 
| 
      
 408 
     | 
    
         
            +
                    end
         
     | 
| 
      
 409 
     | 
    
         
            +
                  end
         
     | 
| 
      
 410 
     | 
    
         
            +
                else
         
     | 
| 
      
 411 
     | 
    
         
            +
                  def wait
         
     | 
| 
      
 412 
     | 
    
         
            +
                    @queue.wait(@timeout)
         
     | 
| 
      
 413 
     | 
    
         
            +
                  end
         
     | 
| 
      
 414 
     | 
    
         
            +
                end
         
     | 
| 
      
 415 
     | 
    
         
            +
             
     | 
| 
      
 416 
     | 
    
         
            +
                def receive(sock)
         
     | 
| 
      
 417 
     | 
    
         
            +
                  receive_header(sock)
         
     | 
| 
      
 418 
     | 
    
         
            +
                  number_received, cursor_id = receive_response_header(sock)
         
     | 
| 
      
 419 
     | 
    
         
            +
                  read_documents(number_received, cursor_id, sock)
         
     | 
| 
      
 420 
     | 
    
         
            +
                end
         
     | 
| 
      
 421 
     | 
    
         
            +
             
     | 
| 
      
 422 
     | 
    
         
            +
                def receive_header(sock)
         
     | 
| 
      
 423 
     | 
    
         
            +
                  header = ByteBuffer.new
         
     | 
| 
      
 424 
     | 
    
         
            +
                  header.put_array(receive_message_on_socket(16, sock).unpack("C*"))
         
     | 
| 
      
 425 
     | 
    
         
            +
                  unless header.size == STANDARD_HEADER_SIZE
         
     | 
| 
      
 426 
     | 
    
         
            +
                    raise "Short read for DB response header: " +
         
     | 
| 
      
 427 
     | 
    
         
            +
                      "expected #{STANDARD_HEADER_SIZE} bytes, saw #{header.size}" 
         
     | 
| 
      
 428 
     | 
    
         
            +
                  end
         
     | 
| 
      
 429 
     | 
    
         
            +
                  header.rewind
         
     | 
| 
      
 430 
     | 
    
         
            +
                  size        = header.get_int
         
     | 
| 
      
 431 
     | 
    
         
            +
                  request_id  = header.get_int
         
     | 
| 
      
 432 
     | 
    
         
            +
                  response_to = header.get_int
         
     | 
| 
      
 433 
     | 
    
         
            +
                  op          = header.get_int
         
     | 
| 
      
 434 
     | 
    
         
            +
                end
         
     | 
| 
      
 435 
     | 
    
         
            +
             
     | 
| 
      
 436 
     | 
    
         
            +
                def receive_response_header(sock)
         
     | 
| 
      
 437 
     | 
    
         
            +
                  header_buf = ByteBuffer.new
         
     | 
| 
      
 438 
     | 
    
         
            +
                  header_buf.put_array(receive_message_on_socket(RESPONSE_HEADER_SIZE, sock).unpack("C*"))
         
     | 
| 
      
 439 
     | 
    
         
            +
                  if header_buf.length != RESPONSE_HEADER_SIZE
         
     | 
| 
      
 440 
     | 
    
         
            +
                    raise "Short read for DB response header; " +
         
     | 
| 
      
 441 
     | 
    
         
            +
                      "expected #{RESPONSE_HEADER_SIZE} bytes, saw #{header_buf.length}"
         
     | 
| 
      
 442 
     | 
    
         
            +
                  end
         
     | 
| 
      
 443 
     | 
    
         
            +
                  header_buf.rewind
         
     | 
| 
      
 444 
     | 
    
         
            +
                  result_flags     = header_buf.get_int
         
     | 
| 
      
 445 
     | 
    
         
            +
                  cursor_id        = header_buf.get_long
         
     | 
| 
      
 446 
     | 
    
         
            +
                  starting_from    = header_buf.get_int
         
     | 
| 
      
 447 
     | 
    
         
            +
                  number_remaining = header_buf.get_int
         
     | 
| 
      
 448 
     | 
    
         
            +
                  [number_remaining, cursor_id]
         
     | 
| 
      
 449 
     | 
    
         
            +
                end
         
     | 
| 
      
 450 
     | 
    
         
            +
             
     | 
| 
      
 451 
     | 
    
         
            +
                def read_documents(number_received, cursor_id, sock)
         
     | 
| 
      
 452 
     | 
    
         
            +
                  docs = []
         
     | 
| 
      
 453 
     | 
    
         
            +
                  number_remaining = number_received
         
     | 
| 
      
 454 
     | 
    
         
            +
                  while number_remaining > 0 do
         
     | 
| 
      
 455 
     | 
    
         
            +
                    buf = ByteBuffer.new
         
     | 
| 
      
 456 
     | 
    
         
            +
                    buf.put_array(receive_message_on_socket(4, sock).unpack("C*"))
         
     | 
| 
      
 457 
     | 
    
         
            +
                    buf.rewind
         
     | 
| 
      
 458 
     | 
    
         
            +
                    size = buf.get_int
         
     | 
| 
      
 459 
     | 
    
         
            +
                    buf.put_array(receive_message_on_socket(size - 4, sock).unpack("C*"), 4)
         
     | 
| 
      
 460 
     | 
    
         
            +
                    number_remaining -= 1
         
     | 
| 
      
 461 
     | 
    
         
            +
                    buf.rewind
         
     | 
| 
      
 462 
     | 
    
         
            +
                    docs << BSON.new.deserialize(buf)
         
     | 
| 
      
 463 
     | 
    
         
            +
                  end
         
     | 
| 
      
 464 
     | 
    
         
            +
                  [docs, number_received, cursor_id]
         
     | 
| 
      
 465 
     | 
    
         
            +
                end
         
     | 
| 
      
 466 
     | 
    
         
            +
             
     | 
| 
      
 467 
     | 
    
         
            +
                def last_error_message(db_name)
         
     | 
| 
      
 468 
     | 
    
         
            +
                  message = ByteBuffer.new
         
     | 
| 
      
 469 
     | 
    
         
            +
                  message.put_int(0)
         
     | 
| 
      
 470 
     | 
    
         
            +
                  BSON.serialize_cstr(message, "#{db_name}.$cmd")
         
     | 
| 
      
 471 
     | 
    
         
            +
                  message.put_int(0)
         
     | 
| 
      
 472 
     | 
    
         
            +
                  message.put_int(-1)
         
     | 
| 
      
 473 
     | 
    
         
            +
                  message.put_array(BSON_SERIALIZER.serialize({:getlasterror => 1}, false).unpack("C*"))
         
     | 
| 
      
 474 
     | 
    
         
            +
                  add_message_headers(Mongo::Constants::OP_QUERY, message)
         
     | 
| 
      
 475 
     | 
    
         
            +
                end
         
     | 
| 
      
 476 
     | 
    
         
            +
                
         
     | 
| 
      
 477 
     | 
    
         
            +
                # Prepares a message for transmission to MongoDB by
         
     | 
| 
      
 478 
     | 
    
         
            +
                # constructing a valid message header.
         
     | 
| 
      
 479 
     | 
    
         
            +
                def add_message_headers(operation, message)
         
     | 
| 
      
 480 
     | 
    
         
            +
                  headers = ByteBuffer.new
         
     | 
| 
      
 481 
     | 
    
         
            +
             
     | 
| 
      
 482 
     | 
    
         
            +
                  # Message size.
         
     | 
| 
      
 483 
     | 
    
         
            +
                  headers.put_int(16 + message.size)
         
     | 
| 
      
 484 
     | 
    
         
            +
             
     | 
| 
      
 485 
     | 
    
         
            +
                  # Unique request id.
         
     | 
| 
      
 486 
     | 
    
         
            +
                  headers.put_int(get_request_id)
         
     | 
| 
      
 487 
     | 
    
         
            +
             
     | 
| 
      
 488 
     | 
    
         
            +
                  # Response id.
         
     | 
| 
      
 489 
     | 
    
         
            +
                  headers.put_int(0)
         
     | 
| 
      
 490 
     | 
    
         
            +
             
     | 
| 
      
 491 
     | 
    
         
            +
                  # Opcode.
         
     | 
| 
      
 492 
     | 
    
         
            +
                  headers.put_int(operation)
         
     | 
| 
      
 493 
     | 
    
         
            +
                  message.prepend!(headers)
         
     | 
| 
      
 494 
     | 
    
         
            +
                end
         
     | 
| 
      
 495 
     | 
    
         
            +
             
     | 
| 
      
 496 
     | 
    
         
            +
                # Low-level method for sending a message on a socket.
         
     | 
| 
      
 497 
     | 
    
         
            +
                # Requires a packed message and an available socket, 
         
     | 
| 
      
 498 
     | 
    
         
            +
                def send_message_on_socket(packed_message, socket)
         
     | 
| 
      
 499 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 500 
     | 
    
         
            +
                  socket.send(packed_message, 0)
         
     | 
| 
      
 501 
     | 
    
         
            +
                  rescue => ex
         
     | 
| 
      
 502 
     | 
    
         
            +
                    close
         
     | 
| 
      
 503 
     | 
    
         
            +
                    raise ConnectionFailure, "Operation failed with the following exception: #{ex}"
         
     | 
| 
      
 504 
     | 
    
         
            +
                  end
         
     | 
| 
      
 505 
     | 
    
         
            +
                end
         
     | 
| 
      
 506 
     | 
    
         
            +
             
     | 
| 
      
 507 
     | 
    
         
            +
                # Low-level method for receiving data from socket.
         
     | 
| 
      
 508 
     | 
    
         
            +
                # Requires length and an available socket.
         
     | 
| 
      
 509 
     | 
    
         
            +
                def receive_message_on_socket(length, socket)
         
     | 
| 
      
 510 
     | 
    
         
            +
                  message = ""
         
     | 
| 
      
 511 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 512 
     | 
    
         
            +
                    while message.length < length do
         
     | 
| 
      
 513 
     | 
    
         
            +
                      chunk = socket.recv(length - message.length)
         
     | 
| 
      
 514 
     | 
    
         
            +
                      raise ConnectionFailure, "connection closed" unless chunk.length > 0
         
     | 
| 
      
 515 
     | 
    
         
            +
                      message += chunk
         
     | 
| 
      
 516 
     | 
    
         
            +
                    end
         
     | 
| 
      
 517 
     | 
    
         
            +
                    rescue => ex
         
     | 
| 
      
 518 
     | 
    
         
            +
                      raise ConnectionFailure, "Operation failed with the following exception: #{ex}"
         
     | 
| 
      
 519 
     | 
    
         
            +
                  end
         
     | 
| 
      
 520 
     | 
    
         
            +
                  message
         
     | 
| 
      
 521 
     | 
    
         
            +
                end
         
     | 
| 
      
 522 
     | 
    
         
            +
             
     | 
| 
      
 523 
     | 
    
         
            +
             
     | 
| 
      
 524 
     | 
    
         
            +
                ## Private helper methods
         
     | 
| 
      
 525 
     | 
    
         
            +
             
     | 
| 
      
 526 
     | 
    
         
            +
                # Returns an array of host-port pairs.
         
     | 
| 
      
 527 
     | 
    
         
            +
                def format_pair(pair_or_host)
         
     | 
| 
      
 528 
     | 
    
         
            +
                  case pair_or_host
         
     | 
| 
      
 529 
     | 
    
         
            +
                    when String
         
     | 
| 
      
 530 
     | 
    
         
            +
                      [[pair_or_host, port ? port.to_i : DEFAULT_PORT]]
         
     | 
| 
      
 531 
     | 
    
         
            +
                    when Hash
         
     | 
| 
      
 532 
     | 
    
         
            +
                     connections = []
         
     | 
| 
      
 533 
     | 
    
         
            +
                     connections << pair_val_to_connection(pair_or_host[:left])
         
     | 
| 
      
 534 
     | 
    
         
            +
                     connections << pair_val_to_connection(pair_or_host[:right])
         
     | 
| 
      
 535 
     | 
    
         
            +
                     connections
         
     | 
| 
      
 536 
     | 
    
         
            +
                    when nil
         
     | 
| 
      
 537 
     | 
    
         
            +
                      [['localhost', DEFAULT_PORT]]
         
     | 
| 
      
 538 
     | 
    
         
            +
                  end
         
     | 
| 
      
 539 
     | 
    
         
            +
                end
         
     | 
| 
      
 540 
     | 
    
         
            +
                
         
     | 
| 
       145 
541 
     | 
    
         
             
                # Turns an array containing a host name string and a
         
     | 
| 
       146 
542 
     | 
    
         
             
                # port number integer into a [host, port] pair array.
         
     | 
| 
       147 
543 
     | 
    
         
             
                def pair_val_to_connection(a)
         
     | 
| 
         @@ -157,19 +553,5 @@ module Mongo 
     | 
|
| 
       157 
553 
     | 
    
         
             
                  end
         
     | 
| 
       158 
554 
     | 
    
         
             
                end
         
     | 
| 
       159 
555 
     | 
    
         | 
| 
       160 
     | 
    
         
            -
                # Send cmd (a hash, possibly ordered) to the admin database and return
         
     | 
| 
       161 
     | 
    
         
            -
                # the answer. Raises an error unless the return is "ok" (DB#ok?
         
     | 
| 
       162 
     | 
    
         
            -
                # returns +true+).
         
     | 
| 
       163 
     | 
    
         
            -
                def single_db_command(db_name, cmd)
         
     | 
| 
       164 
     | 
    
         
            -
                  db = nil
         
     | 
| 
       165 
     | 
    
         
            -
                  begin
         
     | 
| 
       166 
     | 
    
         
            -
                    db = db(db_name)
         
     | 
| 
       167 
     | 
    
         
            -
                    doc = db.db_command(cmd)
         
     | 
| 
       168 
     | 
    
         
            -
                    raise "error retrieving database info: #{doc.inspect}" unless db.ok?(doc)
         
     | 
| 
       169 
     | 
    
         
            -
                    doc
         
     | 
| 
       170 
     | 
    
         
            -
                  ensure
         
     | 
| 
       171 
     | 
    
         
            -
                    db.close if db
         
     | 
| 
       172 
     | 
    
         
            -
                  end
         
     | 
| 
       173 
     | 
    
         
            -
                end
         
     | 
| 
       174 
556 
     | 
    
         
             
              end
         
     | 
| 
       175 
557 
     | 
    
         
             
            end
         
     |