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.
- data/CHANGELOG.md +11 -0
- data/README.md +32 -467
- data/lib/moped.rb +0 -1
- data/lib/moped/bson.rb +3 -0
- data/lib/moped/bson/binary.rb +4 -4
- data/lib/moped/bson/code.rb +12 -10
- data/lib/moped/bson/extensions.rb +64 -54
- data/lib/moped/bson/extensions/array.rb +9 -6
- data/lib/moped/bson/extensions/boolean.rb +1 -1
- data/lib/moped/bson/extensions/false_class.rb +5 -2
- data/lib/moped/bson/extensions/float.rb +1 -2
- data/lib/moped/bson/extensions/hash.rb +4 -5
- data/lib/moped/bson/extensions/integer.rb +2 -4
- data/lib/moped/bson/extensions/nil_class.rb +1 -2
- data/lib/moped/bson/extensions/object.rb +13 -0
- data/lib/moped/bson/extensions/regexp.rb +5 -6
- data/lib/moped/bson/extensions/string.rb +24 -13
- data/lib/moped/bson/extensions/symbol.rb +12 -14
- data/lib/moped/bson/extensions/time.rb +4 -4
- data/lib/moped/bson/extensions/true_class.rb +5 -2
- data/lib/moped/bson/max_key.rb +1 -2
- data/lib/moped/bson/min_key.rb +1 -2
- data/lib/moped/bson/object_id.rb +44 -4
- data/lib/moped/bson/types.rb +1 -1
- data/lib/moped/cluster.rb +13 -5
- data/lib/moped/collection.rb +5 -1
- data/lib/moped/connection.rb +4 -4
- data/lib/moped/database.rb +58 -29
- data/lib/moped/logging.rb +3 -3
- data/lib/moped/node.rb +35 -6
- data/lib/moped/protocol/command.rb +1 -4
- data/lib/moped/protocol/delete.rb +2 -0
- data/lib/moped/protocol/get_more.rb +16 -1
- data/lib/moped/protocol/insert.rb +2 -0
- data/lib/moped/protocol/kill_cursors.rb +2 -0
- data/lib/moped/protocol/message.rb +31 -12
- data/lib/moped/protocol/query.rb +15 -3
- data/lib/moped/protocol/reply.rb +6 -4
- data/lib/moped/protocol/update.rb +2 -0
- data/lib/moped/query.rb +60 -5
- data/lib/moped/session.rb +53 -17
- data/lib/moped/session/context.rb +17 -7
- data/lib/moped/threaded.rb +2 -1
- data/lib/moped/version.rb +1 -1
- 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!.
|
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
|
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
|
-
|
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 <<
|
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
|
data/lib/moped/bson/max_key.rb
CHANGED
data/lib/moped/bson/min_key.rb
CHANGED
data/lib/moped/bson/object_id.rb
CHANGED
@@ -26,13 +26,17 @@ module Moped
|
|
26
26
|
|
27
27
|
def from_data(data)
|
28
28
|
id = allocate
|
29
|
-
id.
|
29
|
+
id.send(:data=, data)
|
30
30
|
id
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
34
|
def data
|
35
|
-
@data
|
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
|
data/lib/moped/bson/types.rb
CHANGED
@@ -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
|
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
|
data/lib/moped/cluster.rb
CHANGED
@@ -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 <
|
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
|
-
|
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(_)
|
data/lib/moped/collection.rb
CHANGED
data/lib/moped/connection.rb
CHANGED
@@ -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
|
-
|
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)
|
193
|
-
|
194
|
-
|
192
|
+
sock = new(host, port)
|
193
|
+
sock.set_encoding('binary')
|
194
|
+
sock
|
195
195
|
end
|
196
196
|
end
|
197
197
|
end
|
data/lib/moped/database.rb
CHANGED
@@ -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
|
data/lib/moped/logging.rb
CHANGED
data/lib/moped/node.rb
CHANGED
@@ -186,7 +186,7 @@ module Moped
|
|
186
186
|
#
|
187
187
|
# @since 1.0.0
|
188
188
|
def hash
|
189
|
-
|
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: #{
|
538
|
+
prefix = " MOPED: #{resolved_address} "
|
510
539
|
indent = " "*prefix.length
|
511
|
-
runtime = (" (%.
|
540
|
+
runtime = (" (%.4fms)" % duration)
|
512
541
|
|
513
542
|
if ops.length == 1
|
514
543
|
logger.debug prefix + ops.first.log_inspect + runtime
|