moped 1.0.0.rc → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of moped might be problematic. Click here for more details.

Files changed (45) hide show
  1. data/CHANGELOG.md +11 -0
  2. data/README.md +32 -467
  3. data/lib/moped.rb +0 -1
  4. data/lib/moped/bson.rb +3 -0
  5. data/lib/moped/bson/binary.rb +4 -4
  6. data/lib/moped/bson/code.rb +12 -10
  7. data/lib/moped/bson/extensions.rb +64 -54
  8. data/lib/moped/bson/extensions/array.rb +9 -6
  9. data/lib/moped/bson/extensions/boolean.rb +1 -1
  10. data/lib/moped/bson/extensions/false_class.rb +5 -2
  11. data/lib/moped/bson/extensions/float.rb +1 -2
  12. data/lib/moped/bson/extensions/hash.rb +4 -5
  13. data/lib/moped/bson/extensions/integer.rb +2 -4
  14. data/lib/moped/bson/extensions/nil_class.rb +1 -2
  15. data/lib/moped/bson/extensions/object.rb +13 -0
  16. data/lib/moped/bson/extensions/regexp.rb +5 -6
  17. data/lib/moped/bson/extensions/string.rb +24 -13
  18. data/lib/moped/bson/extensions/symbol.rb +12 -14
  19. data/lib/moped/bson/extensions/time.rb +4 -4
  20. data/lib/moped/bson/extensions/true_class.rb +5 -2
  21. data/lib/moped/bson/max_key.rb +1 -2
  22. data/lib/moped/bson/min_key.rb +1 -2
  23. data/lib/moped/bson/object_id.rb +44 -4
  24. data/lib/moped/bson/types.rb +1 -1
  25. data/lib/moped/cluster.rb +13 -5
  26. data/lib/moped/collection.rb +5 -1
  27. data/lib/moped/connection.rb +4 -4
  28. data/lib/moped/database.rb +58 -29
  29. data/lib/moped/logging.rb +3 -3
  30. data/lib/moped/node.rb +35 -6
  31. data/lib/moped/protocol/command.rb +1 -4
  32. data/lib/moped/protocol/delete.rb +2 -0
  33. data/lib/moped/protocol/get_more.rb +16 -1
  34. data/lib/moped/protocol/insert.rb +2 -0
  35. data/lib/moped/protocol/kill_cursors.rb +2 -0
  36. data/lib/moped/protocol/message.rb +31 -12
  37. data/lib/moped/protocol/query.rb +15 -3
  38. data/lib/moped/protocol/reply.rb +6 -4
  39. data/lib/moped/protocol/update.rb +2 -0
  40. data/lib/moped/query.rb +60 -5
  41. data/lib/moped/session.rb +53 -17
  42. data/lib/moped/session/context.rb +17 -7
  43. data/lib/moped/threaded.rb +2 -1
  44. data/lib/moped/version.rb +1 -1
  45. metadata +11 -6
@@ -5,30 +5,28 @@ module Moped
5
5
  module Symbol
6
6
  module ClassMethods
7
7
  def __bson_load__(io)
8
- io.read(*io.read(4).unpack(INT32_PACK)).chop!.force_encoding('utf-8').intern
8
+ io.read(*io.read(4).unpack(INT32_PACK)).from_utf8_binary.chop!.intern
9
9
  end
10
10
  end
11
11
 
12
12
  def __bson_dump__(io, key)
13
13
  io << Types::SYMBOL
14
- io << key
15
- io << NULL_BYTE
16
-
17
- begin
18
- data = to_s.encode('utf-8')
19
- rescue EncodingError
20
- data = to_s.dup
21
- data.force_encoding('utf-8')
22
-
23
- raise unless data.valid_encoding?
24
- end
14
+ io << key.to_bson_cstring
25
15
 
26
- data.force_encoding('binary')
16
+ data = to_utf8_binary
27
17
 
28
- io << [data.bytesize+1].pack(INT32_PACK)
18
+ io << [ data.bytesize + 1 ].pack(INT32_PACK)
29
19
  io << data
30
20
  io << NULL_BYTE
31
21
  end
22
+
23
+ def to_bson_cstring
24
+ to_s.to_bson_cstring
25
+ end
26
+
27
+ def to_utf8_binary
28
+ to_s.to_utf8_binary
29
+ end
32
30
  end
33
31
  end
34
32
  end
@@ -5,15 +5,15 @@ module Moped
5
5
  module Time
6
6
  module ClassMethods
7
7
  def __bson_load__(io)
8
- at(io.read(8).unpack(INT64_PACK)[0]/1000.0)
8
+ seconds, fragment = io.read(8).unpack(INT64_PACK)[0].divmod 1000
9
+ at(seconds, fragment * 1000).utc
9
10
  end
