moped 1.5.3 → 2.0.0.beta

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.

Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -5
  3. data/README.md +1 -1
  4. data/lib/moped.rb +10 -13
  5. data/lib/moped/address.rb +56 -0
  6. data/lib/moped/authenticatable.rb +89 -0
  7. data/lib/moped/cluster.rb +169 -136
  8. data/lib/moped/collection.rb +53 -19
  9. data/lib/moped/connection.rb +69 -10
  10. data/lib/moped/connection/manager.rb +49 -0
  11. data/lib/moped/connection/pool.rb +198 -0
  12. data/lib/moped/connection/queue.rb +93 -0
  13. data/lib/moped/connection/reaper.rb +52 -0
  14. data/lib/moped/connection/socket.rb +4 -0
  15. data/lib/moped/connection/socket/connectable.rb +169 -0
  16. data/lib/moped/connection/socket/ssl.rb +52 -0
  17. data/lib/moped/connection/socket/tcp.rb +25 -0
  18. data/lib/moped/connection/sockets.rb +4 -0
  19. data/lib/moped/cursor.rb +3 -5
  20. data/lib/moped/database.rb +18 -24
  21. data/lib/moped/errors.rb +35 -6
  22. data/lib/moped/executable.rb +96 -0
  23. data/lib/moped/failover.rb +41 -0
  24. data/lib/moped/failover/disconnect.rb +31 -0
  25. data/lib/moped/failover/ignore.rb +29 -0
  26. data/lib/moped/failover/reconfigure.rb +34 -0
  27. data/lib/moped/failover/retry.rb +37 -0
  28. data/lib/moped/indexes.rb +4 -1
  29. data/lib/moped/instrumentable.rb +39 -0
  30. data/lib/moped/instrumentable/log.rb +43 -0
  31. data/lib/moped/instrumentable/noop.rb +31 -0
  32. data/lib/moped/loggable.rb +110 -0
  33. data/lib/moped/node.rb +316 -297
  34. data/lib/moped/operation.rb +3 -0
  35. data/lib/moped/operation/read.rb +62 -0
  36. data/lib/moped/operation/write.rb +57 -0
  37. data/lib/moped/protocol/command.rb +65 -4
  38. data/lib/moped/protocol/commands/authenticate.rb +1 -2
  39. data/lib/moped/protocol/delete.rb +16 -0
  40. data/lib/moped/protocol/get_more.rb +102 -31
  41. data/lib/moped/protocol/insert.rb +17 -0
  42. data/lib/moped/protocol/message.rb +44 -46
  43. data/lib/moped/protocol/query.rb +175 -92
  44. data/lib/moped/protocol/reply.rb +19 -8
  45. data/lib/moped/protocol/update.rb +18 -0
  46. data/lib/moped/query.rb +43 -17
  47. data/lib/moped/read_preference.rb +49 -0
  48. data/lib/moped/read_preference/nearest.rb +55 -0
  49. data/lib/moped/read_preference/primary.rb +60 -0
  50. data/lib/moped/read_preference/primary_preferred.rb +55 -0
  51. data/lib/moped/read_preference/secondary.rb +50 -0
  52. data/lib/moped/read_preference/secondary_preferred.rb +53 -0
  53. data/lib/moped/read_preference/selectable.rb +79 -0
  54. data/lib/moped/readable.rb +55 -0
  55. data/lib/moped/session.rb +122 -70
  56. data/lib/moped/{mongo_uri.rb → uri.rb} +75 -31
  57. data/lib/moped/version.rb +1 -1
  58. data/lib/moped/write_concern.rb +33 -0
  59. data/lib/moped/write_concern/propagate.rb +38 -0
  60. data/lib/moped/write_concern/unverified.rb +28 -0
  61. metadata +79 -44
  62. data/lib/moped/bson.rb +0 -45
  63. data/lib/moped/bson/binary.rb +0 -137
  64. data/lib/moped/bson/code.rb +0 -112
  65. data/lib/moped/bson/document.rb +0 -41
  66. data/lib/moped/bson/extensions.rb +0 -91
  67. data/lib/moped/bson/extensions/array.rb +0 -37
  68. data/lib/moped/bson/extensions/boolean.rb +0 -16
  69. data/lib/moped/bson/extensions/false_class.rb +0 -19
  70. data/lib/moped/bson/extensions/float.rb +0 -22
  71. data/lib/moped/bson/extensions/hash.rb +0 -39
  72. data/lib/moped/bson/extensions/integer.rb +0 -36
  73. data/lib/moped/bson/extensions/nil_class.rb +0 -19
  74. data/lib/moped/bson/extensions/object.rb +0 -11
  75. data/lib/moped/bson/extensions/regexp.rb +0 -38
  76. data/lib/moped/bson/extensions/string.rb +0 -45
  77. data/lib/moped/bson/extensions/symbol.rb +0 -33
  78. data/lib/moped/bson/extensions/time.rb +0 -23
  79. data/lib/moped/bson/extensions/true_class.rb +0 -19
  80. data/lib/moped/bson/max_key.rb +0 -51
  81. data/lib/moped/bson/min_key.rb +0 -51
  82. data/lib/moped/bson/object_id.rb +0 -301
  83. data/lib/moped/bson/timestamp.rb +0 -38
  84. data/lib/moped/bson/types.rb +0 -67
  85. data/lib/moped/logging.rb +0 -58
  86. data/lib/moped/session/context.rb +0 -115
  87. data/lib/moped/sockets/connectable.rb +0 -167
  88. data/lib/moped/sockets/ssl.rb +0 -50
  89. data/lib/moped/sockets/tcp.rb +0 -23
  90. data/lib/moped/threaded.rb +0 -69
