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
data/lib/moped/errors.rb CHANGED
@@ -5,6 +5,18 @@ module Moped
5
5
  # source of information on error codes.
6
6
  ERROR_REFERENCE = "https://github.com/mongodb/mongo/blob/master/docs/errors.md"
7
7
 
8
+ # Raised when the connection pool is saturated and no new connection is
9
+ # reaped during the wait time.
10
+ class PoolSaturated < RuntimeError; end
11
+
12
+ # Raised when attempting to checkout a connection on a thread that already
13
+ # has a connection checked out.
14
+ class ConnectionInUse < RuntimeError; end
15
+
16
+ # Raised when attempting to checkout a pinned connection from the pool but
17
+ # it is already in use by another object on the same thread.
18
+ class PoolTimeout < RuntimeError; end
19
+
8
20
  # Generic error class for exceptions related to connection failures.
9
21
  class ConnectionFailure < StandardError; end
10
22
 
@@ -100,10 +112,7 @@ module Moped
100
112
  class PotentialReconfiguration < MongoError
101
113
 
102
114
  # Not master error codes.
103
- NOT_MASTER = [ 13435, 13436, 10009]
104
-
105
- # Error codes received around reconfiguration
106
- CONNECTION_ERRORS_RECONFIGURATION = [ 15988, 10276, 11600, 9001, 13639, 10009, 11002 ]
115
+ NOT_MASTER = [ 13435, 13436 ]
107
116
 
108
117
  # Replica set reconfigurations can be either in the form of an operation
109
118
  # error with code 13435, or with an error message stating the server is
@@ -113,8 +122,28 @@ module Moped
113
122
  NOT_MASTER.include?(details["code"]) || err.include?("not master")
114
123
  end
115
124
 
116
- def connection_failure?
117
- CONNECTION_ERRORS_RECONFIGURATION.include?(details["code"])
125
+ # Is the error due to a namespace not being found?
126
+ #
127
+ # @example Is the namespace not found?
128
+ # error.ns_not_found?
129
+ #
130
+ # @return [ true, false ] If the namespace was not found.
131
+ #
132
+ # @since 2.0.0
133
+ def ns_not_found?
134
+ details["errmsg"] == "ns not found"
135
+ end
136
+
137
+ # Is the error due to a namespace not existing?
138
+ #
139
+ # @example Doest the namespace not exist?
140
+ # error.ns_not_exists?
141
+ #
142
+ # @return [ true, false ] If the namespace was not found.
143
+ #
144
+ # @since 2.0.0
145
+ def ns_not_exists?
146
+ details["errmsg"] =~ /namespace does not exist/
118
147
  end
119
148
  end
120
149
 