10
11
  end
11
12
 
12
13
  def __bson_dump__(io, key)
13
14
  io << Types::TIME
14
- io << key
15
- io << NULL_BYTE
16
- io << [to_f * 1000].pack(INT64_PACK)
15
+ io << key.to_bson_cstring
16
+ io << [(to_i * 1000) + (usec / 1000)].pack(INT64_PACK)
17
17
  end
18
18
  end
19
19
  end
@@ -5,10 +5,13 @@ module Moped
5
5
  module TrueClass
6
6
  def __bson_dump__(io, key)
7
7
  io << Types::BOOLEAN
8
- io << key
9
- io << NULL_BYTE
8
+ io << key.to_bson_cstring
10
9
  io << Types::TRUE
11
10
  end
11
+
12
+ def __safe_options__
13
+ { safe: true }
14
+ end
12
15
  end
13
16
  end
14
17
  end
@@ -12,8 +12,7 @@ module Moped
12
12
 
13
13
  def __bson_dump__(io, key)
14
14
  io << Types::MAX_KEY
15
- io << key
16
- io << NULL_BYTE
15
+ io << key.to_bson_cstring
17
16
  end
18
17
  end
19
18
  end
@@ -12,8 +12,7 @@ module Moped
12
12
 
13
13
  def __bson_dump__(io, key)
14
14
  io << Types::MIN_KEY
15
- io << key
16
- io << NULL_BYTE
15
+ io << key.to_bson_cstring
17
16
  end
18
17
  end
19
18
  end
@@ -26,13 +26,17 @@ module Moped
26
26
 
27
27
  def from_data(data)
28
28
  id = allocate
29
- id.instance_variable_set :@data, data
29
+ id.send(:data=, data)
30
30
  id
31
31
  end
32
32
  end
33
33
 
34
34
  def data
35
- @data ||= @@generator.next
35
+ # If @data is defined, then we know we've been loaded in some
36
+ # non-standard way, so we attempt to repair the data.
37
+ repair! @data if defined? @data
38
+
39
+ @raw_data ||= @@generator.next
36
40
  end
37
41
 
38
42
  def ==(other)
@@ -48,6 +52,10 @@ module Moped
48
52
  @@string_format % data.unpack("C12")
49
53
  end
50
54
 
55
+ def to_json(*args)
56
+ "{\"$oid\": \"#{to_s}\"}"
57
+ end
58
+
51
59
  # Return the UTC time at which this ObjectId was generated. This may
52
60
  # be used instread of a created_at timestamp since this information
53
61
  # is always encoded in the object id.
@@ -63,8 +71,7 @@ module Moped
63
71
 
64
72
  def __bson_dump__(io, key)
65
73
  io << Types::OBJECT_ID
66
- io << key
67
- io << NULL_BYTE
74
+ io << key.to_bson_cstring
68
75
  io << data
69
76
  end
70
77
 
@@ -99,6 +106,39 @@ module Moped
99
106
  end
100
107
 
101
108
  @@generator = Generator.new
109
+
110
+ # @private
111
+ def marshal_dump
112
+ data
113
+ end
114
+
115
+ # @private
116
+ def marshal_load(data)
117
+ self.data = data
118
+ end
119
+
120
+ private
121
+
122
+ # Attempts to repair ObjectId data marshalled in previous formats.
123
+ #
124
+ # The first check covers an ObjectId generated by the mongo-ruby-driver.
125
+ #
126
+ # The second check covers an ObjectId generated by moped before a custom
127
+ # marshal strategy was added.
128
+ def repair!(data)
129
+ if data.is_a?(Array) && data.size == 12
130
+ self.data = data.pack("C*")
131
+ elsif data.is_a?(String) && data.size == 12
132
+ self.data = data
133
+ else
134
+ raise TypeError, "Could not convert #{data.inspect} into an ObjectId"
135
+ end
136
+ end
137
+
138
+ # Private interface for setting the internal data for an object id.
139
+ def data=(data)
140
+ @raw_data = data
141
+ end
102
142
  end
103
143
  end
104
144
  end
@@ -7,7 +7,7 @@ module Moped
7
7
  def self.__bson_load__(io)
8
8
  io.read 4 # swallow the length
9
9
 
10
- code = io.read(*io.read(4).unpack(INT32_PACK)).chop!.force_encoding('utf-8')
10
+ code = io.read(*io.read(4).unpack(INT32_PACK)).from_utf8_binary.chop!
11
11
  scope = BSON::Document.deserialize(io)
12
12
 
13
13
  Code.new code, scope
@@ -44,7 +44,7 @@ module Moped
44
44
 
45
45
  # Returns the list of available nodes, refreshing 1) any nodes which were
46
46
  # down and ready to be checked again and 2) any nodes whose information is