@@ -0,0 +1,93 @@
1
+ # encoding: utf-8
2
+ module Moped
3
+ class Connection
4
+
5
+ # This class contains behaviour of a queue of unpinned connections.
6
+ #
7
+ # @since 2.0.0
8
+ class Queue
9
+
10
+ # Initialize a queue with the provided size.
11
+ #
12
+ # @example Instantiate the queue.
13
+ # Moped::Connection::Queue.new(10)
14
+ #
15
+ # @param [ Integer ] size The number of connections in the queue.
16
+ #
17
+ # @since 2.0.0
18
+ def initialize(size)
19
+ @queue = Array.new(size) { yield }
20
+ @mutex = Mutex.new
21
+ @resource = ConditionVariable.new
22
+ end
23
+
24
+ # Push a connection on the queue.
25
+ #
26
+ # @example Push a connection on the queue.
27
+ # queue.push(connection)
28
+ #
29
+ # @param [ Moped::Connection ] connection The connection to add.
30
+ #
31
+ # @since 2.0.0
32
+ def push(connection)
33
+ mutex.synchronize do
34
+ queue.push(connection)
35
+ resource.broadcast
36
+ end
37
+ end
38
+
39
+ # Pop the next connection off the queue.
40
+ #
41
+ # @example Pop a connection off the queue.
42
+ # queue.pop(0.5)
43
+ #
44
+ # @param [ Float ] timeout The time to wait for the connection.
45
+ #
46
+ # @return [ Moped::Connection ] The next connection.
47
+ #
48
+ # @since 2.0.0
49
+ def pop(timeout = 0.5)
50
+ mutex.synchronize do
51
+ wait_for_next(Time.now + timeout)
52
+ end
53
+ end
54
+
55
+ # Is the queue empty?
56
+ #
57
+ # @example Is the queue empty?
58
+ # queue.empty?
59
+ #
60
+ # @return [ true, false ] Is the queue empty?
61
+ #
62
+ # @since 2.0.0
63
+ def empty?
64
+ queue.empty?
65
+ end
66
+
67
+ # Get the current size of the queue.
68
+ #
69
+ # @example Get the size of the queue.
70
+ # queue.size
71
+ #
72
+ # @return [ Integer ] The number of connections in the queue.
73
+ #
74
+ # @since 2.0.0
75
+ def size
76
+ queue.size
77
+ end
78
+
79
+ private
80
+
81
+ attr_reader :queue, :mutex, :resource
82
+
83
+ def wait_for_next(deadline)
84
+ loop do
85
+ return queue.pop unless queue.empty?
86
+ wait = deadline - Time.now
87
+ raise Timeout::Error, "Waited for item but none was pushed." if wait <= 0
88
+ resource.wait(mutex, wait)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+ module Moped
3
+ class Connection
4
+
5
+ # This object cleans up connections on dead threads at a specified time
6
+ # interval.
7
+ #
8
+ # @since 2.0.0
9
+ class Reaper
10
+
11
+ # The default interval for reaping connections.
12
+ #
13
+ # @since 2.0.0
14
+ INTERVAL = 5
15
+
16
+ # @!attribute interval
17
+ # @return [ Float ] The reaping interval, in seconds.
18
+ # @!attribute pool
19
+ # @return [ Pool ] The connection pool to reap.
20
+ attr_reader :interval, :pool
21
+
22
+ # Initialize a new connection pool reaper.
23
+ #
24
+ # @example Initialize the reaper.
25
+ # Moped::Connection::Reaper.new(5, pool)
26
+ #
27
+ # @param [ Float ] interval The reaping interval.
28
+ # @param [ Pool ] pool The connection pool to reap.
29
+ #
30
+ # @since 2.0.0
31
+ def initialize(interval, pool)
32
+ @interval = interval
33
+ @pool = pool
34
+ end
35
+
36
+ # Start the reaper. Will execute continually on a separate thread.
37
+ #
38
+ # @example Start the reaper.
39
+ # reaper.start
40
+ #
41
+ # @since 2.0.0
42
+ def start
43
+ Thread.new(interval, pool) do |i, p|
44
+ while (true)
45
+ sleep(i)
46
+ p.reap
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,4 @@
1
+ # encoding: utf-8
2
+ require "moped/connection/socket/connectable"
3
+ require "moped/connection/socket/tcp"
4
+ require "moped/connection/socket/ssl"
@@ -0,0 +1,169 @@
1
+ module Moped
2
+ class Connection
3
+ module Socket
4
+ module Connectable
5
+
6
+ attr_reader :host, :port
7
+
8
+ # Is the socket connection alive?
9
+ #
10
+ # @example Is the socket alive?
11
+ # socket.alive?
12
+ #
13
+ # @return [ true, false ] If the socket is alive.
14
+ #
15
+ # @since 1.0.0
16
+ def alive?
17
+ if Kernel::select([ self ], nil, [ self ], 0)
18
+ !eof? rescue false
19
+ else
20
+ true
21
+ end
22
+ rescue IOError
23
+ false
24
+ end
25
+
26
+ # Bring in the class methods when included.
27
+ #
28
+ # @example Extend the class methods.
29
+ # Connectable.included(class)
30
+ #
31
+ # @param [ Class ] klass The class including the module.
32
+ #
33
+ # @since 1.3.0
34
+ def self.included(klass)
35
+ klass.send(:extend, ClassMethods)
36
+ end
37
+
38
+ # Read from the TCP socket.
39
+ #
40
+ # @param [ Integer ] length The length to read.
41
+ #
42
+ # @return [ Object ] The data.
43
+ #
44
+ # @since 1.2.0
45
+ def read(length)
46
+ check_if_alive!
47
+ handle_socket_errors { super }
48
+ end
49
+
50
+ # Write to the socket.
51
+ #
52
+ # @example Write to the socket.
53
+ # socket.write(data)
54
+ #
55
+ # @param [ Object ] args The data to write.
56
+ #
57
+ # @return [ Integer ] The number of bytes written.
58
+ #
59
+ # @since 1.0.0
60
+ def write(*args)
61
+ check_if_alive!
62
+ handle_socket_errors { super }
63
+ end
64
+
65
+ private
66
+
67
+ # Before performing a read or write operating, ping the server to check
68
+ # if it is alive.
69
+ #
70
+ # @api private
71
+ #
72
+ # @example Check if the connection is alive.
73
+ # connectable.check_if_alive!
74
+ #
75
+ # @raise [ ConnectionFailure ] If the connectable is not alive.
76
+ #
77
+ # @since 1.4.0
78
+ def check_if_alive!
79
+ unless alive?
80
+ raise Errors::ConnectionFailure, "Socket connection was closed by remote host"
81
+ end
82
+ end
83
+
84
+ # Generate the message for the connection failure based of the system
85
+ # call error, with some added information.
86
+ #
87
+ # @api private
88
+ #
89
+ # @example Generate the error message.
90
+ # connectable.generate_message(error)
91
+ #
92
+ # @param [ SystemCallError ] error The error.
93
+ #
94
+ # @return [ String ] The error message.
95
+ #
96
+ # @since 1.4.0
97
+ def generate_message(error)
98
+ "#{host}:#{port}: #{error.class.name} (#{error.errno}): #{error.message}"
99
+ end
100
+
101
+ # Handle the potential socket errors that can occur.
102
+ #
103
+ # @api private
104
+ #
105
+ # @example Handle the socket errors while executing the block.
106
+ # handle_socket_errors do
107
+ # socket.read(128)
108
+ # end
109
+ #
110
+ # @raise [ Moped::Errors::ConnectionFailure ] If a system call error or
111
+ # IOError occured which can be retried.
112
+ # @raise [ Moped::Errors::Unrecoverable ] If a system call error occured
113
+ # which cannot be retried and should be re-raised.
114
+ #
115
+ # @return [ Object ] The result of the yield.
116
+ #
117
+ # @since 1.0.0
118
+ def handle_socket_errors
119
+ yield
120
+ rescue Errno::ECONNREFUSED => e
121
+ raise Errors::ConnectionFailure, generate_message(e)
122
+ rescue Errno::EHOSTUNREACH => e
123
+ raise Errors::ConnectionFailure, generate_message(e)
124
+ rescue Errno::EPIPE => e
125
+ raise Errors::ConnectionFailure, generate_message(e)
126
+ rescue Errno::ECONNRESET => e
127
+ raise Errors::ConnectionFailure, generate_message(e)
128
+ rescue Errno::ETIMEDOUT => e
129
+ raise Errors::ConnectionFailure, generate_message(e)
130
+ rescue IOError
131
+ raise Errors::ConnectionFailure, "Connection timed out to Mongo on #{host}:#{port}"
132
+ rescue OpenSSL::SSL::SSLError => e
133
+ raise Errors::ConnectionFailure, "SSL Error '#{e.to_s}' for connection to Mongo on #{host}:#{port}"
134
+ end
135
+
136
+ module ClassMethods
137
+
138
+ # Connect to the tcp server.
139
+ #
140
+ # @example Connect to the server.
141
+ # TCPSocket.connect("127.0.0.1", 27017, 30)
142
+ #
143
+ # @param [ String ] host The host to connect to.
144
+ # @param [ Integer ] post The server port.
145
+ # @param [ Integer ] timeout The connection timeout.
146
+ #
147
+ # @return [ TCPSocket ] The socket.
148
+ #
149
+ # @since 1.0.0
150
+ def connect(host, port, timeout)
151
+ begin
152
+ Timeout::timeout(timeout) do
153
+ sock = new(host, port)
154
+ sock.set_encoding('binary')
155
+ timeout_val = [ timeout, 0 ].pack("l_2")
156
+ sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
157
+ sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_RCVTIMEO, timeout_val)
158
+ sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_SNDTIMEO, timeout_val)
159
+ sock
160
+ end
161
+ rescue Timeout::Error
162
+ raise Errors::ConnectionFailure, "Timed out connection to Mongo on #{host}:#{port}"
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,52 @@
1
+ require "openssl"
2
+
3
+ module Moped
4
+ class Connection
5
+ module Socket
6
+
7
+ # This is a wrapper around a tcp socket.
8
+ class SSL < OpenSSL::SSL::SSLSocket
9
+ include Connectable
10
+
11
+ attr_reader :socket
12
+
13
+ # Initialize the new TCPSocket with SSL.
14
+ #
15
+ # @example Initialize the socket.
16
+ # SSL.new("127.0.0.1", 27017)
17
+ #
18
+ # @param [ String ] host The host.
19
+ # @param [ Integer ] port The port.
20
+ #
21
+ # @since 1.2.0
22
+ def initialize(host, port)
23
+ @host, @port = host, port
24
+ handle_socket_errors do
25
+ @socket = TCPSocket.new(host, port)
26
+ super(socket)
27
+ self.sync_close = true
28
+ connect
29
+ end
30
+ end
31
+
32
+ # Set the encoding of the underlying socket.
33
+ #
34
+ # @param [ String ] string The encoding.
35
+ #
36
+ # @since 1.3.0
37
+ def set_encoding(string)
38
+ socket.set_encoding(string)
39
+ end
40
+
41
+ # Set a socket option on the underlying socket.
42
+ #
43
+ # @param [ Array<Object> ] args The option arguments.
44
+ #
45
+ # @since 1.3.0
46
+ def setsockopt(*args)
47
+ socket.setsockopt(*args)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,25 @@
1
+ module Moped
2
+ class Connection
3
+ module Socket
4
+
5
+ # This is a wrapper around a tcp socket.
6
+ class TCP < ::TCPSocket
7
+ include Connectable
8
+
9
+ # Initialize the new TCPSocket.
10
+ #
11
+ # @example Initialize the socket.
12
+ # TCPSocket.new("127.0.0.1", 27017)
13
+ #
14
+ # @param [ String ] host The host.
15
+ # @param [ Integer ] port The port.
16
+ #
17
+ # @since 1.2.0
18
+ def initialize(host, port)
19
+ @host, @port = host, port
20
+ handle_socket_errors { super }
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,4 @@
1
+ # encoding: utf-8
2
+ require "moped/connection/socket/connectable"
3
+ require "moped/connection/socket/tcp"
4
+ require "moped/connection/socket/ssl"
data/lib/moped/cursor.rb CHANGED
@@ -4,6 +4,7 @@ module Moped
4
4
  #