@@ -0,0 +1,96 @@
1
+ # encoding: utf-8
2
+ module Moped
3
+
4
+ # Provides common behavior around executing a thread local stack safely.
5
+ #
6
+ # @since 2.0.0
7
+ module Executable
8
+
9
+ # Given the name of a thread local stack, ensure that execution happens by
10
+ # starting and ending the stack execution cleanly.
11
+ #
12
+ # @example Ensure execution of a pipeline.
13
+ # execute(:pipeline) do
14
+ # yield(self)
15
+ # end
16
+ #
17
+ # @param [ Symbol ] name The name of the stack.
18
+ #
19
+ # @return [ Object ] The result of the yield.
20
+ #
21
+ # @since 2.0.0
22
+ def execute(name)
23
+ begin_execution(name)
24
+ begin
25
+ yield(self)
26
+ ensure
27
+ end_execution(name)
28
+ end
29
+ end
30
+
31
+ # Are we currently executing a stack on the thread?
32
+ #
33
+ # @example Are we executing a pipeline?
34
+ # executing?(:pipeline)
35
+ #
36
+ # @param [ Symbol ] name The name of the stack.
37
+ #
38
+ # @return [ true, false ] If we are executing the stack.
39
+ #
40
+ # @since 2.0.0
41
+ def executing?(name)
42
+ !stack(name).empty?
43
+ end
44
+
45
+ private
46
+
47
+ # Begin entry into a named thread local stack.
48
+ #
49
+ # @api private
50
+ #
51
+ # @example Begin entry into the stack.
52
+ # executable.begin_execution(:create)
53
+ #
54
+ # @param [ String ] name The name of the stack.
55
+ #
56
+ # @return [ true ] True.
57
+ #
58
+ # @since 1.0.0
59
+ def begin_execution(name)
60
+ stack(name).push(true)
61
+ end
62
+
63
+ # Exit from a named thread local stack.
64
+ #
65
+ # @api private
66
+ #
67
+ # @example Exit from the stack.
68
+ # executable.end_execution(:create)
69
+ #
70
+ # @param [ Symbol ] name The name of the stack
71
+ #
72
+ # @return [ true ] True.
73
+ #
74
+ # @since 1.0.0
75
+ def end_execution(name)
76
+ stack(name).pop
77
+ end
78
+
79
+ # Get the named stack.
80
+ #
81
+ # @api private
82
+ #
83
+ # @example Get a stack by name
84
+ # executable.stack(:create)
85
+ #
86
+ # @param [ Symbol ] name The name of the stack
87
+ #
88
+ # @return [ Array ] The stack.
89
+ #
90
+ # @since 1.0.0
91
+ def stack(name)
92
+ stacks = (Thread.current[:"moped-stacks"] ||= {})
93
+ stacks[name] ||= []
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ require "moped/failover/disconnect"
3
+ require "moped/failover/ignore"
4
+ require "moped/failover/reconfigure"
5
+ require "moped/failover/retry"
6
+
7
+ module Moped
8
+
9
+ # Provides behaviour around failover scenarios for different types of
10
+ # exceptions that get raised on connection and execution of operations.
11
+ #
12
+ # @since 2.0.0
13
+ module Failover
14
+ extend self
15
+
16
+ # Hash lookup for the failover classes based off the exception type.
17
+ #
18
+ # @since 2.0.0
19
+ STRATEGIES = {
20
+ Errors::AuthenticationFailure => Ignore,
21
+ Errors::ConnectionFailure => Retry,
22
+ Errors::CursorNotFound => Ignore,
23
+ Errors::OperationFailure => Reconfigure,
24
+ Errors::QueryFailure => Reconfigure
25
+ }.freeze
26
+
27
+ # Get the appropriate failover handler given the provided exception.
28
+ #
29
+ # @example Get the failover handler for an IOError.
30
+ # Moped::Failover.get(IOError)
31
+ #
32
+ # @param [ Exception ] exception The raised exception.
33
+ #
34
+ # @return [ Object ] The failover handler.
35
+ #
36
+ # @since 2.0.0
37
+ def get(exception)
38
+ STRATEGIES.fetch(exception.class, Disconnect)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+ module Moped
3
+ module Failover
4
+
5
+ # Disconnect is for the case when we get exceptions we do not know about,
6
+ # and need to disconnect the node to cleanup the problem.
7
+ #
8
+ # @since 2.0.0
9
+ module Disconnect
10
+ extend self
11
+
12
+ # Executes the failover strategy. In the case of disconnect, we just re-raise
13
+ # the exception that was thrown previously extending a socket error and
14
+ # disconnect.
15
+ #
16
+ # @example Execute the disconnect strategy.
17
+ # Moped::Failover::Disconnect.execute(exception, node)
18
+ #
19
+ # @param [ Exception ] exception The raised exception.
20
+ # @param [ Node ] node The node the exception got raised on.
21
+ #
22
+ # @raise [ Errors::SocketError ] The extended exception that was thrown.
23
+ #
24
+ # @since 2.0.0
25
+ def execute(exception, node)
26
+ node.disconnect
27
+ raise(exception.extend(Errors::SocketError))
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+ module Moped
3
+ module Failover
4
+
5
+ # Ignore is for the case when we get exceptions we deem are proper user
6
+ # or datbase errors and should be re-raised.
7
+ #
8
+ # @since 2.0.0
9
+ module Ignore
10
+ extend self
11
+
12
+ # Executes the failover strategy. In the case of ignore, we just re-raise
13
+ # the exception that was thrown previously.
14
+ #
15
+ # @example Execute the ignore strategy.
16
+ # Moped::Failover::Ignore.execute(exception, node)
17
+ #
18
+ # @param [ Exception ] exception The raised exception.
19
+ # @param [ Node ] node The node the exception got raised on.
20
+ #
21
+ # @raise [ Exception ] The exception that was previously thrown.
22
+ #
23
+ # @since 2.0.0
24
+ def execute(exception, node)
25
+ raise(exception)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+ module Moped
3
+ module Failover
4
+
5
+ # Reconfigure is for exceptions that indicate that a replica set was
6
+ # potentially reconfigured in the middle of an operation.
7
+ #
8
+ # @since 2.0.0
9
+ module Reconfigure
10
+ extend self
11
+
12
+ # Executes the failover strategy. In the case of reconfigure, we check if
13
+ # the failure was due to a replica set reconfiguration mid operation and
14
+ # raise a new error if appropriate.
15
+ #
16
+ # @example Execute the reconfigure strategy.
17
+ # Moped::Failover::Reconfigure.execute(exception, node)
18
+ #
19
+ # @param [ Exception ] exception The raised exception.
20
+ # @param [ Node ] node The node the exception got raised on.
21
+ #
22
+ # @raise [ Exception, Errors::ReplicaSetReconfigure ] The exception that
23
+ # was previously thrown or a reconfiguration error.
24
+ #
25
+ # @since 2.0.0
26
+ def execute(exception, node)
27
+ if exception.reconfiguring_replica_set?
28
+ raise(Errors::ReplicaSetReconfigured.new(exception.command, exception.details))
29
+ end
30
+ raise(exception)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+ module Moped
3
+ module Failover
4
+
5
+ # Retry is for the case when we get exceptions around the connection, and
6
+ # want to make another attempt to try and resolve the issue.
7
+ #
8
+ # @since 2.0.0
9
+ module Retry
10
+ extend self
11
+
12
+ # Executes the failover strategy. In the case of retyr, we disconnect and
13
+ # reconnect, then try the operation one more time.
14
+ #
15
+ # @example Execute the retry strategy.
16
+ # Moped::Failover::Retry.execute(exception, node)
17
+ #
18
+ # @param [ Exception ] exception The raised exception.
19
+ # @param [ Node ] node The node the exception got raised on.
20
+ #
21
+ # @raise [ Errors::ConnectionFailure ] If the retry fails.
22
+ #
23
+ # @return [ Object ] The result of the block yield.
24
+ #
25
+ # @since 2.0.0
26
+ def execute(exception, node)
27
+ node.disconnect
28
+ begin
29
+ yield if block_given?
30
+ rescue Exception => e
31
+ node.down!
32
+ raise(e)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
data/lib/moped/indexes.rb CHANGED
@@ -55,7 +55,10 @@ module Moped
55
55
  def create(key, options = {})