47
- # out of date.
47
+ # out of date. Arbiter nodes are not returned.
48
48
  #
49
49
  # @example Get the available nodes.
50
50
  # cluster.nodes
@@ -53,18 +53,22 @@ module Moped
53
53
  #
54
54
  # @since 1.0.0
55
55
  def nodes
56
+ current_time = Time.new
57
+ down_boundary = current_time - @options[:down_interval]
58
+ refresh_boundary = current_time - @options[:refresh_interval]
59
+
56
60
  # Find the nodes that were down but are ready to be refreshed, or those
57
61
  # with stale connection information.
58
62
  needs_refresh, available = @nodes.partition do |node|
59
- (node.down? && node.down_at < (Time.new - @options[:down_interval])) ||
60
- node.needs_refresh?(Time.new - @options[:refresh_interval])
63
+ (node.down? && node.down_at < down_boundary) || node.needs_refresh?(refresh_boundary)
61
64
  end
62
65
 
63
66
  # Refresh those nodes.
64
67
  available.concat refresh(needs_refresh)
65
68
 
66
- # Now return all the nodes that are available.
67
- available.reject(&:down?)
69
+ # Now return all the nodes that are available and participating in the
70
+ # replica set.
71
+ available.reject { |node| node.down? || node.arbiter? }
68
72
  end
69
73
 
70
74
  # Refreshes information for each of the nodes provided. The node list
@@ -193,6 +197,10 @@ module Moped
193
197
  end
194
198
  end
195
199
 
200
+ def inspect
201
+ "<#{self.class.name} nodes=#{@nodes.inspect}>"
202
+ end
203
+
196
204
  private
197
205
 
198
206
  def initialize_copy(_)
@@ -22,7 +22,11 @@ module Moped
22
22
  #
23
23
  # @since 1.0.0
24
24
  def drop
25
- database.command(drop: name)
25
+ begin
26
+ database.command(drop: name)
27
+ rescue Moped::Errors::OperationFailure => e
28
+ false
29
+ end
26
30
  end
27
31
 
28
32
  # Build a query for this collection.
@@ -116,7 +116,7 @@ module Moped
116
116
  # @since 1.0.0
117
117
  def receive_replies(operations)
118
118
  operations.map do |operation|
119
- read if operation.is_a?(Protocol::Query) || operation.is_a?(Protocol::GetMore)
119
+ operation.receive_replies(self)
120
120
  end
121
121
  end
122
122
 
@@ -189,9 +189,9 @@ module Moped
189
189
  # @since 1.0.0
190
190
  def connect(host, port, timeout)
191
191
  Timeout::timeout(timeout) do
192
- new(host, port).tap do |sock|
193
- sock.set_encoding('binary')
194
- end
192
+ sock = new(host, port)
193
+ sock.set_encoding('binary')
194
+ sock
195
195
  end
196
196
  end
197
197
  end
@@ -18,6 +18,64 @@ module Moped
18
18
  # @attribute [r] session The session.
19
19
  attr_reader :name, :session
20
20
 
21
+ # Get a collection by the provided name.
22
+ #
23
+ # @example Get a collection.
24
+ # session[:users]
25
+ #
26
+ # @param [ Symbol, String ] collection The collection name.
27
+ #
28
+ # @return [ Collection ] An instance of the collection.
29
+ #
30
+ # @since 1.0.0
31
+ def [](collection)
32
+ Collection.new(self, collection)
33
+ end
34
+
35
+ # Get all non-system collections from the database.
36
+ #
37
+ # @example Get all the collections.
38
+ # database.collections
39
+ #
40
+ # @return [ Array<Collection> ] All the collections.
41
+ #
42
+ # @since 1.0.0
43
+ def collections
44
+ collection_names.map{|name| Collection.new(self, name)}
45
+ end
46
+
47
+ # Get all non-system collection names from the database, this excludes
48
+ # indexes.
49
+ #
50
+ # @example Get all the collection names.
51
+ # database.collection_names
52
+ #
53
+ # @return [ Array<String> ] The names of all collections.
54
+ #
55
+ # @since 1.0.0
56
+ def collection_names
57
+ namespaces = Collection.new(self, "system.namespaces").find(name: { "$not" => /system|\$/ })
58
+ namespaces.map do |doc|
59
+ _name = doc["name"]
60
+ _name[name.length + 1, _name.length]
61
+ end
62
+ end
63
+
64
+ # Run +command+ on the database.
65
+ #
66
+ # @example Run a command.
67
+ # db.command(ismaster: 1)
68
+ # # => { "master" => true, hosts: [] }
69
+ #
70
+ # @param [ Hash ] command The command to run.
71
+ #
72
+ # @return [ Hash ] the result of the command.
73
+ #
74
+ # @since 1.0.0
75
+ def command(command)
76
+ session.context.command name, command
77
+ end
78
+
21
79
  # Drop the database.