5
5
  # @api private
6
6
  class Cursor
7
+ include Readable
7
8
 
8
9
  # @attribute [r] get_more_op The get more message.
9
10
  # @attribute [r] kill_cursor_op The kill cursor message.
@@ -127,15 +128,12 @@ module Moped
127
128
  #
128
129
  # @since 1.0.0
129
130
  def load_docs
130
- consistency = session.consistency
131
- @options[:flags] |= [:slave_ok] if consistency == :eventual
132
131
  @options[:flags] |= [:no_cursor_timeout] if @options[:no_timeout]
133
-
134
132
  options = @options.clone
135
133
  options[:limit] = request_limit
136
134
 
137
- reply, @node = session.context.with_node do |node|
138
- [ node.query(@database, @collection, @selector, options), node ]
135
+ reply, @node = read_preference.with_node(session.cluster) do |node|
136
+ [ node.query(@database, @collection, @selector, query_options(options)), node ]
139
137
  end
140
138
 
141
139
  @limit -= reply.count if limited?
@@ -1,21 +1,17 @@
1
+ # encoding: utf-8
1
2
  module Moped
2
3
 
3
4
  # The class for interacting with a MongoDB database. One only interacts with
4
5
  # this class indirectly through a session.
5
6
  #
6
- # @example
7
- # session.use :moped
8
- # session.drop
9
- # session[:users].insert(name: "John")
10
- #
11
- # @example
12
- # session.with(database: :moped) do |moped|
13
- # moped[:users].drop
14
- # end
7
+ # @since 1.0.0
15
8
  class Database