56
56
  spec = options.merge(ns: namespace, key: key)
57
57
  spec[:name] ||= key.to_a.join("_")
58
- database[:"system.indexes"].insert(spec)
58
+
59
+ database.session.with(write: { w: 1 }) do |_s|
60
+ _s[:"system.indexes"].insert(spec)
61
+ end
59
62
  end
60
63
 
61
64
  # Drop an index, or all indexes.
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+ require "moped/instrumentable/log"
3
+ require "moped/instrumentable/noop"
4
+
5
+ module Moped
6
+ module Instrumentable
7
+
8
+ # The name of the topic of operations for Moped.
9
+ #
10
+ # @since 2.0.0
11
+ TOPIC = "moped.operations"
12
+
13
+ # Topic for warning instrumentation.
14
+ #
15
+ # @since 2.0.0
16
+ WARN = "moped.warn"
17
+
18
+ # @!attribute instrumenter
19
+ # @return [ Object ] The instrumenter
20
+ attr_reader :instrumenter
21
+
22
+ # Instrument and execute the provided block.
23
+ #
24
+ # @example Instrument and execute.
25
+ # instrument("moped.noop") do
26
+ # node.connect
27
+ # end
28
+ #
29
+ # @param [ String ] name The name of the operation.
30
+ # @param [ Hash ] payload The payload.
31
+ #
32
+ # @return [ Object ] The result of the yield.
33
+ #
34
+ # @since 2.0.0
35
+ def instrument(name, payload = {}, &block)
36
+ instrumenter.instrument(name, payload, &block)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+ module Moped
3
+ module Instrumentable
4
+
5
+ # Provides logging instrumentation for compatibility with active support
6
+ # notifications.
7
+ #
8
+ # @since 2.0.0
9
+ class Log
10
+
11
+ class << self
12
+
13
+ # Instrument the log payload.
14
+ #
15
+ # @example Instrument the log payload.
16
+ # Log.instrument("moped.ops", {})
17
+ #
18
+ # @param [ String ] name The name of the logging type.
19
+ # @param [ Hash ] payload The log payload.
20
+ #
21
+ # @return [ Object ] The result of the yield.
22
+ #
23
+ # @since 2.0.0
24
+ def instrument(name, payload = {})
25
+ started = Time.new
26
+ begin
27
+ yield if block_given?
28
+ rescue Exception => e
29
+ payload[:exception] = [ e.class.name, e.message ]
30
+ raise e
31
+ ensure
32
+ runtime = ("%.4fms" % (1000 * (Time.now.to_f - started.to_f)))
33
+ if name == TOPIC
34
+ Moped::Loggable.log_operations(payload[:prefix], payload[:ops], runtime)
35
+ else
36
+ Moped::Loggable.debug(payload[:prefix], payload.reject { |k,v| k == :prefix }, runtime)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+ module Moped
3
+ module Instrumentable
4
+
5
+ # Does not instrument anything, just yields.
6
+ #
7
+ # @since 2.0.0
8
+ class Noop
9
+
10
+ class << self
11
+
12
+ # Do not instrument anything.
13
+ #
14
+ # @example Do not instrument.
15
+ # Noop.instrument("moped.noop") do
16
+ # node.connect
17
+ # end
18
+ #
19
+ # @param [ String ] name The name of the operation.
20
+ # @param [ Hash ] payload The payload.
21
+ #
22
+ # @return [ Object ] The result of the yield.
23
+ #
24
+ # @since 2.0.0
25
+ def instrument(name, payload = {})
26
+ yield payload if block_given?
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end