moped 1.0.0.beta → 1.0.0.rc
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.
Potentially problematic release.
This version of moped might be problematic. Click here for more details.
- data/LICENSE +20 -0
- data/README.md +11 -8
- data/lib/moped/bson.rb +17 -0
- data/lib/moped/bson/extensions/hash.rb +2 -2
- data/lib/moped/bson/extensions/regexp.rb +2 -2
- data/lib/moped/bson/extensions/symbol.rb +13 -3
- data/lib/moped/cluster.rb +96 -39
- data/lib/moped/collection.rb +54 -31
- data/lib/moped/connection.rb +152 -47
- data/lib/moped/cursor.rb +91 -34
- data/lib/moped/database.rb +52 -20
- data/lib/moped/errors.rb +52 -22
- data/lib/moped/indexes.rb +53 -27
- data/lib/moped/logging.rb +35 -2
- data/lib/moped/node.rb +316 -126
- data/lib/moped/protocol/delete.rb +1 -0
- data/lib/moped/protocol/get_more.rb +1 -0
- data/lib/moped/protocol/insert.rb +1 -0
- data/lib/moped/protocol/kill_cursors.rb +1 -0
- data/lib/moped/protocol/message.rb +0 -2
- data/lib/moped/protocol/query.rb +1 -0
- data/lib/moped/protocol/update.rb +1 -0
- data/lib/moped/query.rb +183 -113
- data/lib/moped/session.rb +148 -96
- data/lib/moped/threaded.rb +41 -5
- data/lib/moped/version.rb +1 -1
- metadata +5 -5
- data/MIT_LICENSE +0 -19
data/lib/moped/errors.rb
CHANGED
@@ -8,35 +8,70 @@ module Moped
|
|
8
8
|
# Generic error class for exceptions related to connection failures.
|
9
9
|
class ConnectionFailure < StandardError; end
|
10
10
|
|
11
|
-
#
|
12
|
-
|
11
|
+
# Raised when providing an invalid string from an object id.
|
12
|
+
class InvalidObjectId < StandardError
|
13
|
+
|
14
|
+
# Create the new error.
|
15
|
+
#
|
16
|
+
# @example Create the new error.
|
17
|
+
# InvalidObjectId.new("test")
|
18
|
+
#
|
19
|
+
# @param [ String ] string The provided id.
|
20
|
+
#
|
21
|
+
# @since 1.0.0
|
22
|
+
def initialize(string)
|
23
|
+
super("'#{string}' is not a valid object id.")
|
24
|
+
end
|
25
|
+
end
|
13
26
|
|
14
27
|
# Generic error class for exceptions generated on the remote MongoDB
|
15
28
|
# server.
|
16
29
|
class MongoError < StandardError
|
17
|
-
# @return the command that generated the error
|
18
|
-
attr_reader :command
|
19
30
|
|
20
|
-
# @
|
21
|
-
|
31
|
+
# @attribute [r] details The details about the error.
|
32
|
+
# @attribute [r] command The command that generated the error.
|
33
|
+
attr_reader :details, :command
|
22
34
|
|
23
35
|
# Create a new operation failure exception.
|
24
36
|
#
|
25
|
-
# @
|
26
|
-
#
|
37
|
+
# @example Create the new error.
|
38
|
+
# MongoError.new(command, details)
|
39
|
+
#
|
40
|
+
# @param [ Object ] command The command that generated the error.
|
41
|
+
# @param [ Hash ] details The details about the error.
|
42
|
+
#
|
43
|
+
# @since 1.0.0
|
27
44
|
def initialize(command, details)
|
28
|
-
@command = command
|
29
|
-
|
30
|
-
|
31
|
-
super build_message
|
45
|
+
@command, @details = command, details
|
46
|
+
super(build_message)
|
32
47
|
end
|
33
48
|
|
34
49
|
private
|
35
50
|
|
51
|
+
# Build the error message.
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
#
|
55
|
+
# @example Build the message.
|
56
|
+
# error.build_message
|
57
|
+
#
|
58
|
+
# @return [ String ] The message.
|
59
|
+
#
|
60
|
+
# @since 1.0.0
|
36
61
|
def build_message
|
37
62
|
"The operation: #{command.inspect}\n#{error_message}"
|
38
63
|
end
|
39
64
|
|
65
|
+
# Get the error message.
|
66
|
+
#
|
67
|
+
# @api private
|
68
|
+
#
|
69
|
+
# @example Get the error message.
|
70
|
+
# error.error_message
|
71
|
+
#
|
72
|
+
# @return [ String ] The message.
|
73
|
+
#
|
74
|
+
# @since 1.0.0
|
40
75
|
def error_message
|
41
76
|
err = details["err"] || details["errmsg"] || details["$err"]
|
42
77
|
|
@@ -49,6 +84,9 @@ module Moped
|
|
49
84
|
end
|
50
85
|
end
|
51
86
|
|
87
|
+
# Exception raised when authentication fails.
|
88
|
+
class AuthenticationFailure < MongoError; end
|
89
|
+
|
52
90
|
# Exception class for exceptions generated as a direct result of an
|
53
91
|
# operation, such as a failed insert or an invalid command.
|
54
92
|
class OperationFailure < MongoError; end
|
@@ -56,21 +94,13 @@ module Moped
|
|
56
94
|
# Exception raised on invalid queries.
|
57
95
|
class QueryFailure < MongoError; end
|
58
96
|
|
59
|
-
# Exception raised when authentication fails.
|
60
|
-
class AuthenticationFailure < MongoError; end
|
61
|
-
|
62
|
-
# Raised when providing an invalid string from an object id.
|
63
|
-
class InvalidObjectId < StandardError
|
64
|
-
def initialize(string)
|
65
|
-
super("'#{string}' is not a valid object id.")
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
97
|
# @api private
|
70
98
|
#
|
71
99
|
# Internal exception raised by Node#ensure_primary and captured by
|
72
100
|
# Cluster#with_primary.
|
73
101
|
class ReplicaSetReconfigured < StandardError; end
|
74
102
|
|
103
|
+
# Tag applied to unhandled exceptions on a node.
|
104
|
+
module SocketError; end
|
75
105
|
end
|
76
106
|
end
|
data/lib/moped/indexes.rb
CHANGED
@@ -1,39 +1,31 @@
|
|
1
1
|
module Moped
|
2
|
+
|
3
|
+
# Defines behaviour around indexes.
|
2
4
|
class Indexes
|
3
5
|
include Enumerable
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
attr_reader :collection_name
|
9
|
-
attr_reader :namespace
|
10
|
-
|
11
|
-
def initialize(database, collection_name)
|
12
|
-
@database = database
|
13
|
-
@collection_name = collection_name
|
14
|
-
@namespace = "#{database.name}.#{collection_name}"
|
15
|
-
end
|
16
|
-
|
17
|
-
public
|
7
|
+
# @attribute [r] collection_name The collection name.
|
8
|
+
# @attribute [r] database The database.
|
9
|
+
# @attribute [r] namespace The index namespace.
|
10
|
+
attr_reader :collection_name, :database, :namespace
|
18
11
|
|
19
12
|
# Retrive an index by its definition.
|
20
13
|
#
|
21
|
-
# @
|
22
|
-
# @return [Hash, nil] the index with the provided key, or nil.
|
23
|
-
#
|
24
|
-
# @example
|
14
|
+
# @example Get the index.
|
25
15
|
# session[:users].indexes[id: 1]
|
26
16
|
# # => {"v"=>1, "key"=>{"_id"=>1}, "ns"=>"moped_test.users", "name"=>"_id_" }
|
17
|
+
#
|
18
|
+
# @param [ Hash ] key The index definition.
|
19
|
+
#
|
20
|
+
# @return [ Hash, nil ] The index with the provided key, or nil.
|
21
|
+
#
|
22
|
+
# @since 1.0.0
|
27
23
|
def [](key)
|
28
24
|
database[:"system.indexes"].find(ns: namespace, key: key).one
|
29
25
|
end
|
30
26
|
|
31
27
|
# Create an index unless it already exists.
|
32
28
|
#
|
33
|
-
# @param [Hash] key the index spec
|
34
|
-
# @param [Hash] options the options for the index.
|
35
|
-
# @see http://www.mongodb.org/display/DOCS/Indexes#Indexes-CreationOptions
|
36
|
-
#
|
37
29
|
# @example Without options
|
38
30
|
# session[:users].indexes.create(name: 1)
|
39
31
|
# session[:users].indexes[name: 1]
|
@@ -51,18 +43,23 @@ module Moped
|
|
51
43
|
# "ns"=>"moped_test.users",
|
52
44
|
# "dropDups"=>true,
|
53
45
|
# "name"=>"location_2d_name_1"}
|
46
|
+
#
|
47
|
+
# @param [ Hash ] key The index spec.
|
48
|
+
# @param [ Hash ] options The options for the index.
|
49
|
+
#
|
50
|
+
# @return [ Hash ] The index spec.
|
51
|
+
#
|
52
|
+
# @see http://www.mongodb.org/display/DOCS/Indexes#Indexes-CreationOptions
|
53
|
+
#
|
54
|
+
# @since 1.0.0
|
54
55
|
def create(key, options = {})
|
55
56
|
spec = options.merge(ns: namespace, key: key)
|
56
57
|
spec[:name] ||= key.to_a.join("_")
|
57
|
-
|
58
58
|
database[:"system.indexes"].insert(spec)
|
59
59
|
end
|
60
60
|
|
61
61
|
# Drop an index, or all indexes.
|
62
62
|
#
|
63
|
-
# @param [Hash] key the index's key
|
64
|
-
# @return [Boolean] whether the indexes were dropped.
|
65
|
-
#
|
66
63
|
# @example Drop all indexes
|
67
64
|
# session[:users].indexes.count # => 3
|
68
65
|
# # Does not drop the _id index
|
@@ -72,6 +69,12 @@ module Moped
|
|
72
69
|
# @example Drop a particular index
|
73
70
|
# session[:users].indexes.drop(name: 1)
|
74
71
|
# session[:users].indexes[name: 1] # => nil
|
72
|
+
#
|
73
|
+
# @param [ Hash ] key The index's key.
|
74
|
+
#
|
75
|
+
# @return [ Boolean ] Whether the indexes were dropped.
|
76
|
+
#
|
77
|
+
# @since 1.0.0
|
75
78
|
def drop(key = nil)
|
76
79
|
if key
|
77
80
|
index = self[key] or return false
|
@@ -79,15 +82,38 @@ module Moped
|
|
79
82
|
else
|
80
83
|
name = "*"
|
81
84
|
end
|
82
|
-
|
83
85
|
result = database.command deleteIndexes: collection_name, index: name
|
84
86
|
result["ok"] == 1
|
85
87
|
end
|
86
88
|
|
87
|
-
#
|
89
|
+
# Iterate over each of the indexes for the collection.
|
90
|
+
#
|
91
|
+
# @example Iterate over the indexes.
|
92
|
+
# indexes.each do |spec|
|
93
|
+
# #...
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# @return [ Enumerator ] The enumerator.
|
97
|
+
#
|
98
|
+
# @since 1.0.0
|
99
|
+
#
|
100
|
+
# @yield [ Hash ] Each index for the collection.
|
88
101
|
def each(&block)
|
89
102
|
database[:"system.indexes"].find(ns: namespace).each(&block)
|
90
103
|
end
|
91
104
|
|
105
|
+
# Initialize the indexes.
|
106
|
+
#
|
107
|
+
# @example Create the new indexes.
|
108
|
+
# Indexes.new(database, :artists)
|
109
|
+
#
|
110
|
+
# @param [ Database ] database The database.
|
111
|
+
# @param [ String, Symbol ] collection_name The name of the collection.
|
112
|
+
#
|
113
|
+
# @since 1.0.0
|
114
|
+
def initialize(database, collection_name)
|
115
|
+
@database, @collection_name = database, collection_name
|
116
|
+
@namespace = "#{database.name}.#{collection_name}"
|
117
|
+
end
|
92
118
|
end
|
93
119
|
end
|
data/lib/moped/logging.rb
CHANGED
@@ -1,25 +1,58 @@
|
|
1
1
|
module Moped
|
2
|
+
|
3
|
+
# Contains behaviour for logging.
|
2
4
|
module Logging
|
5
|
+
|
6
|
+
# Get the logger.
|
7
|
+
#
|
8
|
+
# @example Get the logger.
|
9
|
+
# Logging.logger
|
10
|
+
#
|
11
|
+
# @return [ Logger ] The logger.
|
12
|
+
#
|
13
|
+
# @since 1.0.0
|
3
14
|
def logger
|
4
15
|
return @logger if defined?(@logger)
|
5
|
-
|
6
16
|
@logger = rails_logger || default_logger
|
7
17
|
end
|
8
18
|
|
19
|
+
# Get the rails logger.
|
20
|
+
#
|
21
|
+
# @example Get the rails logger.
|
22
|
+
# Logging.rails_logger
|
23
|
+
#
|
24
|
+
# @return [ Logger ] The Rails logger.
|
25
|
+
#
|
26
|
+
# @since 1.0.0
|
9
27
|
def rails_logger
|
10
28
|
defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
|
11
29
|
end
|
12
30
|
|
31
|
+
# Get the default logger.
|
32
|
+
#
|
33
|
+
# @example Get the default logger.
|
34
|
+
# Logging.default_logger
|
35
|
+
#
|
36
|
+
# @return [ Logger ] The default logger.
|
37
|
+
#
|
38
|
+
# @since 1.0.0
|
13
39
|
def default_logger
|
14
40
|
Logger.new(STDOUT).tap do |logger|
|
15
41
|
logger.level = Logger::INFO
|
16
42
|
end
|
17
43
|
end
|
18
44
|
|
45
|
+
# Set the logger.
|
46
|
+
#
|
47
|
+
# @example Set the logger.
|
48
|
+
# Logging.logger = logger
|
49
|
+
#
|
50
|
+
# @return [ Logger ] The logger.
|
51
|
+
#
|
52
|
+
# @since 1.0.0
|
19
53
|
def logger=(logger)
|
20
54
|
@logger = logger
|
21
55
|
end
|
22
56
|
end
|
23
|
-
|
24
57
|
extend Logging
|
25
58
|
end
|
data/lib/moped/node.rb
CHANGED
@@ -1,139 +1,111 @@
|
|
1
1
|
module Moped
|
2
|
-
class Node
|
3
|
-
|
4
|
-
attr_reader :address
|
5
|
-
attr_reader :resolved_address
|
6
|
-
attr_reader :ip_address
|
7
|
-
attr_reader :port
|
8
2
|
|
9
|
-
|
10
|
-
|
3
|
+
# Represents a client to a node in a server cluster.
|
4
|
+
#
|
5
|
+
# @api private
|
6
|
+
class Node
|
11
7
|
|
12
|
-
|
13
|
-
|
8
|
+
# @attribute [r] address The address of the node.
|
9
|
+
# @attribute [r] down_at The time the server node went down.
|
10
|
+
# @attribute [r] ip_address The node's ip.
|
11
|
+
# @attribute [r] peers Other peers in the replica set.
|
12
|
+
# @attribute [r] port The connection port.
|
13
|
+
# @attribute [r] resolved_address The host/port pair.
|
14
|
+
# @attribute [r] timeout The connection timeout.
|
15
|
+
attr_reader :address, :down_at, :ip_address, :peers, :port, :resolved_address, :timeout
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
# Is this node equal to another?
|
18
|
+
#
|
19
|
+
# @example Is the node equal to another.
|
20
|
+
# node == other
|
21
|
+
#
|
22
|
+
# @param [ Node ] other The other node.
|
23
|
+
#
|
24
|
+
# @return [ true, false ] If the addresses are equal.
|
25
|
+
#
|
26
|
+
# @since 1.0.0
|
27
|
+
def ==(other)
|
28
|
+
resolved_address == other.resolved_address
|
29
|
+
end
|
30
|
+
alias :eql? :==
|
19
31
|
|
20
|
-
|
32
|
+
# Apply the authentication details to this node.
|
33
|
+
#
|
34
|
+
# @example Apply authentication.
|
35
|
+
# node.apply_auth([ :db, "user", "pass" ])
|
36
|
+
#
|
37
|
+
# @param [ Array<String> ] credentials The db, username, and password.
|
38
|
+
#
|
39
|
+
# @return [ Node ] The authenticated node.
|
40
|
+
#
|
41
|
+
# @since 1.0.0
|
42
|
+
def apply_auth(credentials)
|
43
|
+
unless auth == credentials
|
44
|
+
logouts = auth.keys - credentials.keys
|
45
|
+
logouts.each { |database| logout(database) }
|
46
|
+
credentials.each do |database, (username, password)|
|
47
|
+
login(database, username, password) unless auth[database] == [username, password]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
self
|
21
51
|
end
|
22
52
|
|
53
|
+
# Execute a command against a database.
|
54
|
+
#
|
55
|
+
# @example Execute a command.
|
56
|
+
# node.command(database, { ping: 1 })
|
57
|
+
#
|
58
|
+
# @param [ Database ] database The database to run the command on.
|
59
|
+
# @param [ Hash ] cmd The command to execute.
|
60
|
+
# @options [ Hash ] options The command options.
|
61
|
+
#
|
62
|
+
# @raise [ OperationFailure ] If the command failed.
|
63
|
+
#
|
64
|
+
# @return [ Hash ] The result of the command.
|
65
|
+
#
|
66
|
+
# @since 1.0.0
|
23
67
|
def command(database, cmd, options = {})
|
24
68
|
operation = Protocol::Command.new(database, cmd, options)
|
25
69
|
|
26
70
|
process(operation) do |reply|
|
27
71
|
result = reply.documents[0]
|
28
|
-
|
29
72
|
raise Errors::OperationFailure.new(
|
30
73
|
operation, result
|
31
74
|
) if result["ok"] != 1 || result["err"] || result["errmsg"]
|
32
|
-
|
33
75
|
result
|
34
76
|
end
|
35
77
|
end
|
36
78
|
|
37
|
-
|
38
|
-
process Protocol::KillCursors.new(cursor_ids)
|
39
|
-
end
|
40
|
-
|
41
|
-
def get_more(database, collection, cursor_id, limit)
|
42
|
-
process Protocol::GetMore.new(database, collection, cursor_id, limit)
|
43
|
-
end
|
44
|
-
|
45
|
-
def remove(database, collection, selector, options = {})
|
46
|
-
process Protocol::Delete.new(database, collection, selector, options)
|
47
|
-
end
|
48
|
-
|
49
|
-
def update(database, collection, selector, change, options = {})
|
50
|
-
process Protocol::Update.new(database, collection, selector, change, options)
|
51
|
-
end
|
52
|
-
|
53
|
-
def insert(database, collection, documents)
|
54
|
-
process Protocol::Insert.new(database, collection, documents)
|
55
|
-
end
|
56
|
-
|
57
|
-
def query(database, collection, selector, options = {})
|
58
|
-
operation = Protocol::Query.new(database, collection, selector, options)
|
59
|
-
|
60
|
-
process operation do |reply|
|
61
|
-
if reply.flags.include? :query_failure
|
62
|
-
raise Errors::QueryFailure.new(operation, reply.documents.first)
|
63
|
-
end
|
64
|
-
|
65
|
-
reply
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# @return [true/false] whether the node needs to be refreshed.
|
70
|
-
def needs_refresh?(time)
|
71
|
-
!@refreshed_at || @refreshed_at < time
|
72
|
-
end
|
73
|
-
|
74
|
-
def primary?
|
75
|
-
@primary
|
76
|
-
end
|
77
|
-
|
78
|
-
def secondary?
|
79
|
-
@secondary
|
80
|
-
end
|
81
|
-
|
82
|
-
# Refresh information about the node, such as it's status in the replica
|
83
|
-
# set and it's known peers.
|
79
|
+
# Is the node down?
|
84
80
|
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
@refreshed_at = Time.now
|
93
|
-
primary = true if info["ismaster"]
|
94
|
-
secondary = true if info["secondary"]
|
95
|
-
|
96
|
-
peers = []
|
97
|
-
peers.push info["primary"] if info["primary"]
|
98
|
-
peers.concat info["hosts"] if info["hosts"]
|
99
|
-
peers.concat info["passives"] if info["passives"]
|
100
|
-
peers.concat info["arbiters"] if info["arbiters"]
|
101
|
-
|
102
|
-
@peers = peers.map { |peer| Node.new(peer) }
|
103
|
-
@primary, @secondary = primary, secondary
|
104
|
-
|
105
|
-
if !primary && Threaded.executing?(:ensure_primary)
|
106
|
-
raise Errors::ReplicaSetReconfigured, "#{inspect} is no longer the primary node."
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
attr_reader :down_at
|
111
|
-
|
81
|
+
# @example Is the node down?
|
82
|
+
# node.down?
|
83
|
+
#
|
84
|
+
# @return [ Time, nil ] The time the node went down, or nil if up.
|
85
|
+
#
|
86
|
+
# @since 1.0.0
|
112
87
|
def down?
|
113
88
|
@down_at
|
114
89
|
end
|
115
90
|
|
116
|
-
# Set a flag on the node for the duration of provided block so that an
|
117
|
-
# exception is raised if the node is no longer the primary node.
|
118
|
-
#
|
119
|
-
# Returns nothing.
|
120
|
-
def ensure_primary
|
121
|
-
Threaded.begin :ensure_primary
|
122
|
-
yield
|
123
|
-
ensure
|
124
|
-
Threaded.end :ensure_primary
|
125
|
-
end
|
126
|
-
|
127
91
|
# Yields the block if a connection can be established, retrying when a
|
128
92
|
# connection error is raised.
|
129
93
|
#
|
130
|
-
# @
|
94
|
+
# @example Ensure we are connection.
|
95
|
+
# node.ensure_connected do
|
96
|
+
# #...
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# @raises [ ConnectionFailure ] When a connection cannot be established.
|
100
|
+
#
|
101
|
+
# @return [ nil ] nil.
|
102
|
+
#
|
103
|
+
# @since 1.0.0
|
131
104
|
def ensure_connected
|
132
105
|
# Don't run the reconnection login if we're already inside an
|
133
106
|
# +ensure_connected+ block.
|
134
|
-
return yield if Threaded.executing?
|
135
|
-
Threaded.begin
|
136
|
-
|
107
|
+
return yield if Threaded.executing?(:connection)
|
108
|
+
Threaded.begin(:connection)
|
137
109
|
retry_on_failure = true
|
138
110
|
|
139
111
|
begin
|
@@ -167,44 +139,261 @@ module Moped
|
|
167
139
|
raise $!.extend(Errors::SocketError)
|
168
140
|
end
|
169
141
|
ensure
|
170
|
-
Threaded.end
|
142
|
+
Threaded.end(:connection)
|
171
143
|
end
|
172
144
|
|
173
|
-
|
174
|
-
|
145
|
+
# Set a flag on the node for the duration of provided block so that an
|
146
|
+
# exception is raised if the node is no longer the primary node.
|
147
|
+
#
|
148
|
+
# @example Ensure this node is primary.
|
149
|
+
# node.ensure_primary do
|
150
|
+
# #...
|
151
|
+
# end
|
152
|
+
#
|
153
|
+
# @return [ nil ] nil.
|
154
|
+
#
|
155
|
+
# @since 1.0.0
|
156
|
+
def ensure_primary
|
157
|
+
Threaded.begin(:ensure_primary)
|
158
|
+
yield
|
159
|
+
ensure
|
160
|
+
Threaded.end(:ensure_primary)
|
161
|
+
end
|
175
162
|
|
163
|
+
# Execute a get more operation on the node.
|
164
|
+
#
|
165
|
+
# @example Execute a get more.
|
166
|
+
# node.get_more(database, collection, 12345, -1)
|
167
|
+
#
|
168
|
+
# @param [ Database ] database The database to get more from.
|
169
|
+
# @param [ Collection ] collection The collection to get more from.
|
170
|
+
# @param [ Integer ] cursor_id The id of the cursor on the server.
|
171
|
+
# @param [ Integer ] limit The number of documents to limit.
|
172
|
+
#
|
173
|
+
# @return [ Message ] The result of the operation.
|
174
|
+
#
|
175
|
+
# @since 1.0.0
|
176
|
+
def get_more(database, collection, cursor_id, limit)
|
177
|
+
process(Protocol::GetMore.new(database, collection, cursor_id, limit))
|
178
|
+
end
|
179
|
+
|
180
|
+
# Get the hash identifier for the node.
|
181
|
+
#
|
182
|
+
# @example Get the hash identifier.
|
183
|
+
# node.hash
|
184
|
+
#
|
185
|
+
# @return [ Integer ] The hash identifier.
|
186
|
+
#
|
187
|
+
# @since 1.0.0
|
188
|
+
def hash
|
189
|
+
[ ip_address, port ].hash
|
190
|
+
end
|
191
|
+
|
192
|
+
# Creat the new node.
|
193
|
+
#
|
194
|
+
# @example Create the new node.
|
195
|
+
# Node.new("127.0.0.1:27017")
|
196
|
+
#
|
197
|
+
# @param [ String ] address The location of the server node.
|
198
|
+
#
|
199
|
+
# @since 1.0.0
|
200
|
+
def initialize(address)
|
201
|
+
@address = address
|
202
|
+
|
203
|
+
host, port = address.split(":")
|
204
|
+
@ip_address = ::Socket.getaddrinfo(host, nil, ::Socket::AF_INET, ::Socket::SOCK_STREAM).first[3]
|
205
|
+
@port = port.to_i
|
206
|
+
@resolved_address = "#{@ip_address}:#{@port}"
|
207
|
+
|
208
|
+
@timeout = 5
|
209
|
+
@down_at = nil
|
210
|
+
@refreshed_at = nil
|
211
|
+
@primary = nil
|
212
|
+
@secondary = nil
|
213
|
+
end
|
214
|
+
|
215
|
+
# Insert documents into the database.
|
216
|
+
#
|
217
|
+
# @example Insert documents.
|
218
|
+
# node.insert(database, collection, [{ name: "Tool" }])
|
219
|
+
#
|
220
|
+
# @param [ Database ] database The database to insert to.
|
221
|
+
# @param [ Collection ] collection The collection to insert to.
|
222
|
+
# @param [ Array<Hash> ] documents The documents to insert.
|
223
|
+
#
|
224
|
+
# @return [ Message ] The result of the operation.
|
225
|
+
#
|
226
|
+
# @since 1.0.0
|
227
|
+
def insert(database, collection, documents)
|
228
|
+
process(Protocol::Insert.new(database, collection, documents))
|
229
|
+
end
|
230
|
+
|
231
|
+
# Kill all provided cursors on the node.
|
232
|
+
#
|
233
|
+
# @example Kill all the provided cursors.
|
234
|
+
# node.kill_cursors([ 12345 ])
|
235
|
+
#
|
236
|
+
# @param [ Array<Integer> ] cursor_ids The cursor ids.
|
237
|
+
#
|
238
|
+
# @return [ Message ] The result of the operation.
|
239
|
+
#
|
240
|
+
# @since 1.0.0
|
241
|
+
def kill_cursors(cursor_ids)
|
242
|
+
process(Protocol::KillCursors.new(cursor_ids))
|
243
|
+
end
|
244
|
+
|
245
|
+
# Does the node need to be refreshed?
|
246
|
+
#
|
247
|
+
# @example Does the node require refreshing?
|
248
|
+
# node.needs_refresh?(time)
|
249
|
+
#
|
250
|
+
# @param [ Time ] time The next referesh time.
|
251
|
+
#
|
252
|
+
# @return [ true, false] Whether the node needs to be refreshed.
|
253
|
+
#
|
254
|
+
# @since 1.0.0
|
255
|
+
def needs_refresh?(time)
|
256
|
+
!@refreshed_at || @refreshed_at < time
|
257
|
+
end
|
258
|
+
|
259
|
+
# Execute a pipeline of commands, for example a safe mode persist.
|
260
|
+
#
|
261
|
+
# @example Execute a pipeline.
|
262
|
+
# node.pipeline do
|
263
|
+
# #...
|
264
|
+
# end
|
265
|
+
#
|
266
|
+
# @return [ nil ] nil.
|
267
|
+
#
|
268
|
+
# @since 1.0.0
|
269
|
+
def pipeline
|
270
|
+
Threaded.begin(:pipeline)
|
176
271
|
begin
|
177
272
|
yield
|
178
273
|
ensure
|
179
|
-
Threaded.end
|
274
|
+
Threaded.end(:pipeline)
|
180
275
|
end
|
276
|
+
flush unless Threaded.executing?(:pipeline)
|
277
|
+
end
|
181
278
|
|
182
|
-
|
279
|
+
# Is the node the replica set primary?
|
280
|
+
#
|
281
|
+
# @example Is the node the primary?
|
282
|
+
# node.primary?
|
283
|
+
#
|
284
|
+
# @return [ true, false ] If the node is the primary.
|
285
|
+
#
|
286
|
+
# @since 1.0.0
|
287
|
+
def primary?
|
288
|
+
@primary
|
183
289
|
end
|
184
290
|
|
185
|
-
|
186
|
-
|
187
|
-
|
291
|
+
# Execute a query on the node.
|
292
|
+
#
|
293
|
+
# @example Execute a query.
|
294
|
+
# node.query(database, collection, { name: "Tool" })
|
295
|
+
#
|
296
|
+
# @param [ Database ] database The database to query from.
|
297
|
+
# @param [ Collection ] collection The collection to query from.
|
298
|
+
# @param [ Hash ] selector The query selector.
|
299
|
+
# @param [ Hash ] options The query options.
|
300
|
+
#
|
301
|
+
# @raise [ QueryFailure ] If the query had an error.
|
302
|
+
#
|
303
|
+
# @return [ Message ] The result of the operation.
|
304
|
+
#
|
305
|
+
# @since 1.0.0
|
306
|
+
def query(database, collection, selector, options = {})
|
307
|
+
operation = Protocol::Query.new(database, collection, selector, options)
|
188
308
|
|
189
|
-
|
190
|
-
|
309
|
+
process operation do |reply|
|
310
|
+
if reply.flags.include?(:query_failure)
|
311
|
+
raise Errors::QueryFailure.new(operation, reply.documents.first)
|
191
312
|
end
|
313
|
+
reply
|
314
|
+
end
|
315
|
+
end
|
192
316
|
|
193
|
-
|
194
|
-
|
195
|
-
|
317
|
+
# Refresh information about the node, such as it's status in the replica
|
318
|
+
# set and it's known peers.
|
319
|
+
#
|
320
|
+
# @example Refresh the node.
|
321
|
+
# node.refresh
|
322
|
+
#
|
323
|
+
# @raise [ ConnectionFailure ] If the node cannot be reached.
|
324
|
+
|
325
|
+
# @raise [ ReplicaSetReconfigured ] If the node is no longer a primary node and
|
326
|
+
# refresh was called within an +#ensure_primary+ block.
|
327
|
+
#
|
328
|
+
# @return [ nil ] nil.
|
329
|
+
#
|
330
|
+
# @since 1.0.0
|
331
|
+
def refresh
|
332
|
+
info = command("admin", ismaster: 1)
|
333
|
+
|
334
|
+
@refreshed_at = Time.now
|
335
|
+
primary = true if info["ismaster"]
|
336
|
+
secondary = true if info["secondary"]
|
337
|
+
|
338
|
+
peers = []
|
339
|
+
peers.push(info["primary"]) if info["primary"]
|
340
|
+
peers.concat(info["hosts"]) if info["hosts"]
|
341
|
+
peers.concat(info["passives"]) if info["passives"]
|
342
|
+
peers.concat(info["arbiters"]) if info["arbiters"]
|
343
|
+
|
344
|
+
@peers = peers.map { |peer| Node.new(peer) }
|
345
|
+
@primary, @secondary = primary, secondary
|
346
|
+
|
347
|
+
if !primary && Threaded.executing?(:ensure_primary)
|
348
|
+
raise Errors::ReplicaSetReconfigured, "#{inspect} is no longer the primary node."
|
196
349
|
end
|
350
|
+
end
|
197
351
|
|
198
|
-
|
352
|
+
# Execute a remove command for the provided selector.
|
353
|
+
#
|
354
|
+
# @example Remove documents.
|
355
|
+
# node.remove(database, collection, { name: "Tool" })
|
356
|
+
#
|
357
|
+
# @param [ Database ] database The database to remove from.
|
358
|
+
# @param [ Collection ] collection The collection to remove from.
|
359
|
+
# @param [ Hash ] selector The query selector.
|
360
|
+
# @param [ Hash ] options The remove options.
|
361
|
+
#
|
362
|
+
# @return [ Message ] The result of the operation.
|
363
|
+
#
|
364
|
+
# @since 1.0.0
|
365
|
+
def remove(database, collection, selector, options = {})
|
366
|
+
process(Protocol::Delete.new(database, collection, selector, options))
|
199
367
|
end
|
200
368
|
|
201
|
-
|
202
|
-
|
369
|
+
# Is the node a replica set secondary?
|
370
|
+
#
|
371
|
+
# @example Is the node a secondary?
|
372
|
+
# node.secondary?
|
373
|
+
#
|
374
|
+
# @return [ true, false ] If the node is a secondary.
|
375
|
+
#
|
376
|
+
# @since 1.0.0
|
377
|
+
def secondary?
|
378
|
+
@secondary
|
203
379
|
end
|
204
|
-
alias eql? ==
|
205
380
|
|
206
|
-
|
207
|
-
|
381
|
+
# Execute an update command for the provided selector.
|
382
|
+
#
|
383
|
+
# @example Update documents.
|
384
|
+
# node.update(database, collection, { name: "Tool" }, { likes: 1000 })
|
385
|
+
#
|
386
|
+
# @param [ Database ] database The database to update.
|
387
|
+
# @param [ Collection ] collection The collection to update.
|
388
|
+
# @param [ Hash ] selector The query selector.
|
389
|
+
# @param [ Hash ] change The updates.
|
390
|
+
# @param [ Hash ] options The update options.
|
391
|
+
#
|
392
|
+
# @return [ Message ] The result of the operation.
|
393
|
+
#
|
394
|
+
# @since 1.0.0
|
395
|
+
def update(database, collection, selector, change, options = {})
|
396
|
+
process(Protocol::Update.new(database, collection, selector, change, options))
|
208
397
|
end
|
209
398
|
|
210
399
|
private
|
@@ -276,6 +465,8 @@ module Moped
|
|
276
465
|
raise Errors::ConnectionFailure, "Timed out connection to Mongo on #{address}"
|
277
466
|
rescue Errno::ECONNREFUSED
|
278
467
|
raise Errors::ConnectionFailure, "Could not connect to Mongo on #{address}"
|
468
|
+
rescue Errno::ECONNRESET
|
469
|
+
raise Errors::ConnectionFailure, "Connection reset to Mongo on #{address}"
|
279
470
|
end
|
280
471
|
|
281
472
|
def process(operation, &callback)
|
@@ -329,6 +520,5 @@ module Moped
|
|
329
520
|
logger.debug indent + last.log_inspect + runtime
|
330
521
|
end
|
331
522
|
end
|
332
|
-
|
333
523
|
end
|
334
524
|
end
|