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
data/lib/moped/collection.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "moped/query"
|
3
|
+
|
1
4
|
module Moped
|
2
5
|
|
3
6
|
# The class for interacting with a MongoDB collection.
|
4
7
|
#
|
5
|
-
# @
|
6
|
-
# users = session[:users] # => <Moped::Collection ...>
|
7
|
-
# users.drop
|
8
|
-
# users.insert(name: "John")
|
9
|
-
# users.find.to_a # => [{ name: "John" }]
|
8
|
+
# @since 1.0.0
|
10
9
|
class Collection
|
10
|
+
include Readable
|
11
11
|
|
12
|
-
#
|
13
|
-
#
|
12
|
+
# @!attribute database
|
13
|
+
# @return [ Database ] The database for the collection.
|
14
|
+
# @!attribute name
|
15
|
+
# @return [ String ] The name of the collection.
|
14
16
|
attr_reader :database, :name
|
15
17
|
|
16
18
|
# Return whether or not this collection is a capped collection.
|
@@ -35,15 +37,31 @@ module Moped
|
|
35
37
|
# @since 1.0.0
|
36
38
|
def drop
|
37
39
|
begin
|
38
|
-
|
39
|
-
session.context.command(database.name, drop: name)
|
40
|
-
end
|
40
|
+
session.with(read: :primary).command(drop: name)
|
41
41
|
rescue Moped::Errors::OperationFailure => e
|
42
|
-
raise e unless e.
|
42
|
+
raise e unless e.ns_not_found?
|
43
43
|
false
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
# Rename the collection
|
48
|
+
#
|
49
|
+
# @example Rename the collection to 'foo'
|
50
|
+
# collection.rename('foo')
|
51
|
+
#
|
52
|
+
# @return [ Hash ] The command information.
|
53
|
+
#
|
54
|
+
# @since 2.0.0
|
55
|
+
def rename(to_name)
|
56
|
+
begin
|
57
|
+
session.
|
58
|
+
with(database: "admin", read: :primary).
|
59
|
+
command(renameCollection: "#{database.name}.#{name}", to: "#{database.name}.#{to_name}")
|
60
|
+
rescue Moped::Errors::OperationFailure => e
|
61
|
+
raise e unless e.ns_not_exists?
|
62
|
+
false
|
63
|
+
end
|
64
|
+
end
|
47
65
|
# Build a query for this collection.
|
48
66
|
#
|
49
67
|
# @example Build a query based on the provided selector.
|
@@ -81,7 +99,8 @@ module Moped
|
|
81
99
|
#
|
82
100
|
# @since 1.0.0
|
83
101
|
def initialize(database, name)
|
84
|
-
@database
|
102
|
+
@database = database
|
103
|
+
@name = name.to_s
|
85
104
|
end
|
86
105
|
|
87
106
|
# Insert one or more documents into the collection.
|
@@ -101,9 +120,9 @@ module Moped
|
|
101
120
|
#
|
102
121
|
# @since 1.0.0
|
103
122
|
def insert(documents, flags = nil)
|
104
|
-
|
105
|
-
|
106
|
-
|
123
|
+
docs = documents.is_a?(Array) ? documents : [ documents ]
|
124
|
+
cluster.with_primary do |node|
|
125
|
+
node.insert(database.name, name, docs, write_concern, flags: flags || [])
|
107
126
|
end
|
108
127
|
end
|
109
128
|
|
@@ -117,15 +136,30 @@ module Moped
|
|
117
136
|
# }
|
118
137
|
# })
|
119
138
|
#
|
120
|
-
# @param [ Hash, Array<Hash> ] documents representing the aggregate
|
139
|
+
# @param [ Hash, Array<Hash> ] documents representing the aggregate
|
140
|
+
# function to execute
|
121
141
|
#
|
122
142
|
# @return [ Hash ] containing the result of aggregation
|
123
143
|
#
|
124
144
|
# @since 1.3.0
|
125
145
|
def aggregate(*pipeline)
|
126
|
-
pipeline.flatten
|
127
|
-
|
128
|
-
|
146
|
+
session.command(aggregate: name, pipeline: pipeline.flatten)["result"]
|
147
|
+
end
|
148
|
+
|
149
|
+
# Get the session for the collection.
|
150
|
+
#
|
151
|
+
# @example Get the session for the collection.
|
152
|
+
# collection.session
|
153
|
+
#
|
154
|
+
# @return [ Session ] The session for the collection.
|
155
|
+
#
|
156
|
+
# @since 2.0.0
|
157
|
+
def session
|
158
|
+
database.session
|
159
|
+
end
|
160
|
+
|
161
|
+
def write_concern
|
162
|
+
session.write_concern
|
129
163
|
end
|
130
164
|
end
|
131
165
|
end
|
data/lib/moped/connection.rb
CHANGED
@@ -1,16 +1,33 @@
|
|
1
|
-
|
2
|
-
require "moped/
|
3
|
-
require "moped/
|
4
|
-
require "moped/
|
1
|
+
# encoding: utf-8
|
2
|
+
require "moped/connection/manager"
|
3
|
+
require "moped/connection/pool"
|
4
|
+
require "moped/connection/queue"
|
5
|
+
require "moped/connection/reaper"
|
6
|
+
require "moped/connection/sockets"
|
5
7
|
|
6
8
|
module Moped
|
7
9
|
|
8
10
|
# This class contains behaviour of database socket connections.
|
9
11
|
#
|
10
|
-
# @
|
12
|
+
# @since 2.0.0
|
11
13
|
class Connection
|
12
14
|
|
13
|
-
|
15
|
+
# The default connection timeout, in seconds.
|
16
|
+
#
|
17
|
+
# @since 2.0.0
|
18
|
+
TIMEOUT = 5
|
19
|
+
|
20
|
+
# @!attribute host
|
21
|
+
# @return [ String ] The ip address of the host.
|
22
|
+
# @!attribute options
|
23
|
+
# @return [ Hash ] The connection options.
|
24
|
+
# @!attribute port
|
25
|
+
# @return [ String ] The port the connection connects on.
|
26
|
+
# @!attribute timeout
|
27
|
+
# @return [ Integer ] The timeout in seconds.
|
28
|
+
# @!attribute last_use
|
29
|
+
# @return [ Time ] The time the connection was last checked out.
|
30
|
+
attr_reader :host, :options, :port, :timeout, :last_use
|
14
31
|
|
15
32
|
# Is the connection alive?
|
16
33
|
#
|
@@ -34,9 +51,9 @@ module Moped
|
|
34
51
|
# @since 1.0.0
|
35
52
|
def connect
|
36
53
|
@sock = if !!options[:ssl]
|
37
|
-
|
54
|
+
Socket::SSL.connect(host, port, timeout)
|
38
55
|
else
|
39
|
-
|
56
|
+
Socket::TCP.connect(host, port, timeout)
|
40
57
|
end
|
41
58
|
end
|
42
59
|
|
@@ -80,9 +97,49 @@ module Moped
|
|
80
97
|
# @option options [ Boolean ] :ssl Connect using SSL
|
81
98
|
# @since 1.0.0
|
82
99
|
def initialize(host, port, timeout, options = {})
|
100
|
+
@host = host
|
101
|
+
@port = port
|
102
|
+
@timeout = timeout
|
103
|
+
@options = options
|
83
104
|
@sock = nil
|
105
|
+
@last_use = nil
|
84
106
|
@request_id = 0
|
85
|
-
|
107
|
+
end
|
108
|
+
|
109
|
+
# Expiring a connection means returning it to the connection pool.
|
110
|
+
#
|
111
|
+
# @example Expire the connection.
|
112
|
+
# connection.expire
|
113
|
+
#
|
114
|
+
# @return [ nil ] nil.
|
115
|
+
#
|
116
|
+
# @since 2.0.0
|
117
|
+
def expire
|
118
|
+
@last_use = nil
|
119
|
+
end
|
120
|
+
|
121
|
+
# An expired connection is not currently being used.
|
122
|
+
#
|
123
|
+
# @example Is the connection expired?
|
124
|
+
# connection.expired?
|
125
|
+
#
|
126
|
+
# @return [ true, false ] If the connection is expired.
|
127
|
+
#
|
128
|
+
# @since 2.0.0
|
129
|
+
def expired?
|
130
|
+
@last_use.nil?
|
131
|
+
end
|
132
|
+
|
133
|
+
# A leased connection is currently checkout out from the connection pool.
|
134
|
+
#
|
135
|
+
# @example Lease the connection.
|
136
|
+
# connection.lease
|
137
|
+
#
|
138
|
+
# @return [ Time ] The current time of leasing.
|
139
|
+
#
|
140
|
+
# @since 2.0.0
|
141
|
+
def lease
|
142
|
+
@last_use = Time.now
|
86
143
|
end
|
87
144
|
|
88
145
|
# Read from the connection.
|
@@ -113,7 +170,7 @@ module Moped
|
|
113
170
|
sock_read = read_data(socket, reply.length - 36)
|
114
171
|
buffer = StringIO.new(sock_read)
|
115
172
|
reply.documents = reply.count.times.map do
|
116
|
-
BSON::Document.
|
173
|
+
BSON::Document.from_bson(buffer)
|
117
174
|
end
|
118
175
|
end
|
119
176
|
reply
|
@@ -189,6 +246,8 @@ module Moped
|
|
189
246
|
# Yields a connected socket to the calling back. It will attempt to reconnect
|
190
247
|
# the socket if it is not connected.
|
191
248
|
#
|
249
|
+
# @api private
|
250
|
+
#
|
192
251
|
# @example Write to the connection.
|
193
252
|
# with_connection do |socket|
|
194
253
|
# socket.write(buf)
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Moped
|
3
|
+
class Connection
|
4
|
+
|
5
|
+
# This class contains behaviour of connection pools for specific addresses.
|
6
|
+
#
|
7
|
+
# @since 2.0.0
|
8
|
+
module Manager
|
9
|
+
extend self
|
10
|
+
|
11
|
+
# Used for synchronization of pools access.
|
12
|
+
MUTEX = Mutex.new
|
13
|
+
|
14
|
+
# Get a connection pool for the provided node.
|
15
|
+
#
|
16
|
+
# @example Get a connection pool for the node.
|
17
|
+
# Manager.pool(node)
|
18
|
+
#
|
19
|
+
# @param [ Node ] The node.
|
20
|
+
#
|
21
|
+
# @return [ Pool ] The connection pool for the Node.
|
22
|
+
#
|
23
|
+
# @since 2.0.0
|
24
|
+
def pool(node)
|
25
|
+
MUTEX.synchronize do
|
26
|
+
pools[node.address.resolved] ||=
|
27
|
+
Pool.new(node.address.ip, node.address.port, node.options)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Get all the connection pools. This is a cache that stores each pool
|
34
|
+
# with lookup by it's resolved address.
|
35
|
+
#
|
36
|
+
# @api private
|
37
|
+
#
|
38
|
+
# @example Get the pools.
|
39
|
+
# Manager.pools
|
40
|
+
#
|
41
|
+
# @return [ Hash ] The cache of pools.
|
42
|
+
#
|
43
|
+
# @since 2.0.0
|
44
|
+
def pools
|
45
|
+
@pools ||= {}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Moped
|
3
|
+
class Connection
|
4
|
+
|
5
|
+
# This class contains behaviour of connection pools for specific addresses.
|
6
|
+
#
|
7
|
+
# @since 2.0.0
|
8
|
+
class Pool
|
9
|
+
|
10
|
+
# The default max size for the connection pool.
|
11
|
+
POOL_SIZE = 5
|
12
|
+
|
13
|
+
# The default timeout for getting connections from the queue.
|
14
|
+
TIMEOUT = 0.25
|
15
|
+
|
16
|
+
# @!attribute host
|
17
|
+
# @return [ String ] The host the pool is for.
|
18
|
+
# @!attribute port
|
19
|
+
# @return [ Integer ] The port on the host.
|
20
|
+
# @!attribute options
|
21
|
+
# @return [ Hash ] The connection pool options.
|
22
|
+
# @!attribute reaper
|
23
|
+
# @return [ Reaper ] The connection pool reaper.
|
24
|
+
attr_reader :host, :port, :options, :reaper
|
25
|
+
|
26
|
+
# Checkout a connection from the connection pool. If there exists a
|
27
|
+
# connection pinned to the current thread, then we return that first. If
|
28
|
+
# no connection is pinned, we will take an unpinned connection or create
|
29
|
+
# a new one if no unpinned exist and the pool is not saturated.
|
30
|
+
#
|
31
|
+
# @example Checkout a connection.
|
32
|
+
# pool.checkout
|
33
|
+
#
|
34
|
+
# @return [ Connection ] A connection.
|
35
|
+
#
|
36
|
+
# @since 2.0.0
|
37
|
+
def checkout
|
38
|
+
mutex.synchronize do
|
39
|
+
connection = pinned[thread_id]
|
40
|
+
if connection
|
41
|
+
unless connection.expired?
|
42
|
+
raise Errors::ConnectionInUse, "The connection on thread: #{thread_id} is in use."
|
43
|
+
else
|
44
|
+
lease(connection)
|
45
|
+
end
|
46
|
+
else
|
47
|
+
connection = pinned[thread_id] = next_connection
|
48
|
+
lease(connection)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Checkin the connection, indicating that it is finished being used. The
|
54
|
+
# connection will stay pinned to the current thread.
|
55
|
+
#
|
56
|
+
# @example Checkin the connection.
|
57
|
+
# pool.checkin(connection)
|
58
|
+
#
|
59
|
+
# @param [ Connection ] connection The connection to checkin.
|
60
|
+
#
|
61
|
+
# @since 2.0.0
|
62
|
+
def checkin(connection)
|
63
|
+
mutex.synchronize do
|
64
|
+
expire(connection)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Initialize the connection pool.
|
69
|
+
#
|
70
|
+
# @example Instantiate the connection pool.
|
71
|
+
# Pool.new(max_size: 4)
|
72
|
+
#
|
73
|
+
# @param [ Hash ] options The connection pool options.
|
74
|
+
#
|
75
|
+
# @since 2.0.0
|
76
|
+
def initialize(host, port, options = {})
|
77
|
+
@host = host
|
78
|
+
@port = port
|
79
|
+
@options = options
|
80
|
+
@reaper = Reaper.new(options[:reap_interval] || Reaper::INTERVAL, self)
|
81
|
+
@mutex = Mutex.new
|
82
|
+
@resource = ConditionVariable.new
|
83
|
+
@pinned = {}
|
84
|
+
@unpinned = Queue.new(max_size) do
|
85
|
+
Connection.new(host, port, options[:timeout] || Connection::TIMEOUT, options)
|
86
|
+
end
|
87
|
+
reaper.start
|
88
|
+
end
|
89
|
+
|
90
|
+
# Get the max size for the connection pool.
|
91
|
+
#
|
92
|
+
# @example Get the max size.
|
93
|
+
# pool.max_size
|
94
|
+
#
|
95
|
+
# @return [ Integer ] The max size of the pool.
|
96
|
+
#
|
97
|
+
# @since 2.0.0
|
98
|
+
def max_size
|
99
|
+
@max_size ||= (options[:pool_size] || POOL_SIZE)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Reap all connections that are active and associated with dead threads.
|
103
|
+
#
|
104
|
+
# @example Reap the connections.
|
105
|
+
# pool.reap([ 12351122313 ])
|
106
|
+
#
|
107
|
+
# @param [ Array<Integer> ] ids The ids of the current active threads.
|
108
|
+
#
|
109
|
+
# @return [ Pool ] The connection pool.
|
110
|
+
#
|
111
|
+
# @since 2.0.0
|
112
|
+
def reap(ids = active_threads)
|
113
|
+
pinned.each do |id, conn|
|
114
|
+
unless ids.include?(id)
|
115
|
+
conn.expire
|
116
|
+
unpinned.push(pinned.delete(id))
|
117
|
+
end
|
118
|
+
end and self
|
119
|
+
end
|
120
|
+
|
121
|
+
# Get the current size of the connection pool. Is the total of pinned
|
122
|
+
# plus unpinned connections.
|
123
|
+
#
|
124
|
+
# @example Get the pool's current size.
|
125
|
+
# pool.size
|
126
|
+
#
|
127
|
+
# @return [ Integer ] The current size of the pool.
|
128
|
+
#
|
129
|
+
# @since 2.0.0
|
130
|
+
def size
|
131
|
+
unpinned.size + pinned.size
|
132
|
+
end
|
133
|
+
|
134
|
+
# Get the timeout when attempting to check out items from the pool.
|
135
|
+
#
|
136
|
+
# @example Get the checkout timeout.
|
137
|
+
# pool.timeout
|
138
|
+
#
|
139
|
+
# @return [ Float ] The pool timeout.
|
140
|
+
#
|
141
|
+
# @since 2.0.0
|
142
|
+
def timeout
|
143
|
+
@timeout ||= (options[:pool_timeout] || TIMEOUT)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Execute the block with a connection, ensuring that the checkin/checkout
|
147
|
+
# workflow is properly executed.
|
148
|
+
#
|
149
|
+
# @example Execute the block with a connection.
|
150
|
+
# pool.with_connection do |conn|
|
151
|
+
# conn.connect
|
152
|
+
# end
|
153
|
+
#
|
154
|
+
# @return [ Object ] The result of the yield.
|
155
|
+
#
|
156
|
+
# @since 2.0.0
|
157
|
+
def with_connection
|
158
|
+
connection = checkout
|
159
|
+
begin
|
160
|
+
yield(connection)
|
161
|
+
ensure
|
162
|
+
checkin(connection)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
attr_reader :mutex, :resource, :pinned, :unpinned
|
169
|
+
|
170
|
+
def expire(connection)
|
171
|
+
connection.expire
|
172
|
+
pinned[thread_id] = connection
|
173
|
+
end
|
174
|
+
|
175
|
+
def lease(connection)
|
176
|
+
connection.lease
|
177
|
+
connection
|
178
|
+
end
|
179
|
+
|
180
|
+
def next_connection
|
181
|
+
reap if saturated?
|
182
|
+
unpinned.pop(timeout)
|
183
|
+
end
|
184
|
+
|
185
|
+
def saturated?
|
186
|
+
size == max_size
|
187
|
+
end
|
188
|
+
|
189
|
+
def thread_id
|
190
|
+
Thread.current.object_id
|
191
|
+
end
|
192
|
+
|
193
|
+
def active_threads
|
194
|
+
Thread.list.select{ |thread| thread.alive? }.map{ |thread| thread.object_id }
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|