moped 1.5.3 → 2.0.0.beta
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +42 -5
- data/README.md +1 -1
- data/lib/moped.rb +10 -13
- data/lib/moped/address.rb +56 -0
- data/lib/moped/authenticatable.rb +89 -0
- data/lib/moped/cluster.rb +169 -136
- data/lib/moped/collection.rb +53 -19
- data/lib/moped/connection.rb +69 -10
- data/lib/moped/connection/manager.rb +49 -0
- data/lib/moped/connection/pool.rb +198 -0
- data/lib/moped/connection/queue.rb +93 -0
- data/lib/moped/connection/reaper.rb +52 -0
- data/lib/moped/connection/socket.rb +4 -0
- data/lib/moped/connection/socket/connectable.rb +169 -0
- data/lib/moped/connection/socket/ssl.rb +52 -0
- data/lib/moped/connection/socket/tcp.rb +25 -0
- data/lib/moped/connection/sockets.rb +4 -0
- data/lib/moped/cursor.rb +3 -5
- data/lib/moped/database.rb +18 -24
- data/lib/moped/errors.rb +35 -6
- data/lib/moped/executable.rb +96 -0
- data/lib/moped/failover.rb +41 -0
- data/lib/moped/failover/disconnect.rb +31 -0
- data/lib/moped/failover/ignore.rb +29 -0
- data/lib/moped/failover/reconfigure.rb +34 -0
- data/lib/moped/failover/retry.rb +37 -0
- data/lib/moped/indexes.rb +4 -1
- data/lib/moped/instrumentable.rb +39 -0
- data/lib/moped/instrumentable/log.rb +43 -0
- data/lib/moped/instrumentable/noop.rb +31 -0
- data/lib/moped/loggable.rb +110 -0
- data/lib/moped/node.rb +316 -297
- data/lib/moped/operation.rb +3 -0
- data/lib/moped/operation/read.rb +62 -0
- data/lib/moped/operation/write.rb +57 -0
- data/lib/moped/protocol/command.rb +65 -4
- data/lib/moped/protocol/commands/authenticate.rb +1 -2
- data/lib/moped/protocol/delete.rb +16 -0
- data/lib/moped/protocol/get_more.rb +102 -31
- data/lib/moped/protocol/insert.rb +17 -0
- data/lib/moped/protocol/message.rb +44 -46
- data/lib/moped/protocol/query.rb +175 -92
- data/lib/moped/protocol/reply.rb +19 -8
- data/lib/moped/protocol/update.rb +18 -0
- data/lib/moped/query.rb +43 -17
- data/lib/moped/read_preference.rb +49 -0
- data/lib/moped/read_preference/nearest.rb +55 -0
- data/lib/moped/read_preference/primary.rb +60 -0
- data/lib/moped/read_preference/primary_preferred.rb +55 -0
- data/lib/moped/read_preference/secondary.rb +50 -0
- data/lib/moped/read_preference/secondary_preferred.rb +53 -0
- data/lib/moped/read_preference/selectable.rb +79 -0
- data/lib/moped/readable.rb +55 -0
- data/lib/moped/session.rb +122 -70
- data/lib/moped/{mongo_uri.rb → uri.rb} +75 -31
- data/lib/moped/version.rb +1 -1
- data/lib/moped/write_concern.rb +33 -0
- data/lib/moped/write_concern/propagate.rb +38 -0
- data/lib/moped/write_concern/unverified.rb +28 -0
- metadata +79 -44
- data/lib/moped/bson.rb +0 -45
- data/lib/moped/bson/binary.rb +0 -137
- data/lib/moped/bson/code.rb +0 -112
- data/lib/moped/bson/document.rb +0 -41
- data/lib/moped/bson/extensions.rb +0 -91
- data/lib/moped/bson/extensions/array.rb +0 -37
- data/lib/moped/bson/extensions/boolean.rb +0 -16
- data/lib/moped/bson/extensions/false_class.rb +0 -19
- data/lib/moped/bson/extensions/float.rb +0 -22
- data/lib/moped/bson/extensions/hash.rb +0 -39
- data/lib/moped/bson/extensions/integer.rb +0 -36
- data/lib/moped/bson/extensions/nil_class.rb +0 -19
- data/lib/moped/bson/extensions/object.rb +0 -11
- data/lib/moped/bson/extensions/regexp.rb +0 -38
- data/lib/moped/bson/extensions/string.rb +0 -45
- data/lib/moped/bson/extensions/symbol.rb +0 -33
- data/lib/moped/bson/extensions/time.rb +0 -23
- data/lib/moped/bson/extensions/true_class.rb +0 -19
- data/lib/moped/bson/max_key.rb +0 -51
- data/lib/moped/bson/min_key.rb +0 -51
- data/lib/moped/bson/object_id.rb +0 -301
- data/lib/moped/bson/timestamp.rb +0 -38
- data/lib/moped/bson/types.rb +0 -67
- data/lib/moped/logging.rb +0 -58
- data/lib/moped/session/context.rb +0 -115
- data/lib/moped/sockets/connectable.rb +0 -167
- data/lib/moped/sockets/ssl.rb +0 -50
- data/lib/moped/sockets/tcp.rb +0 -23
- data/lib/moped/threaded.rb +0 -69
@@ -0,0 +1,110 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Moped
|
3
|
+
|
4
|
+
# Contains behaviour for logging.
|
5
|
+
#
|
6
|
+
# @since 1.0.0
|
7
|
+
module Loggable
|
8
|
+
|
9
|
+
# Log the provided operations.
|
10
|
+
#
|
11
|
+
# @example Log the operations.
|
12
|
+
# Loggable.log_operations("MOPED", {}, 30)
|
13
|
+
#
|
14
|
+
# @param [ String ] prefix The prefix for all operations in the log.
|
15
|
+
# @param [ Array ] ops The operations.
|
16
|
+
# @param [ String ] runtime The runtime in formatted ms.
|
17
|
+
#
|
18
|
+
# @since 2.0.0
|
19
|
+
def self.log_operations(prefix, ops, runtime)
|
20
|
+
indent = " "*prefix.length
|
21
|
+
if ops.length == 1
|
22
|
+
Moped.logger.debug([ prefix, ops.first.log_inspect, "runtime: #{runtime}" ].join(' '))
|
23
|
+
else
|
24
|
+
first, *middle, last = ops
|
25
|
+
Moped.logger.debug([ prefix, first.log_inspect ].join(' '))
|
26
|
+
middle.each { |m| Moped.logger.debug([ indent, m.log_inspect ].join(' ')) }
|
27
|
+
Moped.logger.debug([ indent, last.log_inspect, "runtime: #{runtime}" ].join(' '))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Log the payload to debug.
|
32
|
+
#
|
33
|
+
# @example Log to debug.
|
34
|
+
# Loggable.debug("MOPED", payload "30.012ms")
|
35
|
+
#
|
36
|
+
# @param [ String ] prefix The log prefix.
|
37
|
+
# @param [ String ] payload The log operations.
|
38
|
+
# @param [ String ] runtime The runtime in formatted ms.
|
39
|
+
#
|
40
|
+
# @since 2.0.0
|
41
|
+
def self.debug(prefix, payload, runtime)
|
42
|
+
Moped.logger.debug([ prefix, payload, "runtime: #{runtime}" ].join(' '))
|
43
|
+
end
|
44
|
+
|
45
|
+
# Log the payload to warn.
|
46
|
+
#
|
47
|
+
# @example Log to warn.
|
48
|
+
# Loggable.warn("MOPED", payload "30.012ms")
|
49
|
+
#
|
50
|
+
# @param [ String ] prefix The log prefix.
|
51
|
+
# @param [ String ] payload The log operations.
|
52
|
+
# @param [ String ] runtime The runtime in formatted ms.
|
53
|
+
#
|
54
|
+
# @since 2.0.0
|
55
|
+
def self.warn(prefix, payload, runtime)
|
56
|
+
Moped.logger.warn([ prefix, payload, "runtime: #{runtime}" ].join(' '))
|
57
|
+
end
|
58
|
+
|
59
|
+
# Get the logger.
|
60
|
+
#
|
61
|
+
# @example Get the logger.
|
62
|
+
# Loggable.logger
|
63
|
+
#
|
64
|
+
# @return [ Logger ] The logger.
|
65
|
+
#
|
66
|
+
# @since 1.0.0
|
67
|
+
def logger
|
68
|
+
return @logger if defined?(@logger)
|
69
|
+
@logger = rails_logger || default_logger
|
70
|
+
end
|
71
|
+
|
72
|
+
# Get the rails logger.
|
73
|
+
#
|
74
|
+
# @example Get the rails logger.
|
75
|
+
# Loggable.rails_logger
|
76
|
+
#
|
77
|
+
# @return [ Logger ] The Rails logger.
|
78
|
+
#
|
79
|
+
# @since 1.0.0
|
80
|
+
def rails_logger
|
81
|
+
defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
|
82
|
+
end
|
83
|
+
|
84
|
+
# Get the default logger.
|
85
|
+
#
|
86
|
+
# @example Get the default logger.
|
87
|
+
# Loggable.default_logger
|
88
|
+
#
|
89
|
+
# @return [ Logger ] The default logger.
|
90
|
+
#
|
91
|
+
# @since 1.0.0
|
92
|
+
def default_logger
|
93
|
+
logger = Logger.new(STDOUT)
|
94
|
+
logger.level = Logger::INFO
|
95
|
+
logger
|
96
|
+
end
|
97
|
+
|
98
|
+
# Set the logger.
|
99
|
+
#
|
100
|
+
# @example Set the logger.
|
101
|
+
# Loggable.logger = logger
|
102
|
+
#
|
103
|
+
# @return [ Logger ] The logger.
|
104
|
+
#
|
105
|
+
# @since 1.0.0
|
106
|
+
def logger=(logger)
|
107
|
+
@logger = logger
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/moped/node.rb
CHANGED
@@ -1,28 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "moped/address"
|
3
|
+
require "moped/authenticatable"
|
4
|
+
require "moped/connection"
|
5
|
+
require "moped/executable"
|
6
|
+
require "moped/failover"
|
7
|
+
require "moped/instrumentable"
|
8
|
+
require "moped/operation"
|
9
|
+
|
1
10
|
module Moped
|
2
11
|
|
3
12
|
# Represents a client to a node in a server cluster.
|
4
13
|
#
|
5
|
-
# @
|
14
|
+
# @since 1.0.0
|
6
15
|
class Node
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
:resolved_address,
|
23
|
-
:timeout,
|
24
|
-
:options,
|
25
|
-
:refreshed_at
|
16
|
+
include Authenticatable
|
17
|
+
include Executable
|
18
|
+
include Instrumentable
|
19
|
+
|
20
|
+
# @!attribute address
|
21
|
+
# @return [ Address ] The address.
|
22
|
+
# @!attribute down_at
|
23
|
+
# @return [ Time ] The time the node was marked as down.
|
24
|
+
# @!attribute latency
|
25
|
+
# @return [ Integer ] The latency in milliseconds.
|
26
|
+
# @!attribute options
|
27
|
+
# @return [ Hash ] The node options.
|
28
|
+
# @!attribute refreshed_at
|
29
|
+
# @return [ Time ] The last time the node did a refresh.
|
30
|
+
attr_reader :address, :down_at, :latency, :options, :refreshed_at
|
26
31
|
|
27
32
|
# Is this node equal to another?
|
28
33
|
#
|
@@ -35,29 +40,21 @@ module Moped
|
|
35
40
|
#
|
36
41
|
# @since 1.0.0
|
37
42
|
def ==(other)
|
38
|
-
|
43
|
+
return false unless other.is_a?(Node)
|
44
|
+
address.resolved == other.address.resolved
|
39
45
|
end
|
40
46
|
alias :eql? :==
|
41
47
|
|
42
|
-
#
|
43
|
-
#
|
44
|
-
# @example Apply authentication.
|
45
|
-
# node.apply_auth([ :db, "user", "pass" ])
|
48
|
+
# Is the node an arbiter?
|
46
49
|
#
|
47
|
-
# @
|
50
|
+
# @example Is the node an arbiter?
|
51
|
+
# node.arbiter?
|
48
52
|
#
|
49
|
-
# @return [
|
53
|
+
# @return [ true, false ] If the node is an arbiter.
|
50
54
|
#
|
51
55
|
# @since 1.0.0
|
52
|
-
def
|
53
|
-
|
54
|
-
logouts = auth.keys - credentials.keys
|
55
|
-
logouts.each { |database| logout(database) }
|
56
|
-
credentials.each do |database, (username, password)|
|
57
|
-
login(database, username, password) unless auth[database] == [username, password]
|
58
|
-
end
|
59
|
-
end
|
60
|
-
self
|
56
|
+
def arbiter?
|
57
|
+
!!@arbiter
|
61
58
|
end
|
62
59
|
|
63
60
|
# Is the cluster auto-discovering new nodes in the cluster?
|
@@ -87,30 +84,65 @@ module Moped
|
|
87
84
|
#
|
88
85
|
# @since 1.0.0
|
89
86
|
def command(database, cmd, options = {})
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
87
|
+
read(Protocol::Command.new(database, cmd, options))
|
88
|
+
end
|
89
|
+
|
90
|
+
# Connect the node on the underlying connection.
|
91
|
+
#
|
92
|
+
# @example Connect the node.
|
93
|
+
# node.connect
|
94
|
+
#
|
95
|
+
# @raise [ Errors::ConnectionFailure ] If connection failed.
|
96
|
+
#
|
97
|
+
# @return [ true ] If the connection suceeded.
|
98
|
+
#
|
99
|
+
# @since 2.0.0
|
100
|
+
def connect
|
101
|
+
start = Time.now
|
102
|
+
connection{ |conn| conn.connect }
|
103
|
+
@latency = Time.now - start
|
104
|
+
@down_at = nil
|
105
|
+
true
|
106
|
+
end
|
107
|
+
|
108
|
+
# Is the node currently connected?
|
109
|
+
#
|
110
|
+
# @example Is the node connected?
|
111
|
+
# node.connected?
|
112
|
+
#
|
113
|
+
# @return [ true, false ] If the node is connected or not.
|
114
|
+
#
|
115
|
+
# @since 2.0.0
|
116
|
+
def connected?
|
117
|
+
connection{ |conn| conn.connected? }
|
118
|
+
end
|
119
|
+
|
120
|
+
# Get the underlying connection for the node.
|
121
|
+
#
|
122
|
+
# @example Get the node's connection.
|
123
|
+
# node.connection
|
124
|
+
#
|
125
|
+
# @return [ Connection ] The connection.
|
126
|
+
#
|
127
|
+
# @since 2.0.0
|
128
|
+
def connection
|
129
|
+
pool.with_connection do |conn|
|
130
|
+
yield(conn)
|
103
131
|
end
|
104
132
|
end
|
105
133
|
|
106
134
|
# Force the node to disconnect from the server.
|
107
135
|
#
|
108
|
-
# @
|
136
|
+
# @example Disconnect the node.
|
137
|
+
# node.disconnect
|
138
|
+
#
|
139
|
+
# @return [ true ] If the disconnection succeeded.
|
109
140
|
#
|
110
141
|
# @since 1.2.0
|
111
142
|
def disconnect
|
112
|
-
|
113
|
-
connection.disconnect
|
143
|
+
credentials.clear
|
144
|
+
connection{ |conn| conn.disconnect }
|
145
|
+
true
|
114
146
|
end
|
115
147
|
|
116
148
|
# Is the node down?
|
@@ -125,6 +157,20 @@ module Moped
|
|
125
157
|
@down_at
|
126
158
|
end
|
127
159
|
|
160
|
+
# Mark the node as down.
|
161
|
+
#
|
162
|
+
# @example Mark the node as down.
|
163
|
+
# node.down!
|
164
|
+
#
|
165
|
+
# @return [ nil ] Nothing.
|
166
|
+
#
|
167
|
+
# @since 2.0.0
|
168
|
+
def down!
|
169
|
+
@down_at = Time.new
|
170
|
+
@latency = nil
|
171
|
+
disconnect if connected?
|
172
|
+
end
|
173
|
+
|
128
174
|
# Yields the block if a connection can be established, retrying when a
|
129
175
|
# connection error is raised.
|
130
176
|
#
|
@@ -138,47 +184,16 @@ module Moped
|
|
138
184
|
# @return [ nil ] nil.
|
139
185
|
#
|
140
186
|
# @since 1.0.0
|
141
|
-
def ensure_connected
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
connect unless connected?
|
150
|
-
yield
|
151
|
-
rescue Errors::PotentialReconfiguration => e
|
152
|
-
if e.reconfiguring_replica_set?
|
153
|
-
raise Errors::ReplicaSetReconfigured.new(e.command, e.details)
|
154
|
-
elsif e.connection_failure?
|
155
|
-
raise Errors::ConnectionFailure.new(e.inspect)
|
156
|
-
end
|
157
|
-
raise
|
158
|
-
rescue Errors::DoNotDisconnect
|
159
|
-
# These exceptions are "expected" in the normal course of events, and
|
160
|
-
# don't necessitate disconnecting.
|
161
|
-
raise
|
162
|
-
rescue Errors::ConnectionFailure
|
163
|
-
disconnect
|
164
|
-
if retry_on_failure
|
165
|
-
# Maybe there was a hiccup -- try reconnecting one more time
|
166
|
-
retry_on_failure = false
|
167
|
-
retry
|
168
|
-
else
|
169
|
-
# Nope, we failed to connect twice. Flag the node as down and re-raise
|
170
|
-
# the exception.
|
171
|
-
down!
|
172
|
-
raise
|
187
|
+
def ensure_connected(&block)
|
188
|
+
return yield if executing?(:connection)
|
189
|
+
execute(:connection) do
|
190
|
+
begin
|
191
|
+
connect unless connected?
|
192
|
+
yield(self)
|
193
|
+
rescue Exception => e
|
194
|
+
Failover.get(e).execute(e, self, &block)
|
173
195
|
end
|
174
|
-
rescue
|
175
|
-
# Looks like we got an unexpected error, so we'll clean up the connection
|
176
|
-
# and re-raise the exception.
|
177
|
-
disconnect
|
178
|
-
raise $!.extend(Errors::SocketError)
|
179
196
|
end
|
180
|
-
ensure
|
181
|
-
Threaded.end(:connection)
|
182
197
|
end
|
183
198
|
|
184
199
|
# Set a flag on the node for the duration of provided block so that an
|
@@ -186,17 +201,16 @@ module Moped
|
|
186
201
|
#
|
187
202
|
# @example Ensure this node is primary.
|
188
203
|
# node.ensure_primary do
|
189
|
-
#
|
204
|
+
# node.command(ismaster: 1)
|
190
205
|
# end
|
191
206
|
#
|
192
207
|
# @return [ nil ] nil.
|
193
208
|
#
|
194
209
|
# @since 1.0.0
|
195
210
|
def ensure_primary
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
Threaded.end(:ensure_primary)
|
211
|
+
execute(:ensure_primary) do
|
212
|
+
yield(self)
|
213
|
+
end
|
200
214
|
end
|
201
215
|
|
202
216
|
# Execute a get more operation on the node.
|
@@ -214,9 +228,7 @@ module Moped
|
|
214
228
|
#
|
215
229
|
# @since 1.0.0
|
216
230
|
def get_more(database, collection, cursor_id, limit)
|
217
|
-
|
218
|
-
raise Moped::Errors::CursorNotFound.new("GET MORE", cursor_id) if reply.cursor_not_found?
|
219
|
-
reply
|
231
|
+
read(Protocol::GetMore.new(database, collection, cursor_id, limit))
|
220
232
|
end
|
221
233
|
|
222
234
|
# Get the hash identifier for the node.
|
@@ -228,7 +240,7 @@ module Moped
|
|
228
240
|
#
|
229
241
|
# @since 1.0.0
|
230
242
|
def hash
|
231
|
-
|
243
|
+
address.resolved.hash
|
232
244
|
end
|
233
245
|
|
234
246
|
# Creat the new node.
|
@@ -241,14 +253,15 @@ module Moped
|
|
241
253
|
#
|
242
254
|
# @since 1.0.0
|
243
255
|
def initialize(address, options = {})
|
244
|
-
@address = address
|
256
|
+
@address = Address.new(address)
|
245
257
|
@options = options
|
246
|
-
@timeout = options[:timeout] || 5
|
247
258
|
@down_at = nil
|
248
259
|
@refreshed_at = nil
|
260
|
+
@latency = nil
|
249
261
|
@primary = nil
|
250
262
|
@secondary = nil
|
251
|
-
|
263
|
+
@instrumenter = options[:instrumenter] || Instrumentable::Log
|
264
|
+
@address.resolve(self)
|
252
265
|
end
|
253
266
|
|
254
267
|
# Insert documents into the database.
|
@@ -263,8 +276,8 @@ module Moped
|
|
263
276
|
# @return [ Message ] The result of the operation.
|
264
277
|
#
|
265
278
|
# @since 1.0.0
|
266
|
-
def insert(database, collection, documents, options = {})
|
267
|
-
|
279
|
+
def insert(database, collection, documents, concern, options = {})
|
280
|
+
write(Protocol::Insert.new(database, collection, documents, options), concern)
|
268
281
|
end
|
269
282
|
|
270
283
|
# Kill all provided cursors on the node.
|
@@ -281,6 +294,20 @@ module Moped
|
|
281
294
|
process(Protocol::KillCursors.new(cursor_ids))
|
282
295
|
end
|
283
296
|
|
297
|
+
# Can we send messages to this node in normal cirucmstances? This is true
|
298
|
+
# only if the node is a primary or secondary node - arbiters or passives
|
299
|
+
# cannot be sent anything.
|
300
|
+
#
|
301
|
+
# @example Is the node messagable?
|
302
|
+
# node.messagable?
|
303
|
+
#
|
304
|
+
# @return [ true, false ] If messages can be sent to the node.
|
305
|
+
#
|
306
|
+
# @since 2.0.0
|
307
|
+
def messagable?
|
308
|
+
primary? || secondary?
|
309
|
+
end
|
310
|
+
|
284
311
|
# Does the node need to be refreshed?
|
285
312
|
#
|
286
313
|
# @example Does the node require refreshing?
|
@@ -295,6 +322,31 @@ module Moped
|
|
295
322
|
!refreshed_at || refreshed_at < time
|
296
323
|
end
|
297
324
|
|
325
|
+
# Is the node passive?
|
326
|
+
#
|
327
|
+
# @example Is the node passive?
|
328
|
+
# node.passive?
|
329
|
+
#
|
330
|
+
# @return [ true, false ] If the node is passive.
|
331
|
+
#
|
332
|
+
# @since 1.0.0
|
333
|
+
def passive?
|
334
|
+
!!@passive
|
335
|
+
end
|
336
|
+
|
337
|
+
# Get all the other nodes in the replica set according to the server
|
338
|
+
# information.
|
339
|
+
#
|
340
|
+
# @example Get the node's peers.
|
341
|
+
# node.peers
|
342
|
+
#
|
343
|
+
# @return [ Array<Node> ] The peers.
|
344
|
+
#
|
345
|
+
# @since 2.0.0
|
346
|
+
def peers
|
347
|
+
@peers ||= []
|
348
|
+
end
|
349
|
+
|
298
350
|
# Execute a pipeline of commands, for example a safe mode persist.
|
299
351
|
#
|
300
352
|
# @example Execute a pipeline.
|
@@ -305,14 +357,12 @@ module Moped
|
|
305
357
|
# @return [ nil ] nil.
|
306
358
|
#
|
307
359
|
# @since 1.0.0
|
360
|
+
# @todo: Remove with piggbacked gle.
|
308
361
|
def pipeline
|
309
|
-
|
310
|
-
|
311
|
-
yield
|
312
|
-
ensure
|
313
|
-
Threaded.end(:pipeline)
|
362
|
+
execute(:pipeline) do
|
363
|
+
yield(self)
|
314
364
|
end
|
315
|
-
flush unless
|
365
|
+
flush unless executing?(:pipeline)
|
316
366
|
end
|
317
367
|
|
318
368
|
# Is the node the replica set primary?
|
@@ -324,31 +374,29 @@ module Moped
|
|
324
374
|
#
|
325
375
|
# @since 1.0.0
|
326
376
|
def primary?
|
327
|
-
|
377
|
+
!!@primary
|
328
378
|
end
|
329
379
|
|
330
|
-
#
|
380
|
+
# Processes the provided operation on this node, and will execute the
|
381
|
+
# callback when the operation is sent to the database.
|
331
382
|
#
|
332
|
-
# @example
|
333
|
-
# node.
|
383
|
+
# @example Process a read operation.
|
384
|
+
# node.process(query) do |reply|
|
385
|
+
# return reply.documents
|
386
|
+
# end
|
334
387
|
#
|
335
|
-
# @
|
388
|
+
# @param [ Message ] operation The database operation.
|
389
|
+
# @param [ Proc ] callback The callback to run on operation completion.
|
336
390
|
#
|
337
|
-
# @
|
338
|
-
def arbiter?
|
339
|
-
@arbiter
|
340
|
-
end
|
341
|
-
|
342
|
-
# Is the node passive?
|
343
|
-
#
|
344
|
-
# @example Is the node passive?
|
345
|
-
# node.passive?
|
346
|
-
#
|
347
|
-
# @return [ true, false ] If the node is passive.
|
391
|
+
# @return [ Object ] The result of the callback.
|
348
392
|
#
|
349
393
|
# @since 1.0.0
|
350
|
-
def
|
351
|
-
|
394
|
+
def process(operation, &callback)
|
395
|
+
if executing?(:pipeline)
|
396
|
+
queue.push([ operation, callback ])
|
397
|
+
else
|
398
|
+
flush([[ operation, callback ]])
|
399
|
+
end
|
352
400
|
end
|
353
401
|
|
354
402
|
# Execute a query on the node.
|
@@ -367,25 +415,7 @@ module Moped
|
|
367
415
|
#
|
368
416
|
# @since 1.0.0
|
369
417
|
def query(database, collection, selector, options = {})
|
370
|
-
|
371
|
-
|
372
|
-
process(operation) do |reply|
|
373
|
-
if reply.query_failed?
|
374
|
-
if reply.unauthorized? && auth.has_key?(database)
|
375
|
-
# If we got here, most likely this is the case of Moped
|
376
|
-
# authenticating successfully against the node originally, but the
|
377
|
-
# node has been reset or gone down and come back up. The most
|
378
|
-
# common case here is a rs.stepDown() which will reinitialize the
|
379
|
-
# connection. In this case we need to requthenticate and try again,
|
380
|
-
# otherwise we'll just raise the error to the user.
|
381
|
-
login(database, *auth[database])
|
382
|
-
reply = query(database, collection, selector, options)
|
383
|
-
else
|
384
|
-
raise Errors::QueryFailure.new(operation, reply.documents.first)
|
385
|
-
end
|
386
|
-
end
|
387
|
-
reply
|
388
|
-
end
|
418
|
+
read(Protocol::Query.new(database, collection, selector, options))
|
389
419
|
end
|
390
420
|
|
391
421
|
# Refresh information about the node, such as it's status in the replica
|
@@ -403,27 +433,18 @@ module Moped
|
|
403
433
|
#
|
404
434
|
# @since 1.0.0
|
405
435
|
def refresh
|
406
|
-
if
|
436
|
+
if address.resolve(self)
|
407
437
|
begin
|
408
438
|
@refreshed_at = Time.now
|
409
|
-
|
410
|
-
primary
|
411
|
-
secondary = true if info["secondary"]
|
412
|
-
generate_peers(info)
|
413
|
-
|
414
|
-
@primary, @secondary = primary, secondary
|
415
|
-
@arbiter = info["arbiterOnly"]
|
416
|
-
@passive = info["passive"]
|
417
|
-
|
418
|
-
if !primary && Threaded.executing?(:ensure_primary)
|
439
|
+
configure(command("admin", ismaster: 1))
|
440
|
+
if !primary? && executing?(:ensure_primary)
|
419
441
|
raise Errors::ReplicaSetReconfigured.new("#{inspect} is no longer the primary node.", {})
|
420
|
-
elsif !
|
442
|
+
elsif !messagable?
|
421
443
|
# not primary or secondary so mark it as down, since it's probably
|
422
444
|
# a recovering node withing the replica set
|
423
445
|
down!
|
424
446
|
end
|
425
447
|
rescue Timeout::Error
|
426
|
-
@peers = []
|
427
448
|
down!
|
428
449
|
end
|
429
450
|
end
|
@@ -442,8 +463,8 @@ module Moped
|
|
442
463
|
# @return [ Message ] The result of the operation.
|
443
464
|
#
|
444
465
|
# @since 1.0.0
|
445
|
-
def remove(database, collection, selector, options = {})
|
446
|
-
|
466
|
+
def remove(database, collection, selector, concern, options = {})
|
467
|
+
write(Protocol::Delete.new(database, collection, selector, options), concern)
|
447
468
|
end
|
448
469
|
|
449
470
|
# Is the node a replica set secondary?
|
@@ -458,6 +479,18 @@ module Moped
|
|
458
479
|
@secondary
|
459
480
|
end
|
460
481
|
|
482
|
+
# Get the timeout, in seconds, for this node.
|
483
|
+
#
|
484
|
+
# @example Get the timeout in seconds.
|
485
|
+
# node.timeout
|
486
|
+
#
|
487
|
+
# @return [ Integer ] The configured timeout or the default of 5.
|
488
|
+
#
|
489
|
+
# @since 1.0.0
|
490
|
+
def timeout
|
491
|
+
@timeout ||= (options[:timeout] || 5)
|
492
|
+
end
|
493
|
+
|
461
494
|
# Execute an update command for the provided selector.
|
462
495
|
#
|
463
496
|
# @example Update documents.
|
@@ -472,8 +505,8 @@ module Moped
|
|
472
505
|
# @return [ Message ] The result of the operation.
|
473
506
|
#
|
474
507
|
# @since 1.0.0
|
475
|
-
def update(database, collection, selector, change, options = {})
|
476
|
-
|
508
|
+
def update(database, collection, selector, change, concern, options = {})
|
509
|
+
write(Protocol::Update.new(database, collection, selector, change, options), concern)
|
477
510
|
end
|
478
511
|
|
479
512
|
# Get the node as a nice formatted string.
|
@@ -485,117 +518,68 @@ module Moped
|
|
485
518
|
#
|
486
519
|
# @since 1.0.0
|
487
520
|
def inspect
|
488
|
-
"<#{self.class.name} resolved_address=#{
|
521
|
+
"<#{self.class.name} resolved_address=#{address.resolved.inspect}>"
|
489
522
|
end
|
490
523
|
|
491
524
|
private
|
492
525
|
|
493
|
-
|
494
|
-
@auth ||= {}
|
495
|
-
end
|
496
|
-
|
497
|
-
def login(database, username, password)
|
498
|
-
getnonce = Protocol::Command.new(database, getnonce: 1)
|
499
|
-
connection.write [getnonce]
|
500
|
-
result = connection.read.documents.first
|
501
|
-
raise Errors::OperationFailure.new(getnonce, result) unless result["ok"] == 1
|
502
|
-
authenticate = Protocol::Commands::Authenticate.new(database, username, password, result["nonce"])
|
503
|
-
connection.write [authenticate]
|
504
|
-
result = connection.read.documents.first
|
505
|
-
|
506
|
-
unless result["ok"] == 1
|
507
|
-
# See if we had connectivity issues so we can retry
|
508
|
-
e = Errors::PotentialReconfiguration.new(authenticate, result)
|
509
|
-
if e.reconfiguring_replica_set?
|
510
|
-
raise Errors::ReplicaSetReconfigured.new(e.command, e.details)
|
511
|
-
elsif e.connection_failure?
|
512
|
-
raise Errors::ConnectionFailure.new(e.inspect)
|
513
|
-
end
|
514
|
-
|
515
|
-
raise Errors::AuthenticationFailure.new(authenticate, result)
|
516
|
-
end
|
517
|
-
auth[database] = [username, password]
|
518
|
-
end
|
519
|
-
|
520
|
-
def logout(database)
|
521
|
-
command = Protocol::Command.new(database, logout: 1)
|
522
|
-
connection.write [command]
|
523
|
-
result = connection.read.documents.first
|
524
|
-
raise Errors::OperationFailure.new(command, result) unless result["ok"] == 1
|
525
|
-
|
526
|
-
auth.delete(database)
|
527
|
-
end
|
528
|
-
|
529
|
-
private
|
530
|
-
|
531
|
-
def generate_peers(info)
|
532
|
-
peers = []
|
533
|
-
if auto_discovering?
|
534
|
-
peers.push(info["primary"]) if info["primary"]
|
535
|
-
peers.concat(info["hosts"]) if info["hosts"]
|
536
|
-
peers.concat(info["passives"]) if info["passives"]
|
537
|
-
peers.concat(info["arbiters"]) if info["arbiters"]
|
538
|
-
end
|
539
|
-
@peers = peers.map{ |peer| discover(peer) }.uniq
|
540
|
-
end
|
541
|
-
|
542
|
-
def discover(peer)
|
543
|
-
Node.new(peer, options).tap do |node|
|
544
|
-
node.send(:auth).merge!(auth)
|
545
|
-
end
|
546
|
-
end
|
547
|
-
|
548
|
-
def initialize_copy(_)
|
549
|
-
@connection = nil
|
550
|
-
end
|
551
|
-
|
552
|
-
def connection
|
553
|
-
@connection ||= Connection.new(ip_address, port, timeout, options)
|
554
|
-
end
|
555
|
-
|
556
|
-
def connected?
|
557
|
-
connection.connected?
|
558
|
-
end
|
559
|
-
|
560
|
-
# Mark the node as down.
|
526
|
+
# Configure the node based on the return from the ismaster command.
|
561
527
|
#
|
562
|
-
#
|
563
|
-
def down!
|
564
|
-
@down_at = Time.new
|
565
|
-
|
566
|
-
disconnect
|
567
|
-
end
|
568
|
-
|
569
|
-
# Connect to the node.
|
528
|
+
# @api private
|
570
529
|
#
|
571
|
-
#
|
572
|
-
#
|
573
|
-
#
|
574
|
-
|
575
|
-
|
576
|
-
|
530
|
+
# @example Configure the node.
|
531
|
+
# node.configure(ismaster)
|
532
|
+
#
|
533
|
+
# @param [ Hash ] settings The result of the ismaster command.
|
534
|
+
#
|
535
|
+
# @since 2.0.0
|
536
|
+
def configure(settings)
|
537
|
+
@arbiter = settings["arbiterOnly"]
|
538
|
+
@passive = settings["passive"]
|
539
|
+
@primary = settings["ismaster"]
|
540
|
+
@secondary = settings["secondary"]
|
541
|
+
discover(settings["hosts"]) if auto_discovering?
|
577
542
|
end
|
578
543
|
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
544
|
+
# Discover the additional nodes.
|
545
|
+
#
|
546
|
+
# @api private
|
547
|
+
#
|
548
|
+
# @example Discover the additional nodes.
|
549
|
+
# node.discover([ "127.0.0.1:27019" ])
|
550
|
+
#
|
551
|
+
# @param [ Array<String> ] nodes The new nodes.
|
552
|
+
#
|
553
|
+
# @since 2.0.0
|
554
|
+
def discover(*nodes)
|
555
|
+
nodes.flatten.compact.each do |peer|
|
556
|
+
node = Node.new(peer, options)
|
557
|
+
node.credentials.merge!(credentials)
|
558
|
+
peers.push(node)
|
584
559
|
end
|
585
560
|
end
|
586
561
|
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
562
|
+
# Flush the node operations to the database.
|
563
|
+
#
|
564
|
+
# @api private
|
565
|
+
#
|
566
|
+
# @example Flush the operations.
|
567
|
+
# node.flush([ command ])
|
568
|
+
#
|
569
|
+
# @param [ Array<Message> ] ops The operations to flush.
|
570
|
+
#
|
571
|
+
# @return [ Object ] The result of the operations.
|
572
|
+
#
|
573
|
+
# @since 2.0.0
|
591
574
|
def flush(ops = queue)
|
592
575
|
operations, callbacks = ops.transpose
|
593
|
-
|
594
576
|
logging(operations) do
|
595
577
|
ensure_connected do
|
596
|
-
|
597
|
-
|
598
|
-
|
578
|
+
replies = nil
|
579
|
+
connection do |conn|
|
580
|
+
conn.write(operations)
|
581
|
+
replies = conn.receive_replies(operations)
|
582
|
+
end
|
599
583
|
replies.zip(callbacks).map do |reply, callback|
|
600
584
|
callback ? callback[reply] : reply
|
601
585
|
end.last
|
@@ -605,50 +589,85 @@ module Moped
|
|
605
589
|
ops.clear
|
606
590
|
end
|
607
591
|
|
592
|
+
# Yield the block with logging.
|
593
|
+
#
|
594
|
+
# @api private
|
595
|
+
#
|
596
|
+
# @example Yield with logging.
|
597
|
+
# logging(operations) do
|
598
|
+
# node.command(ismaster: 1)
|
599
|
+
# end
|
600
|
+
#
|
601
|
+
# @param [ Array<Message> ] operations The operations.
|
602
|
+
#
|
603
|
+
# @return [ Object ] The result of the yield.
|
604
|
+
#
|
605
|
+
# @since 2.0.0
|
608
606
|
def logging(operations)
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
log_operations(logger, operations, 1000 * (Time.new.to_f - instrument_start.to_f)) if instrument_start
|
607
|
+
instrument(TOPIC, prefix: " MOPED: #{address.resolved}", ops: operations) do
|
608
|
+
yield if block_given?
|
609
|
+
end
|
613
610
|
end
|
614
611
|
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
612
|
+
# Get the connection pool for the node.
|
613
|
+
#
|
614
|
+
# @api private
|
615
|
+
#
|
616
|
+
# @example Get the connection pool.
|
617
|
+
# node.pool
|
618
|
+
#
|
619
|
+
# @return [ Connection::Pool ] The connection pool.
|
620
|
+
#
|
621
|
+
# @since 2.0.0
|
622
|
+
def pool
|
623
|
+
@pool ||= Connection::Manager.pool(self)
|
624
|
+
end
|
624
625
|
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
626
|
+
# Execute a read operation.
|
627
|
+
#
|
628
|
+
# @api private
|
629
|
+
#
|
630
|
+
# @example Execute a read operation.
|
631
|
+
# node.read(operation)
|
632
|
+
#
|
633
|
+
# @param [ Message ] operation The read operation.
|
634
|
+
#
|
635
|
+
# @return [ Object ] The result of the read.
|
636
|
+
#
|
637
|
+
# @since 2.0.0
|
638
|
+
def read(operation)
|
639
|
+
Operation::Read.new(operation).execute(self)
|
629
640
|
end
|
630
641
|
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
642
|
+
# Execute a write operation.
|
643
|
+
#
|
644
|
+
# @api private
|
645
|
+
#
|
646
|
+
# @example Execute a write operation.
|
647
|
+
# node.write(operation, concern)
|
648
|
+
#
|
649
|
+
# @param [ Message ] operation The write operation.
|
650
|
+
# @param [ WriteConcern ] concern The write concern.
|
651
|
+
#
|
652
|
+
# @return [ Object ] The result of the write.
|
653
|
+
#
|
654
|
+
# @since 2.0.0
|
655
|
+
def write(operation, concern)
|
656
|
+
Operation::Write.new(operation, concern).execute(self)
|
645
657
|
end
|
646
658
|
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
659
|
+
# Get the queue of operations.
|
660
|
+
#
|
661
|
+
# @api private
|
662
|
+
#
|
663
|
+
# @example Get the operation queue.
|
664
|
+
# node.queue
|
665
|
+
#
|
666
|
+
# @return [ Array<Message> ] The queue of operations.
|
667
|
+
#
|
668
|
+
# @since 2.0.0
|
669
|
+
def queue
|
670
|
+
stack(:pipelined_operations)
|
652
671
|
end
|
653
672
|
end
|
654
673
|
end
|