22
80
  #
23
81
  # @example Drop the database.
@@ -67,34 +125,5 @@ module Moped
67
125
  def logout
68
126
  session.context.logout(name)
69
127
  end
70
-
71
- # Run +command+ on the database.
72
- #
73
- # @example Run a command.
74
- # db.command(ismaster: 1)
75
- # # => { "master" => true, hosts: [] }
76
- #
77
- # @param [ Hash ] command The command to run.
78
- #
79
- # @return [ Hash ] the result of the command.
80
- #
81
- # @since 1.0.0
82
- def command(command)
83
- session.context.command name, command
84
- end
85
-
86
- # Get a collection by the provided name.
87
- #
88
- # @example Get a collection.
89
- # session[:users]
90
- #
91
- # @param [ Symbol, String ] collection The collection name.
92
- #
93
- # @return [ Collection ] An instance of the collection.
94
- #
95
- # @since 1.0.0
96
- def [](collection)
97
- Collection.new(self, collection)
98
- end
99
128
  end
100
129
  end
@@ -37,9 +37,9 @@ module Moped
37
37
  #
38
38
  # @since 1.0.0
39
39
  def default_logger
40
- Logger.new(STDOUT).tap do |logger|
41
- logger.level = Logger::INFO
42
- end
40
+ logger = Logger.new(STDOUT)
41
+ logger.level = Logger::INFO
42
+ logger
43
43
  end
44
44
 
45
45
  # Set the logger.
@@ -186,7 +186,7 @@ module Moped
186
186
  #
187
187
  # @since 1.0.0
188
188
  def hash
189
- [ ip_address, port ].hash
189
+ resolved_address.hash
190
190
  end
191
191
 
192
192
  # Creat the new node.
@@ -288,6 +288,30 @@ module Moped
288
288
  @primary
289
289
  end
290
290
 
291
+ # Is the node an arbiter?
292
+ #
293
+ # @example Is the node an arbiter?
294
+ # node.arbiter?
295
+ #
296
+ # @return [ true, false ] If the node is an arbiter.
297
+ #
298
+ # @since 1.0.0
299
+ def arbiter?
300
+ @arbiter
301
+ end
302
+
303
+ # Is the node passive?
304
+ #
305
+ # @example Is the node passive?
306
+ # node.passive?
307
+ #
308
+ # @return [ true, false ] If the node is passive.
309
+ #
310
+ # @since 1.0.0
311
+ def passive?
312
+ @passive
313
+ end
314
+
291
315
  # Execute a query on the node.
292
316
  #
293
317
  # @example Execute a query.
@@ -343,6 +367,8 @@ module Moped
343
367
 
344
368
  @peers = peers.map { |peer| Node.new(peer) }
345
369
  @primary, @secondary = primary, secondary
370
+ @arbiter = info["arbiterOnly"]
371
+ @passive = info["passive"]
346
372
 
347
373
  if !primary && Threaded.executing?(:ensure_primary)
348
374
  raise Errors::ReplicaSetReconfigured, "#{inspect} is no longer the primary node."
@@ -396,6 +422,11 @@ module Moped
396
422
  process(Protocol::Update.new(database, collection, selector, change, options))
397
423
  end
398
424
 
425
+ def inspect
426
+ "<#{self.class.name} resolved_address=#{@resolved_address.inspect}>"
427
+ end
428
+
429
+
399
430
  private
400
431
 
401
432
  def auth
@@ -459,8 +490,6 @@ module Moped
459
490
  def connect
460
491
  connection.connect ip_address, port, timeout
461
492
  @down_at = nil
462
-
463
- refresh
464
493
  rescue Timeout::Error
465
494
  raise Errors::ConnectionFailure, "Timed out connection to Mongo on #{address}"
466
495
  rescue Errno::ECONNREFUSED
@@ -502,13 +531,13 @@ module Moped
502
531
  instrument_start = (logger = Moped.logger) && logger.debug? && Time.new
503
532
  yield
504
533
  ensure
505
- log_operations(logger, operations, Time.new - instrument_start) if instrument_start && !$!
534
+ log_operations(logger, operations, Time.new - instrument_start) if instrument_start
506
535
  end
507
536
 
508
537
  def log_operations(logger, ops, duration)
509
- prefix = " MOPED: #{address} "
538
+ prefix = " MOPED: #{resolved_address} "
510
539
  indent = " "*prefix.length
511
- runtime = (" (%.1fms)" % duration)
540
+ runtime = (" (%.4fms)" % duration)
512
541
 
513
542
  if ops.length == 1
514
543
  logger.debug prefix + ops.first.log_inspect + runtime