mongo 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +9 -6
- data/Rakefile +3 -4
- data/docs/HISTORY.md +20 -2
- data/docs/READ_PREFERENCE.md +39 -0
- data/docs/RELEASES.md +1 -1
- data/docs/REPLICA_SETS.md +23 -2
- data/docs/TAILABLE_CURSORS.md +51 -0
- data/docs/TUTORIAL.md +4 -4
- data/docs/WRITE_CONCERN.md +5 -2
- data/lib/mongo.rb +7 -22
- data/lib/mongo/collection.rb +96 -29
- data/lib/mongo/connection.rb +107 -62
- data/lib/mongo/cursor.rb +136 -57
- data/lib/mongo/db.rb +26 -5
- data/lib/mongo/exceptions.rb +17 -1
- data/lib/mongo/gridfs/grid.rb +1 -1
- data/lib/mongo/repl_set_connection.rb +273 -156
- data/lib/mongo/util/logging.rb +42 -0
- data/lib/mongo/util/node.rb +183 -0
- data/lib/mongo/util/pool.rb +76 -13
- data/lib/mongo/util/pool_manager.rb +208 -0
- data/lib/mongo/util/ssl_socket.rb +38 -0
- data/lib/mongo/util/support.rb +9 -1
- data/lib/mongo/util/timeout.rb +42 -0
- data/lib/mongo/version.rb +3 -0
- data/mongo.gemspec +2 -2
- data/test/bson/binary_test.rb +1 -1
- data/test/bson/bson_string_test.rb +30 -0
- data/test/bson/bson_test.rb +6 -3
- data/test/bson/byte_buffer_test.rb +1 -1
- data/test/bson/hash_with_indifferent_access_test.rb +1 -1
- data/test/bson/json_test.rb +1 -1
- data/test/bson/object_id_test.rb +2 -18
- data/test/bson/ordered_hash_test.rb +38 -3
- data/test/bson/test_helper.rb +46 -0
- data/test/bson/timestamp_test.rb +32 -10
- data/test/collection_test.rb +89 -3
- data/test/connection_test.rb +35 -20
- data/test/cursor_test.rb +63 -2
- data/test/db_test.rb +12 -2
- data/test/pool_test.rb +21 -0
- data/test/replica_sets/connect_test.rb +26 -13
- data/test/replica_sets/connection_string_test.rb +1 -4
- data/test/replica_sets/count_test.rb +1 -0
- data/test/replica_sets/insert_test.rb +1 -0
- data/test/replica_sets/pooled_insert_test.rb +4 -1
- data/test/replica_sets/query_secondaries.rb +2 -1
- data/test/replica_sets/query_test.rb +2 -1
- data/test/replica_sets/read_preference_test.rb +43 -0
- data/test/replica_sets/refresh_test.rb +123 -0
- data/test/replica_sets/replication_ack_test.rb +9 -4
- data/test/replica_sets/rs_test_helper.rb +2 -2
- data/test/timeout_test.rb +14 -0
- data/test/tools/repl_set_manager.rb +134 -23
- data/test/unit/collection_test.rb +6 -8
- data/test/unit/connection_test.rb +4 -4
- data/test/unit/cursor_test.rb +23 -5
- data/test/unit/db_test.rb +2 -0
- data/test/unit/grid_test.rb +2 -0
- data/test/unit/node_test.rb +73 -0
- data/test/unit/pool_manager_test.rb +47 -0
- data/test/unit/read_test.rb +101 -0
- metadata +214 -138
- data/lib/mongo/test.rb +0 -20
- data/test/async/collection_test.rb +0 -224
- data/test/async/connection_test.rb +0 -24
- data/test/async/cursor_test.rb +0 -162
- data/test/async/worker_pool_test.rb +0 -99
- data/test/load/resque/load.rb +0 -21
- data/test/load/resque/processor.rb +0 -26
- data/test/load/unicorn/unicorn.rb +0 -29
- data/test/tools/load.rb +0 -58
- data/test/tools/sharding_manager.rb +0 -202
- data/test/tools/test.rb +0 -4
- data/test/unit/repl_set_connection_test.rb +0 -59
data/lib/mongo/connection.rb
CHANGED
@@ -24,18 +24,18 @@ module Mongo
|
|
24
24
|
|
25
25
|
# Instantiates and manages connections to MongoDB.
|
26
26
|
class Connection
|
27
|
+
include Mongo::Logging
|
28
|
+
|
27
29
|
TCPSocket = ::TCPSocket
|
28
30
|
Mutex = ::Mutex
|
29
31
|
ConditionVariable = ::ConditionVariable
|
30
32
|
|
31
|
-
# Abort connections if a ConnectionError is raised.
|
32
|
-
Thread.abort_on_exception = true
|
33
|
-
|
34
33
|
DEFAULT_PORT = 27017
|
35
34
|
STANDARD_HEADER_SIZE = 16
|
36
35
|
RESPONSE_HEADER_SIZE = 20
|
37
36
|
|
38
|
-
attr_reader :logger, :size, :auths, :primary, :safe, :
|
37
|
+
attr_reader :logger, :size, :auths, :primary, :safe, :host_to_try,
|
38
|
+
:pool_size, :connect_timeout, :primary_pool, :socket_class
|
39
39
|
|
40
40
|
# Counter for generating unique request ids.
|
41
41
|
@@current_request_id = 0
|
@@ -65,11 +65,14 @@ module Mongo
|
|
65
65
|
# logging negatively impacts performance; therefore, it should not be used for high-performance apps.
|
66
66
|
# @option opts [Integer] :pool_size (1) The maximum number of socket connections allowed per
|
67
67
|
# connection pool. Note: this setting is relevant only for multi-threaded applications.
|
68
|
-
# @option opts [Float] :
|
68
|
+
# @option opts [Float] :pool_timeout (5.0) When all of the connections a pool are checked out,
|
69
69
|
# this is the number of seconds to wait for a new connection to be released before throwing an exception.
|
70
70
|
# Note: this setting is relevant only for multi-threaded applications (which in Ruby are rare).
|
71
71
|
# @option opts [Float] :op_timeout (nil) The number of seconds to wait for a read operation to time out.
|
72
72
|
# Disabled by default.
|
73
|
+
# @option opts [Float] :connect_timeout (nil) The number of seconds to wait before timing out a
|
74
|
+
# connection attempt.
|
75
|
+
# @option opts [Boolean] :ssl (false) If true, create the connection to the server using SSL.
|
73
76
|
#
|
74
77
|
# @example localhost, 27017
|
75
78
|
# Connection.new
|
@@ -128,7 +131,7 @@ module Mongo
|
|
128
131
|
#
|
129
132
|
# @deprecated
|
130
133
|
def self.multi(nodes, opts={})
|
131
|
-
warn "Connection.multi is now deprecated. Please use ReplSetConnection.new instead."
|
134
|
+
warn "Connection.multi is now deprecated and will be removed in v2.0. Please use ReplSetConnection.new instead."
|
132
135
|
|
133
136
|
nodes << opts
|
134
137
|
ReplSetConnection.new(*nodes)
|
@@ -306,7 +309,7 @@ module Mongo
|
|
306
309
|
#
|
307
310
|
# @core databases []-instance_method
|
308
311
|
def [](db_name)
|
309
|
-
DB.new(db_name, self
|
312
|
+
DB.new(db_name, self)
|
310
313
|
end
|
311
314
|
|
312
315
|
# Drop a database.
|
@@ -406,11 +409,7 @@ module Mongo
|
|
406
409
|
|
407
410
|
send_message_on_socket(packed_message, socket)
|
408
411
|
ensure
|
409
|
-
|
410
|
-
checkin_writer(socket)
|
411
|
-
else
|
412
|
-
checkin_reader(socket)
|
413
|
-
end
|
412
|
+
checkin(socket)
|
414
413
|
end
|
415
414
|
end
|
416
415
|
|
@@ -442,13 +441,13 @@ module Mongo
|
|
442
441
|
docs, num_received, cursor_id = receive(sock, last_error_id)
|
443
442
|
end
|
444
443
|
ensure
|
445
|
-
|
444
|
+
checkin(sock)
|
446
445
|
end
|
447
446
|
|
448
447
|
if num_received == 1 && (error = docs[0]['err'] || docs[0]['errmsg'])
|
449
448
|
close if error == "not master"
|
450
449
|
error = "wtimeout" if error == "timeout"
|
451
|
-
raise
|
450
|
+
raise OperationFailure.new(docs[0]['code'].to_s + ': ' + error, docs[0]['code'], docs[0])
|
452
451
|
end
|
453
452
|
|
454
453
|
docs[0]
|
@@ -462,30 +461,40 @@ module Mongo
|
|
462
461
|
# @param [Socket] socket a socket to use in lieu of checking out a new one.
|
463
462
|
# @param [Boolean] command (false) indicate whether this is a command. If this is a command,
|
464
463
|
# the message will be sent to the primary node.
|
464
|
+
# @param [Boolean] command (false) indicate whether the cursor should be exhausted. Set
|
465
|
+
# this to true only when the OP_QUERY_EXHAUST flag is set.
|
465
466
|
#
|
466
467
|
# @return [Array]
|
467
468
|
# An array whose indexes include [0] documents returned, [1] number of document received,
|
468
469
|
# and [3] a cursor_id.
|
469
|
-
def receive_message(operation, message, log_message=nil, socket=nil, command=false)
|
470
|
+
def receive_message(operation, message, log_message=nil, socket=nil, command=false, read=:primary, exhaust=false)
|
470
471
|
request_id = add_message_headers(message, operation)
|
471
472
|
packed_message = message.to_s
|
472
473
|
begin
|
473
474
|
if socket
|
474
475
|
sock = socket
|
475
|
-
|
476
|
+
should_checkin = false
|
476
477
|
else
|
477
|
-
|
478
|
-
|
478
|
+
if command
|
479
|
+
sock = checkout_writer
|
480
|
+
elsif read == :primary
|
481
|
+
sock = checkout_writer
|
482
|
+
elsif read == :secondary
|
483
|
+
sock = checkout_reader
|
484
|
+
else
|
485
|
+
sock = checkout_tagged(read)
|
486
|
+
end
|
487
|
+
should_checkin = true
|
479
488
|
end
|
480
489
|
|
481
490
|
result = ''
|
482
491
|
@safe_mutexes[sock].synchronize do
|
483
492
|
send_message_on_socket(packed_message, sock)
|
484
|
-
result = receive(sock, request_id)
|
493
|
+
result = receive(sock, request_id, exhaust)
|
485
494
|
end
|
486
495
|
ensure
|
487
|
-
if
|
488
|
-
|
496
|
+
if should_checkin
|
497
|
+
checkin(sock)
|
489
498
|
end
|
490
499
|
end
|
491
500
|
result
|
@@ -509,12 +518,11 @@ module Mongo
|
|
509
518
|
@read_primary = false
|
510
519
|
end
|
511
520
|
|
521
|
+
@max_bson_size = config['maxBsonObjectSize'] || Mongo::DEFAULT_MAX_BSON_SIZE
|
512
522
|
set_primary(@host_to_try)
|
513
523
|
end
|
514
524
|
|
515
|
-
if connected?
|
516
|
-
BSON::BSON_CODER.update_max_bson_size(self)
|
517
|
-
else
|
525
|
+
if !connected?
|
518
526
|
raise ConnectionFailure, "Failed to connect to a master node at #{@host_to_try[0]}:#{@host_to_try[1]}"
|
519
527
|
end
|
520
528
|
end
|
@@ -556,6 +564,14 @@ module Mongo
|
|
556
564
|
end
|
557
565
|
alias :primary? :read_primary?
|
558
566
|
|
567
|
+
# The value of the read preference. Because
|
568
|
+
# this is a single-node connection, the value
|
569
|
+
# is +:primary+, and the connection will read
|
570
|
+
# from whichever type of node it's connected to.
|
571
|
+
def read_preference
|
572
|
+
:primary
|
573
|
+
end
|
574
|
+
|
559
575
|
# Close the connection to the database.
|
560
576
|
def close
|
561
577
|
@primary_pool.close if @primary_pool
|
@@ -568,8 +584,7 @@ module Mongo
|
|
568
584
|
#
|
569
585
|
# @return [Integer]
|
570
586
|
def max_bson_size
|
571
|
-
|
572
|
-
config['maxBsonObjectSize'] || Mongo::DEFAULT_MAX_BSON_SIZE
|
587
|
+
@max_bson_size
|
573
588
|
end
|
574
589
|
|
575
590
|
# Checkout a socket for reading (i.e., a secondary node).
|
@@ -589,32 +604,41 @@ module Mongo
|
|
589
604
|
# Checkin a socket used for reading.
|
590
605
|
# Note: this is overridden in ReplSetConnection.
|
591
606
|
def checkin_reader(socket)
|
592
|
-
|
593
|
-
|
594
|
-
|
607
|
+
warn "Connection#checkin_writer is not deprecated and will be removed " +
|
608
|
+
"in driver v2.0. Use Connection#checkin instead."
|
609
|
+
checkin(socket)
|
595
610
|
end
|
596
611
|
|
597
612
|
# Checkin a socket used for writing.
|
598
613
|
# Note: this is overridden in ReplSetConnection.
|
599
614
|
def checkin_writer(socket)
|
615
|
+
warn "Connection#checkin_writer is not deprecated and will be removed " +
|
616
|
+
"in driver v2.0. Use Connection#checkin instead."
|
617
|
+
checkin(socket)
|
618
|
+
end
|
619
|
+
|
620
|
+
# Check a socket back into its pool.
|
621
|
+
def checkin(socket)
|
600
622
|
if @primary_pool
|
601
623
|
@primary_pool.checkin(socket)
|
602
624
|
end
|
603
625
|
end
|
604
626
|
|
605
|
-
# Execute the block and log the operation described by name
|
606
|
-
# and payload.
|
607
|
-
# TODO: Not sure if this should take a block.
|
608
|
-
def instrument(name, payload = {}, &blk)
|
609
|
-
res = yield
|
610
|
-
log_operation(name, payload)
|
611
|
-
res
|
612
|
-
end
|
613
|
-
|
614
627
|
protected
|
615
628
|
|
616
629
|
# Generic initialization code.
|
617
630
|
def setup(opts)
|
631
|
+
# Default maximum BSON object size
|
632
|
+
@max_bson_size = Mongo::DEFAULT_MAX_BSON_SIZE
|
633
|
+
|
634
|
+
# Determine whether to use SSL.
|
635
|
+
@ssl = opts.fetch(:ssl, false)
|
636
|
+
if @ssl
|
637
|
+
@socket_class = Mongo::SSLSocket
|
638
|
+
else
|
639
|
+
@socket_class = ::TCPSocket
|
640
|
+
end
|
641
|
+
|
618
642
|
# Authentication objects
|
619
643
|
@auths = opts.fetch(:auths, [])
|
620
644
|
|
@@ -623,18 +647,26 @@ module Mongo
|
|
623
647
|
|
624
648
|
# Pool size and timeout.
|
625
649
|
@pool_size = opts[:pool_size] || 1
|
626
|
-
|
650
|
+
if opts[:timeout]
|
651
|
+
warn "The :timeout option has been deprecated " +
|
652
|
+
"and will be removed in the 2.0 release. Use :pool_timeout instead."
|
653
|
+
end
|
654
|
+
@timeout = opts[:pool_timeout] || opts[:timeout] || 5.0
|
627
655
|
|
628
656
|
# Timeout on socket read operation.
|
629
657
|
@op_timeout = opts[:op_timeout] || nil
|
630
658
|
|
659
|
+
# Timeout on socket connect.
|
660
|
+
@connect_timeout = opts[:connect_timeout] || nil
|
661
|
+
|
631
662
|
# Mutex for synchronizing pool access
|
663
|
+
# TODO: remove this.
|
632
664
|
@connection_mutex = Mutex.new
|
633
665
|
|
634
666
|
# Global safe option. This is false by default.
|
635
667
|
@safe = opts[:safe] || false
|
636
668
|
|
637
|
-
|
669
|
+
# Create a mutex when a new key, in this case a socket,
|
638
670
|
# is added to the hash.
|
639
671
|
@safe_mutexes = Hash.new { |h, k| h[k] = Mutex.new }
|
640
672
|
|
@@ -672,18 +704,6 @@ module Mongo
|
|
672
704
|
end
|
673
705
|
end
|
674
706
|
|
675
|
-
## Logging methods
|
676
|
-
|
677
|
-
def log_operation(name, payload)
|
678
|
-
return unless @logger
|
679
|
-
msg = "#{payload[:database]}['#{payload[:collection]}'].#{name}("
|
680
|
-
msg += payload.values_at(:selector, :document, :documents, :fields ).compact.map(&:inspect).join(', ') + ")"
|
681
|
-
msg += ".skip(#{payload[:skip]})" if payload[:skip]
|
682
|
-
msg += ".limit(#{payload[:limit]})" if payload[:limit]
|
683
|
-
msg += ".sort(#{payload[:order]})" if payload[:order]
|
684
|
-
@logger.debug "MONGODB #{msg}"
|
685
|
-
end
|
686
|
-
|
687
707
|
private
|
688
708
|
|
689
709
|
## Methods for establishing a connection:
|
@@ -698,8 +718,16 @@ module Mongo
|
|
698
718
|
def check_is_master(node)
|
699
719
|
begin
|
700
720
|
host, port = *node
|
701
|
-
|
702
|
-
|
721
|
+
|
722
|
+
if @connect_timeout
|
723
|
+
Mongo::TimeoutHandler.timeout(@connect_timeout, OperationTimeout) do
|
724
|
+
socket = @socket_class.new(host, port)
|
725
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
726
|
+
end
|
727
|
+
else
|
728
|
+
socket = @socket_class.new(host, port)
|
729
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
730
|
+
end
|
703
731
|
|
704
732
|
config = self['admin'].command({:ismaster => 1}, :socket => socket)
|
705
733
|
rescue OperationFailure, SocketError, SystemCallError, IOError => ex
|
@@ -720,21 +748,38 @@ module Mongo
|
|
720
748
|
|
721
749
|
## Low-level connection methods.
|
722
750
|
|
723
|
-
def receive(sock,
|
751
|
+
def receive(sock, cursor_id, exhaust=false)
|
724
752
|
begin
|
725
|
-
|
726
|
-
|
727
|
-
|
753
|
+
if exhaust
|
754
|
+
docs = []
|
755
|
+
num_received = 0
|
756
|
+
|
757
|
+
while(cursor_id != 0) do
|
758
|
+
receive_header(sock, cursor_id, exhaust)
|
759
|
+
number_received, cursor_id = receive_response_header(sock)
|
760
|
+
new_docs, n = read_documents(number_received, sock)
|
761
|
+
docs += new_docs
|
762
|
+
num_received += n
|
763
|
+
end
|
764
|
+
|
765
|
+
return [docs, num_received, cursor_id]
|
766
|
+
else
|
767
|
+
receive_header(sock, cursor_id, exhaust)
|
768
|
+
number_received, cursor_id = receive_response_header(sock)
|
769
|
+
docs, num_received = read_documents(number_received, sock)
|
770
|
+
|
771
|
+
return [docs, num_received, cursor_id]
|
772
|
+
end
|
728
773
|
rescue Mongo::ConnectionFailure => ex
|
729
774
|
close
|
730
775
|
raise ex
|
731
776
|
end
|
732
777
|
end
|
733
778
|
|
734
|
-
def receive_header(sock, expected_response)
|
779
|
+
def receive_header(sock, expected_response, exhaust=false)
|
735
780
|
header = receive_message_on_socket(16, sock)
|
736
781
|
size, request_id, response_to = header.unpack('VVV')
|
737
|
-
if expected_response != response_to
|
782
|
+
if !exhaust && expected_response != response_to
|
738
783
|
raise Mongo::ConnectionFailure, "Expected response #{expected_response} but got #{response_to}"
|
739
784
|
end
|
740
785
|
|
@@ -766,7 +811,7 @@ module Mongo
|
|
766
811
|
end
|
767
812
|
end
|
768
813
|
|
769
|
-
def read_documents(number_received,
|
814
|
+
def read_documents(number_received, sock)
|
770
815
|
docs = []
|
771
816
|
number_remaining = number_received
|
772
817
|
while number_remaining > 0 do
|
@@ -776,7 +821,7 @@ module Mongo
|
|
776
821
|
number_remaining -= 1
|
777
822
|
docs << BSON::BSON_CODER.deserialize(buf)
|
778
823
|
end
|
779
|
-
[docs, number_received
|
824
|
+
[docs, number_received]
|
780
825
|
end
|
781
826
|
|
782
827
|
# Constructs a getlasterror message. This method is used exclusively by
|
data/lib/mongo/cursor.rb
CHANGED
@@ -18,12 +18,15 @@ module Mongo
|
|
18
18
|
|
19
19
|
# A cursor over query results. Returned objects are hashes.
|
20
20
|
class Cursor
|
21
|
-
include Mongo::Conversions
|
22
21
|
include Enumerable
|
22
|
+
include Mongo::Constants
|
23
|
+
include Mongo::Conversions
|
24
|
+
include Mongo::Logging
|
23
25
|
|
24
26
|
attr_reader :collection, :selector, :fields,
|
25
27
|
:order, :hint, :snapshot, :timeout,
|
26
|
-
:full_collection_name, :transformer
|
28
|
+
:full_collection_name, :transformer,
|
29
|
+
:options, :cursor_id
|
27
30
|
|
28
31
|
# Create a new cursor.
|
29
32
|
#
|
@@ -59,6 +62,7 @@ module Mongo
|
|
59
62
|
@limit = opts[:limit] || 0
|
60
63
|
@tailable = opts[:tailable] || false
|
61
64
|
@timeout = opts.fetch(:timeout, true)
|
65
|
+
@options = 0
|
62
66
|
|
63
67
|
# Use this socket for the query
|
64
68
|
@socket = opts[:socket]
|
@@ -67,12 +71,28 @@ module Mongo
|
|
67
71
|
@query_run = false
|
68
72
|
|
69
73
|
@transformer = opts[:transformer]
|
74
|
+
if value = opts[:read]
|
75
|
+
Mongo::Support.validate_read_preference(value)
|
76
|
+
else
|
77
|
+
value = collection.read_preference
|
78
|
+
end
|
79
|
+
@read_preference = value.is_a?(Hash) ? value.dup : value
|
70
80
|
batch_size(opts[:batch_size] || 0)
|
71
81
|
|
72
82
|
@full_collection_name = "#{@collection.db.name}.#{@collection.name}"
|
73
83
|
@cache = []
|
74
84
|
@returned = 0
|
75
85
|
|
86
|
+
if(!@timeout)
|
87
|
+
add_option(OP_QUERY_NO_CURSOR_TIMEOUT)
|
88
|
+
end
|
89
|
+
if(@connection.slave_ok?)
|
90
|
+
add_option(OP_QUERY_SLAVE_OK)
|
91
|
+
end
|
92
|
+
if(@tailable)
|
93
|
+
add_option(OP_QUERY_TAILABLE)
|
94
|
+
end
|
95
|
+
|
76
96
|
if @collection.name =~ /^\$cmd/ || @collection.name =~ /^system/
|
77
97
|
@command = true
|
78
98
|
else
|
@@ -80,11 +100,30 @@ module Mongo
|
|
80
100
|
end
|
81
101
|
end
|
82
102
|
|
103
|
+
# Guess whether the cursor is alive on the server.
|
104
|
+
#
|
105
|
+
# Note that this method only checks whether we have
|
106
|
+
# a cursor id. The cursor may still have timed out
|
107
|
+
# on the server. This will be indicated in the next
|
108
|
+
# call to Cursor#next.
|
109
|
+
#
|
110
|
+
# @return [Boolean]
|
111
|
+
def alive?
|
112
|
+
@cursor_id && @cursor_id != 0
|
113
|
+
end
|
114
|
+
|
83
115
|
# Get the next document specified the cursor options.
|
84
116
|
#
|
85
117
|
# @return [Hash, Nil] the next document or Nil if no documents remain.
|
86
|
-
def
|
87
|
-
|
118
|
+
def next
|
119
|
+
if @cache.length == 0
|
120
|
+
if @query_run && (@options & OP_QUERY_EXHAUST != 0)
|
121
|
+
close
|
122
|
+
return nil
|
123
|
+
else
|
124
|
+
refresh
|
125
|
+
end
|
126
|
+
end
|
88
127
|
doc = @cache.shift
|
89
128
|
|
90
129
|
if doc && doc['$err']
|
@@ -95,10 +134,10 @@ module Mongo
|
|
95
134
|
# connection. The next request will re-open on master server.
|
96
135
|
if err == "not master"
|
97
136
|
@connection.close
|
98
|
-
raise ConnectionFailure,
|
137
|
+
raise ConnectionFailure.new(err, doc['code'], doc)
|
99
138
|
end
|
100
139
|
|
101
|
-
raise OperationFailure,
|
140
|
+
raise OperationFailure.new(err, doc['code'], doc)
|
102
141
|
end
|
103
142
|
|
104
143
|
if @transformer.nil?
|
@@ -107,7 +146,7 @@ module Mongo
|
|
107
146
|
@transformer.call(doc) if doc
|
108
147
|
end
|
109
148
|
end
|
110
|
-
alias :
|
149
|
+
alias :next_document :next
|
111
150
|
|
112
151
|
# Reset this cursor on the server. Cursor options, such as the
|
113
152
|
# query string and the values for skip and limit, are preserved.
|
@@ -148,7 +187,7 @@ module Mongo
|
|
148
187
|
response = @db.command(command)
|
149
188
|
return response['n'].to_i if Mongo::Support.ok?(response)
|
150
189
|
return 0 if response['errmsg'] == "ns missing"
|
151
|
-
raise OperationFailure
|
190
|
+
raise OperationFailure.new("Count failed: #{response['errmsg']}", response['code'], response)
|
152
191
|
end
|
153
192
|
|
154
193
|
# Sort this cursor's results.
|
@@ -220,12 +259,13 @@ module Mongo
|
|
220
259
|
# the server will determine the batch size.
|
221
260
|
#
|
222
261
|
# @return [Cursor]
|
223
|
-
def batch_size(size=
|
262
|
+
def batch_size(size=nil)
|
263
|
+
return @batch_size unless size
|
224
264
|
check_modifiable
|
225
265
|
if size < 0 || size == 1
|
226
266
|
raise ArgumentError, "Invalid value for batch_size #{size}; must be 0 or > 1."
|
227
267
|
else
|
228
|
-
@batch_size = size > @limit ? @limit : size
|
268
|
+
@batch_size = @limit != 0 && size > @limit ? @limit : size
|
229
269
|
end
|
230
270
|
|
231
271
|
self
|
@@ -243,11 +283,8 @@ module Mongo
|
|
243
283
|
# puts doc['user']
|
244
284
|
# end
|
245
285
|
def each
|
246
|
-
|
247
|
-
|
248
|
-
while doc = next_document
|
249
|
-
yield doc #next_document
|
250
|
-
#num_returned += 1
|
286
|
+
while doc = self.next
|
287
|
+
yield doc
|
251
288
|
end
|
252
289
|
end
|
253
290
|
|
@@ -273,7 +310,8 @@ module Mongo
|
|
273
310
|
#
|
274
311
|
# @core explain explain-instance_method
|
275
312
|
def explain
|
276
|
-
c = Cursor.new(@collection,
|
313
|
+
c = Cursor.new(@collection,
|
314
|
+
query_options_hash.merge(:limit => -@limit.abs, :explain => true))
|
277
315
|
explanation = c.next_document
|
278
316
|
c.close
|
279
317
|
|
@@ -295,7 +333,7 @@ module Mongo
|
|
295
333
|
message = BSON::ByteBuffer.new([0, 0, 0, 0])
|
296
334
|
message.put_int(1)
|
297
335
|
message.put_long(@cursor_id)
|
298
|
-
|
336
|
+
log(:debug, "Cursor#close #{@cursor_id}")
|
299
337
|
@connection.send_message(Mongo::Constants::OP_KILL_CURSORS, message, :connection => :reader)
|
300
338
|
end
|
301
339
|
@cursor_id = 0
|
@@ -305,7 +343,9 @@ module Mongo
|
|
305
343
|
# Is this cursor closed?
|
306
344
|
#
|
307
345
|
# @return [Boolean]
|
308
|
-
def closed
|
346
|
+
def closed?
|
347
|
+
@closed
|
348
|
+
end
|
309
349
|
|
310
350
|
# Returns an integer indicating which query options have been selected.
|
311
351
|
#
|
@@ -314,11 +354,43 @@ module Mongo
|
|
314
354
|
# @see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-Mongo::Constants::OPQUERY
|
315
355
|
# The MongoDB wire protocol.
|
316
356
|
def query_opts
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
357
|
+
warn "The method Cursor#query_opts has been deprecated " +
|
358
|
+
"and will removed in v2.0. Use Cursor#options instead."
|
359
|
+
@options
|
360
|
+
end
|
361
|
+
|
362
|
+
# Add an option to the query options bitfield.
|
363
|
+
#
|
364
|
+
# @param opt a valid query option
|
365
|
+
#
|
366
|
+
# @raise InvalidOperation if this method is run after the cursor has bee
|
367
|
+
# iterated for the first time.
|
368
|
+
#
|
369
|
+
# @return [Integer] the current value of the options bitfield for this cursor.
|
370
|
+
#
|
371
|
+
# @see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-Mongo::Constants::OPQUERY
|
372
|
+
def add_option(opt)
|
373
|
+
check_modifiable
|
374
|
+
|
375
|
+
@options |= opt
|
376
|
+
@options
|
377
|
+
end
|
378
|
+
|
379
|
+
# Remove an option from the query options bitfield.
|
380
|
+
#
|
381
|
+
# @param opt a valid query option
|
382
|
+
#
|
383
|
+
# @raise InvalidOperation if this method is run after the cursor has bee
|
384
|
+
# iterated for the first time.
|
385
|
+
#
|
386
|
+
# @return [Integer] the current value of the options bitfield for this cursor.
|
387
|
+
#
|
388
|
+
# @see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-Mongo::Constants::OPQUERY
|
389
|
+
def remove_option(opt)
|
390
|
+
check_modifiable
|
391
|
+
|
392
|
+
@options &= ~opt
|
393
|
+
@options
|
322
394
|
end
|
323
395
|
|
324
396
|
# Get the query options for this Cursor.
|
@@ -341,7 +413,7 @@ module Mongo
|
|
341
413
|
# Clean output for inspect.
|
342
414
|
def inspect
|
343
415
|
"<Mongo::Cursor:0x#{object_id.to_s(16)} namespace='#{@db.name}.#{@collection.name}' " +
|
344
|
-
"@selector=#{@selector.inspect}>"
|
416
|
+
"@selector=#{@selector.inspect} @cursor_id=#{@cursor_id}>"
|
345
417
|
end
|
346
418
|
|
347
419
|
private
|
@@ -363,12 +435,43 @@ module Mongo
|
|
363
435
|
|
364
436
|
# Return the number of documents remaining for this cursor.
|
365
437
|
def num_remaining
|
366
|
-
|
438
|
+
if @cache.length == 0
|
439
|
+
if @query_run && (@options & OP_QUERY_EXHAUST != 0)
|
440
|
+
close
|
441
|
+
return 0
|
442
|
+
else
|
443
|
+
refresh
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
367
447
|
@cache.length
|
368
448
|
end
|
369
449
|
|
450
|
+
# Refresh the documents in @cache. This means either
|
451
|
+
# sending the initial query or sending a GET_MORE operation.
|
370
452
|
def refresh
|
371
|
-
|
453
|
+
if !@query_run
|
454
|
+
send_initial_query
|
455
|
+
elsif !@cursor_id.zero?
|
456
|
+
send_get_more
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
def send_initial_query
|
461
|
+
message = construct_query_message
|
462
|
+
payload = instrument_payload if @logger
|
463
|
+
instrument(:find, payload) do
|
464
|
+
results, @n_received, @cursor_id = @connection.receive_message(
|
465
|
+
Mongo::Constants::OP_QUERY, message, nil, @socket, @command,
|
466
|
+
@read_preference, @options & OP_QUERY_EXHAUST != 0)
|
467
|
+
@returned += @n_received
|
468
|
+
@cache += results
|
469
|
+
@query_run = true
|
470
|
+
close_cursor_if_query_complete
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
def send_get_more
|
372
475
|
message = BSON::ByteBuffer.new([0, 0, 0, 0])
|
373
476
|
|
374
477
|
# DB name.
|
@@ -387,37 +490,17 @@ module Mongo
|
|
387
490
|
|
388
491
|
# Cursor id.
|
389
492
|
message.put_long(@cursor_id)
|
390
|
-
|
493
|
+
log(:debug, "cursor.refresh() for cursor #{@cursor_id}") if @logger
|
391
494
|
results, @n_received, @cursor_id = @connection.receive_message(
|
392
|
-
Mongo::Constants::OP_GET_MORE, message, nil, @socket, @command)
|
495
|
+
Mongo::Constants::OP_GET_MORE, message, nil, @socket, @command, @read_preference)
|
393
496
|
@returned += @n_received
|
394
497
|
@cache += results
|
395
498
|
close_cursor_if_query_complete
|
396
499
|
end
|
397
500
|
|
398
|
-
# Run query the first time we request an object from the wire
|
399
|
-
# TODO: should we be calling instrument_payload even if logging
|
400
|
-
# is disabled?
|
401
|
-
def send_initial_query
|
402
|
-
if @query_run
|
403
|
-
false
|
404
|
-
else
|
405
|
-
message = construct_query_message
|
406
|
-
@connection.instrument(:find, instrument_payload) do
|
407
|
-
results, @n_received, @cursor_id = @connection.receive_message(
|
408
|
-
Mongo::Constants::OP_QUERY, message, nil, @socket, @command)
|
409
|
-
@returned += @n_received
|
410
|
-
@cache += results
|
411
|
-
@query_run = true
|
412
|
-
close_cursor_if_query_complete
|
413
|
-
end
|
414
|
-
true
|
415
|
-
end
|
416
|
-
end
|
417
|
-
|
418
501
|
def construct_query_message
|
419
502
|
message = BSON::ByteBuffer.new
|
420
|
-
message.put_int(
|
503
|
+
message.put_int(@options)
|
421
504
|
BSON::BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@collection.name}")
|
422
505
|
message.put_int(@skip)
|
423
506
|
message.put_int(@limit)
|
@@ -429,10 +512,10 @@ module Mongo
|
|
429
512
|
|
430
513
|
def instrument_payload
|
431
514
|
log = { :database => @db.name, :collection => @collection.name, :selector => selector }
|
432
|
-
log[:fields] = @fields
|
433
|
-
log[:skip]
|
434
|
-
log[:limit]
|
435
|
-
log[:order]
|
515
|
+
log[:fields] = @fields if @fields
|
516
|
+
log[:skip] = @skip if @skip && (@skip != 0)
|
517
|
+
log[:limit] = @limit if @limit && (@limit != 0)
|
518
|
+
log[:order] = @order if @order
|
436
519
|
log
|
437
520
|
end
|
438
521
|
|
@@ -455,10 +538,6 @@ module Mongo
|
|
455
538
|
@order || @explain || @hint || @snapshot
|
456
539
|
end
|
457
540
|
|
458
|
-
def to_s
|
459
|
-
"DBResponse(flags=#@result_flags, cursor_id=#@cursor_id, start=#@starting_from)"
|
460
|
-
end
|
461
|
-
|
462
541
|
def close_cursor_if_query_complete
|
463
542
|
if @limit > 0 && @returned >= @limit
|
464
543
|
close
|