9
+ include Readable
16
10
 
17
- # @attribute [r] name The name of the database.
18
- # @attribute [r] session The session.
11
+ # @!attribute name
12
+ # @return [ String ] The name of the database.
13
+ # @!attribute session
14
+ # @return [ Session ] The database session.
19
15
  attr_reader :name, :session
20
16
 
21
17
  # Get a collection by the provided name.
@@ -41,7 +37,7 @@ module Moped
41
37
  #
42
38
  # @since 1.0.0
43
39
  def collections
44
- collection_names.map{|name| Collection.new(self, name)}
40
+ collection_names.map{ |name| Collection.new(self, name) }
45
41
  end
46
42
 
47
43
  # Get all non-system collection names from the database, this excludes
@@ -54,7 +50,7 @@ module Moped
54
50
  #
55
51
  # @since 1.0.0
56
52
  def collection_names
57
- namespaces = Collection.new(self, "system.namespaces").find(name: { "$not" => /#{name}\.system\.|\$/ })
53
+ namespaces = self["system.namespaces"].find(name: { "$not" => /#{name}\.system\.|\$/ })
58
54
  namespaces.map do |doc|
59
55
  _name = doc["name"]
60
56
  _name[name.length + 1, _name.length]
@@ -73,7 +69,9 @@ module Moped
73
69
  #
74
70
  # @since 1.0.0
75
71
  def command(command)
76
- session.context.command(name.to_s, command)
72
+ read_preference.with_node(cluster) do |node|
73
+ node.command(name, command, query_options({}))
74
+ end
77
75
  end
78
76
 
79
77
  # Drop the database.
@@ -85,8 +83,8 @@ module Moped
85
83
  #
86
84
  # @since 1.0.0
87
85
  def drop
88
- session.with(consistency: :strong) do |session|
89
- session.context.command(name, dropDatabase: 1)
86
+ session.with(read: :primary) do |session|
87
+ session.command(dropDatabase: 1)
90
88
  end
91
89
  end
92
90
 
@@ -100,12 +98,8 @@ module Moped
100
98
  #
101
99
  # @since 1.0.0
102
100
  def initialize(session, name)
103
- if match = name.match(/\.|\s|\$|\\|\/|\x00/)
104
- raise Errors::InvalidDatabaseName.new(
105
- "Database names may not contain the character '#{match[0]}'."
106
- )
107
- end
108
- @session, @name = session, name
101
+ @session = session
102
+ @name = name.to_s
109
103
  end
110
104
 
111
105
  # Log in with +username+ and +password+ on the current database.
@@ -118,7 +112,7 @@ module Moped
118
112
  #
119
113
  # @since 1.0.0
120
114
  def login(username, password)
121
- session.context.login(name, username, password)
115
+ cluster.credentials[name] = [ username, password ]
122
116
  end
123
117
 
124
118
  # Log out from the current database.
@@ -128,7 +122,7 @@ module Moped
128
122
  #
129
123
  # @since 1.0.0
130
124
  def logout
131
- session.context.logout(name)
125
+ cluster.credentials.delete(name)
132
126
  end
133
127
  end
134
128
  end