Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

RUBY-494 new write-concern defaults and 'w' parameter

  • Loading branch information...
commit f91a41d054595f43374a13e63afac120458a43f7 1 parent 43cef18
@TylerBrock TylerBrock authored brandonblack committed
Showing with 1,381 additions and 2,006 deletions.
  1. +1 −0  .gitignore
  2. +1 −1  .travis.yml
  3. +1 −0  Gemfile
  4. +1 −1  README.md
  5. +1 −1  bin/mongo_console
  6. +1 −1  examples/admin.rb
  7. +1 −1  examples/capped.rb
  8. +1 −1  examples/cursor.rb
  9. +1 −1  examples/gridfs.rb
  10. +1 −1  examples/index_test.rb
  11. +1 −1  examples/info.rb
  12. +1 −1  examples/queries.rb
  13. +1 −1  examples/simple.rb
  14. +1 −1  examples/strict.rb
  15. +1 −1  examples/types.rb
  16. +1 −1  examples/web/thin/load.rb
  17. +1 −1  examples/web/unicorn/load.rb
  18. +1 −1  examples/web/unicorn/unicorn.rb.template
  19. +0 −3  lib/bson/version.rb
  20. +4 −2 lib/mongo.rb
  21. +49 −28 lib/mongo/client.rb
  22. +63 −52 lib/mongo/collection.rb
  23. +18 −16 lib/mongo/db.rb
  24. +3 −2 lib/mongo/gridfs/grid.rb
  25. +2 −1  lib/mongo/gridfs/grid_file_system.rb
  26. +2 −1  lib/mongo/gridfs/grid_io.rb
  27. +45 −6 lib/mongo/legacy.rb
  28. +11 −2 lib/mongo/networking.rb
  29. +31 −17 lib/mongo/repl_set_client.rb
  30. +2 −2 lib/mongo/sharded_client.rb
  31. +1 −1  lib/mongo/util/ssl_socket.rb
  32. +19 −3 lib/mongo/util/support.rb
  33. +46 −30 lib/mongo/util/uri_parser.rb
  34. +67 −0 lib/mongo/util/write_concern.rb
  35. +0 −3  lib/mongo/version.rb
  36. +8 −2 tasks/testing.rake
  37. +11 −11 test/auxillary/authentication_test.rb
  38. +2 −2 test/auxillary/repl_set_auth_test.rb
  39. +8 −8 test/auxillary/threaded_authentication_test.rb
  40. +35 −35 test/functional/collection_test.rb
  41. +1 −1  test/functional/connection_test.rb
  42. +2 −28 test/functional/cursor_fail_test.rb
  43. +6 −6 test/functional/cursor_test.rb
  44. +4 −5 test/functional/db_api_test.rb
  45. +1 −1  test/functional/grid_file_system_test.rb
  46. +3 −3 test/functional/grid_io_test.rb
  47. +0 −6 test/functional/grid_test.rb
  48. +26 −26 test/functional/safe_test.rb
  49. +70 −0 test/functional/write_concern.rb
  50. +7 −7 test/replica_set/basic_test.rb
  51. +22 −22 test/replica_set/{connect_test.rb → client_test.rb}
  52. +255 −0 test/replica_set/connection_test.rb
  53. +4 −4 test/replica_set/count_test.rb
  54. +3 −3 test/replica_set/cursor_test.rb
  55. +7 −7 test/replica_set/insert_test.rb
  56. +0 −119 test/replica_set/old/basic_test.rb
  57. +0 −57 test/replica_set/old/complex_connect_test.rb
  58. +0 −237 test/replica_set/old/complex_read_preference_test.rb
  59. +0 −248 test/replica_set/old/connect_test.rb
  60. +0 −43 test/replica_set/old/count_test.rb
  61. +0 −70 test/replica_set/old/cursor_test.rb
  62. +0 −52 test/replica_set/old/insert_test.rb
  63. +0 −57 test/replica_set/old/pooled_insert_test.rb
  64. +0 −50 test/replica_set/old/query_test.rb
  65. +0 −240 test/replica_set/old/read_preference_test.rb
  66. +0 −156 test/replica_set/old/refresh_test.rb
  67. +0 −60 test/replica_set/old/refresh_with_threads_test.rb
  68. +0 −68 test/replica_set/old/replication_ack_test.rb
  69. +0 −39 test/replica_set/old/rs_test_helper.rb
  70. +3 −3 test/replica_set/query_test.rb
  71. +10 −9 test/replica_set/replication_ack_test.rb
  72. +8 −4 test/test_helper.rb
  73. +230 −0 test/unit/client_test.rb
  74. +29 −5 test/unit/collection_test.rb
  75. +67 −82 test/unit/connection_test.rb
  76. +1 −1  test/unit/db_test.rb
  77. +1 −1  test/unit/grid_test.rb
  78. +125 −0 test/unit/safe_test.rb
  79. +51 −43 test/unit/write_concern_test.rb
View
1  .gitignore
@@ -1,5 +1,6 @@
docs
.yardoc
+coverage
*.gem
nbproject
*.bundle
View
2  .travis.yml
@@ -12,7 +12,7 @@ script: bundle exec rake test:ruby
notifications:
email: false
- #TODO: flowdock: [api token]
+ flowdock: 1da4416b8ff98d1880986472428b1b1b
services:
- mongodb
View
1  Gemfile
@@ -14,6 +14,7 @@ group :deploy do
end
group :testing do
+ gem 'simplecov'
gem 'test-unit'
gem 'mocha', '0.12.7'
gem 'shoulda'
View
2  README.md
@@ -45,7 +45,7 @@ Here's a quick code sample. Again, see the [MongoDB Ruby Tutorial](https://githu
require 'rubygems'
require 'mongo'
-@client = Mongo::Client.new('localhost', 27017, :safe => true)
+@client = Mongo::Client.new('localhost', 27017)
@db = @client['sample-db']
@coll = @db['test']
View
2  bin/mongo_console
@@ -14,7 +14,7 @@ port = org_argv[1] || ENV['MONGO_RUBY_DRIVER_PORT'] || Client::DEFAULT_PORT
dbnm = org_argv[2] || ENV['MONGO_RUBY_DRIVER_DB'] || 'ruby-mongo-console'
puts "Connecting to #{host}:#{port} (CLIENT) on with database #{dbnm} (DB)"
-CLIENT = Client.new(host, port, :safe => true)
+CLIENT = Client.new(host, port)
DB = CLIENT.db(dbnm)
puts "Starting IRB session..."
View
2  examples/admin.rb
@@ -9,7 +9,7 @@
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Client::DEFAULT_PORT
puts "Connecting to #{host}:#{port}"
-client = Mongo::Client.new(host, port, :safe => true)
+client = Mongo::Client.new(host, port)
db = client.db('ruby-mongo-examples')
coll = db.create_collection('test')
View
2  examples/capped.rb
@@ -7,7 +7,7 @@
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Client::DEFAULT_PORT
puts "Connecting to #{host}:#{port}"
-db = Client.new(host, port, :safe => true).db('ruby-mongo-examples')
+db = Client.new(host, port).db('ruby-mongo-examples')
db.drop_collection('test')
# A capped collection has a max size and, optionally, a max number of records.
View
2  examples/cursor.rb
@@ -9,7 +9,7 @@
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Client::DEFAULT_PORT
puts "Connecting to #{host}:#{port}"
-db = Client.new(host, port, :safe => true).db('ruby-mongo-examples')
+db = Client.new(host, port).db('ruby-mongo-examples')
coll = db.collection('test')
# Erase all records from collection, if any
View
2  examples/gridfs.rb
@@ -10,7 +10,7 @@ def assert
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Client::DEFAULT_PORT
puts "Connecting to #{host}:#{port}"
-db = Client.new(host, port, :safe => true).db('ruby-mongo-examples')
+db = Client.new(host, port).db('ruby-mongo-examples')
data = "hello, world!"
View
2  examples/index_test.rb
@@ -8,7 +8,7 @@
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Client::DEFAULT_PORT
puts ">> Connecting to #{host}:#{port}"
-db = Client.new(host, port, :safe => true).db('ruby-mongo-index_test')
+db = Client.new(host, port).db('ruby-mongo-index_test')
class Exception
def errmsg
View
2  examples/info.rb
@@ -8,7 +8,7 @@
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Client::DEFAULT_PORT
puts "Connecting to #{host}:#{port}"
-db = Client.new(host, port, :safe => true).db('ruby-mongo-examples')
+db = Client.new(host, port).db('ruby-mongo-examples')
coll = db.collection('test')
# Erase all records from collection, if any
View
2  examples/queries.rb
@@ -9,7 +9,7 @@
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Client::DEFAULT_PORT
puts "Connecting to #{host}:#{port}"
-db = Client.new(host, port, :safe => true).db('ruby-mongo-examples')
+db = Client.new(host, port).db('ruby-mongo-examples')
coll = db.collection('test')
# Remove all records, if any
View
2  examples/simple.rb
@@ -9,7 +9,7 @@
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Client::DEFAULT_PORT
puts "Connecting to #{host}:#{port}"
-db = Client.new(host, port, :safe => true).db('ruby-mongo-examples')
+db = Client.new(host, port).db('ruby-mongo-examples')
coll = db.collection('test')
# Erase all records from collection, if any
View
2  examples/strict.rb
@@ -8,7 +8,7 @@
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Client::DEFAULT_PORT
puts "Connecting to #{host}:#{port}"
-db = Client.new(host, port, :safe => true).db('ruby-mongo-examples')
+db = Client.new(host, port).db('ruby-mongo-examples')
db.drop_collection('does-not-exist')
db.create_collection('test')
View
2  examples/types.rb
@@ -9,7 +9,7 @@
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Client::DEFAULT_PORT
puts "Connecting to #{host}:#{port}"
-db = Client.new(host, port, :safe => true).db('ruby-mongo-examples')
+db = Client.new(host, port).db('ruby-mongo-examples')
coll = db.collection('test')
# Remove all records, if any
View
2  examples/web/thin/load.rb
@@ -1,7 +1,7 @@
require File.join(File.dirname(__FILE__), '..', '..', '..', 'lib', 'mongo')
require 'logger'
-$con = Mongo::ReplSetClient.new(['localhost:30000', 'localhost:30001'], :safe => true, :read => :secondary, :refresh_mode => :sync, :refresh_interval => 30)
+$con = Mongo::ReplSetClient.new(['localhost:30000', 'localhost:30001'], :read => :secondary, :refresh_mode => :sync, :refresh_interval => 30)
$db = $con['foo']
class Load < Sinatra::Base
View
2  examples/web/unicorn/load.rb
@@ -1,6 +1,6 @@
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'mongo')
-$client = Mongo::Client.new('localhost', 27017, :safe => true)
+$client = Mongo::Client.new('localhost', 27017)
$db = $client['foo']
class Load < Sinatra::Base
View
2  examples/web/unicorn/unicorn.rb.template
@@ -23,7 +23,7 @@ stdout_path "#{@dir}log/unicorn.stdout.log"
# NOTE: You need this when using forking web servers!
after_fork do |server, worker|
$client.close if $client
- $client = Mongo::Client.new('localhost', 27017, :safe => true)
+ $client = Mongo::Client.new('localhost', 27017)
$db = $client['foo']
STDERR << "FORKED #{server} #{worker}"
end
View
3  lib/bson/version.rb
@@ -1,3 +0,0 @@
-module BSON
- VERSION = "1.7.1"
-end
View
6 lib/mongo.rb
@@ -52,6 +52,7 @@ module Constants
require 'mongo/util/conversions'
require 'mongo/util/support'
+require 'mongo/util/write_concern'
require 'mongo/util/core_ext'
require 'mongo/util/logging'
require 'mongo/util/node'
@@ -63,15 +64,16 @@ module Constants
require 'mongo/util/tcp_socket'
require 'mongo/util/uri_parser'
-require 'mongo/collection'
+
require 'mongo/networking'
require 'mongo/client'
require 'mongo/repl_set_client'
require 'mongo/sharded_client'
+require 'mongo/legacy'
+require 'mongo/collection'
require 'mongo/cursor'
require 'mongo/db'
require 'mongo/exceptions'
-require 'mongo/legacy'
require 'mongo/gridfs/grid_ext'
require 'mongo/gridfs/grid'
require 'mongo/gridfs/grid_io'
View
77 lib/mongo/client.rb
@@ -19,28 +19,44 @@
require 'set'
require 'socket'
require 'thread'
+
module Mongo
# Instantiates and manages self.connections to MongoDB.
class Client
include Mongo::Logging
include Mongo::Networking
+ include Mongo::WriteConcern
- TCPSocket = Mongo::TCPSocket
- Mutex = ::Mutex
- ConditionVariable = ::ConditionVariable
+ TCPSocket = Mongo::TCPSocket
+ Mutex = ::Mutex
+ ConditionVariable = ::ConditionVariable
- DEFAULT_HOST = 'localhost'
- DEFAULT_PORT = 27017
- DEFAULT_DB_NAME = 'test'
- GENERIC_OPTS = [:ssl, :auths, :pool_size, :pool_timeout, :timeout, :op_timeout, :connect_timeout, :safe, :logger, :connect]
- CONNECTION_OPTS = [:slave_ok]
+ DEFAULT_HOST = 'localhost'
+ DEFAULT_PORT = 27017
+ DEFAULT_DB_NAME = 'test'
+ GENERIC_OPTS = [:ssl, :auths, :logger, :connect]
+ TIMEOUT_OPTS = [:timeout, :op_timeout, :connect_timeout]
+ POOL_OPTS = [:pool_size, :pool_timeout]
+ WRITE_CONCERN_OPTS = [:w, :j, :fsync, :wtimeout]
+ CLIENT_ONLY_OPTS = [:slave_ok]
mongo_thread_local_accessor :connections
- attr_reader :logger, :size, :auths, :primary, :safe, :host_to_try,
- :pool_size, :connect_timeout, :pool_timeout,
- :primary_pool, :socket_class, :op_timeout, :tag_sets, :acceptable_latency
+ attr_reader :logger,
+ :size,
+ :auths,
+ :primary,
+ :write_concern,
+ :host_to_try,
+ :pool_size,
+ :connect_timeout,
+ :pool_timeout,
+ :primary_pool,
+ :socket_class,
+ :op_timeout,
+ :tag_sets,
+ :acceptable_latency
# Create a connection to single MongoDB instance.
#
@@ -59,10 +75,10 @@ class Client
# @param [String, Hash] host
# @param [Integer] port specify a port number here if only one host is being specified.
#
- # @option opts [Boolean, Hash] :safe (false) Set the default safe-mode options
- # propagated to DB objects instantiated off of this Client. This
- # default can be overridden upon instantiation of any DB by explicitly setting a :safe value
- # on initialization.
+ # @option opts [Hash] :w (1), :j (false), :wtimeout (false), :fsync (false) Set the default write concern
+ # options propagated to DB objects instantiated off of this Client.
+ # This default can be overridden upon instantiation of any DB by explicitly setting an options hash
+ # on initialization. It can also be overridden at instantiation of a collection or at the time of a write operation.
# @option opts [Boolean] :slave_ok (false) Must be set to +true+ when connecting
# to a single, slave node.
# @option opts [Logger, #debug] :logger (nil) A Logger instance for debugging driver ops. Note that
@@ -104,7 +120,7 @@ def initialize(host=nil, port=nil, opts={})
if parser.replicaset?
raise MongoArgumentError, "Mongo::Client.new called with no arguments, but ENV['MONGODB_URI'] implies a replica set."
end
- opts = parser.connection_options.merge! opts
+ opts.merge!(parser.connection_options)
@host_to_try = [parser.host, parser.port]
elsif host.is_a?(String) && (port || DEFAULT_PORT).respond_to?(:to_i)
@host_to_try = [host, (port || DEFAULT_PORT).to_i]
@@ -169,7 +185,8 @@ def self.multi(nodes, opts={})
# Initialize a connection to MongoDB using the MongoDB URI spec.
#
- # Since Client.new cannot be used with any <code>ENV["MONGODB_URI"]</code> that has multiple hosts (implying a replicaset), you may use this when the type of your connection varies by environment and should be determined solely from <code>ENV["MONGODB_URI"]</code>.
+ # Since Client.new cannot be used with any <code>ENV["MONGODB_URI"]</code> that has multiple hosts (implying a replicaset),
+ # you may use this when the type of your connection varies by environment and should be determined solely from <code>ENV["MONGODB_URI"]</code>.
#
# @param uri [String]
# A string of the format mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/database]
@@ -530,7 +547,11 @@ def checkin(socket)
protected
def valid_opts
- GENERIC_OPTS + CONNECTION_OPTS
+ GENERIC_OPTS +
+ CLIENT_ONLY_OPTS +
+ POOL_OPTS +
+ WRITE_CONCERN_OPTS +
+ TIMEOUT_OPTS
end
def check_opts(opts)
@@ -544,10 +565,10 @@ def check_opts(opts)
# Parse option hash
def setup(opts)
# slave_ok can be true only if one node is specified
- @slave_ok = opts[:slave_ok]
+ @slave_ok = opts.delete(:slave_ok)
# Determine whether to use SSL.
- @ssl = opts.fetch(:ssl, false)
+ @ssl = opts.delete(:ssl)
if @ssl
@socket_class = Mongo::SSLSocket
else
@@ -555,27 +576,27 @@ def setup(opts)
end
# Authentication objects
- @auths = opts.fetch(:auths, [])
+ @auths = opts.delete(:auths) || []
# Pool size and timeout.
- @pool_size = opts[:pool_size] || 1
+ @pool_size = opts.delete(:pool_size) || 1
if opts[:timeout]
warn "The :timeout option has been deprecated " +
"and will be removed in the 2.0 release. Use :pool_timeout instead."
end
- @pool_timeout = opts[:pool_timeout] || opts[:timeout] || 5.0
+ @pool_timeout = opts.delete(:pool_timeout) || opts.delete(:timeout) || 5.0
# Timeout on socket read operation.
- @op_timeout = opts[:op_timeout] || nil
+ @op_timeout = opts.delete(:op_timeout) || nil
# Timeout on socket connect.
- @connect_timeout = opts[:connect_timeout] || nil
-
- # Global safe option. This is false by default.
- @safe = opts[:safe] || false
+ @connect_timeout = opts.delete(:connect_timeout) || nil
@logger = opts.fetch(:logger, nil)
+ # Connection level write concern options.
+ @write_concern = get_write_concern(opts)
+
if @logger
write_logging_startup_message
end
View
115 lib/mongo/collection.rb
@@ -20,11 +20,18 @@ module Mongo
# A named collection of documents in a database.
class Collection
include Mongo::Logging
+ include Mongo::WriteConcern
- attr_reader :db, :name, :pk_factory, :hint, :safe
+ attr_reader :db,
+ :name,
+ :pk_factory,
+ :hint,
+ :write_concern
# Read Preference
- attr_accessor :read_preference, :tag_sets, :acceptable_latency
+ attr_accessor :read_preference,
+ :tag_sets,
+ :acceptable_latency
# Initialize a collection object.
#
@@ -34,10 +41,10 @@ class Collection
# @option opts [:create_pk] :pk (BSON::ObjectId) A primary key factory to use
# other than the default BSON::ObjectId.
#
- # @option opts [Boolean, Hash] :safe (false) Set the default safe-mode options
+ # @option opts [Hash] :w, :j, :wtimeout, :fsync Set the default write concern
# for +insert+, +update+, and +remove+ method called on this Collection instance. If no
- # value is provided, the default value set on this instance's DB will be used. This
- # default can be overridden for any invocation of +insert+, +update+, or +remove+.
+ # value is provided, the default values set on this instance's DB will be used. These option
+ # values can be overridden for any invocation of +insert+, +update+, or +remove+.
# @option options [:primary, :secondary] :read The default read preference for queries
# initiates from this connection object. If +:secondary+ is chosen, reads will be sent
# to one of the closest available secondary nodes. If a secondary node cannot be located, the
@@ -93,7 +100,7 @@ def initialize(name, db, opts={})
@cache_time = @db.cache_time
@cache = Hash.new(0)
unless pk_factory
- @safe = opts.fetch(:safe, @db.safe)
+ @write_concern = get_write_concern(opts, db)
if value = opts[:read]
Mongo::Support.validate_read_preference(value)
else
@@ -312,22 +319,24 @@ def find_one(spec_or_object_id=nil, opts={})
#
# @return [ObjectId] the _id of the saved document.
#
- # @option opts [Boolean, Hash] :safe (+false+)
- # run the operation in safe mode, which runs a +getlasterror+ command on the
- # database to report any assertion. In addition, a hash can be provided to
- # run an fsync and/or wait for replication (>= 1.5.1). Safe
- # options provided here will override any safe options set on this collection,
+ # @option opts [Hash] :w, :j, :wtimeout, :fsync Set the write concern for this operation.
+ # :w > 0 will run a +getlasterror+ command on the database to report any assertion.
+ # :j will confirm a write has been committed to the journal
+ # :wtimeout specifies how long to wait for write confirmation
+ # :fsync will confirm that a write has been fsynced
+ # options provided here will override any write concern options set on this collection,
# its database object, or the current connection. See the options
# for +DB#get_last_error+.
#
- # @raise [Mongo::OperationFailure] will be raised iff safe mode is enabled and the operation fails.
+ # @raise [Mongo::OperationFailure] will be raised iff :w > 0 and the operation fails.
def save(doc, opts={})
+ write_concern = get_write_concern(opts, self)
if doc.has_key?(:_id) || doc.has_key?('_id')
id = doc[:_id] || doc['_id']
- update({:_id => id}, doc, :upsert => true, :safe => opts.fetch(:safe, @safe))
+ update({:_id => id}, doc, :upsert => true, :write_concern => write_concern)
id
else
- insert(doc, :safe => opts.fetch(:safe, @safe))
+ insert(doc, write_concern)
end
end
@@ -343,33 +352,34 @@ def save(doc, opts={})
# 2nd, a list of invalid documents.
# Return this result format only when :collect_on_error is true.
#
- # @option opts [Boolean, Hash] :safe (+false+)
- # run the operation in safe mode, which runs a +getlasterror+ command on the
- # database to report any assertion. In addition, a hash can be provided to
- # run an fsync and/or wait for replication of the insert (>= 1.5.1). Safe
- # options provided here will override any safe options set on this collection,
+ # @option opts [Hash] :w, :j, :wtimeout, :fsync Set the write concern for this operation.
+ # :w > 0 will run a +getlasterror+ command on the database to report any assertion.
+ # :j will confirm a write has been committed to the journal
+ # :wtimeout specifies how long to wait for write confirmation
+ # :fsync will confirm that a write has been fsynced
+ # options provided here will override any write concern options set on this collection,
# its database object, or the current connection. See the options
# for +DB#get_last_error+.
#
# @option opts [Boolean] :continue_on_error (+false+) If true, then
# continue a bulk insert even if one of the documents inserted
# triggers a database assertion (as in a duplicate insert, for instance).
- # If not using safe mode, the list of ids returned will
+ # If not acknowledging writes, the list of ids returned will
# include the object ids of all documents attempted on insert, even
- # if some are rejected on error. When safe mode is
- # enabled, any error will raise an OperationFailure exception.
+ # if some are rejected on error. When acknowledging writes, any error will raise an
+ # OperationFailure exception.
# MongoDB v2.0+.
# @option opts [Boolean] :collect_on_error (+false+) if true, then
# collects invalid documents as an array. Note that this option changes the result format.
#
- # @raise [Mongo::OperationFailure] will be raised iff safe mode is enabled and the operation fails.
+ # @raise [Mongo::OperationFailure] will be raised iff :w > 0 and the operation fails.
#
# @core insert insert-instance_method
def insert(doc_or_docs, opts={})
doc_or_docs = [doc_or_docs] unless doc_or_docs.is_a?(Array)
doc_or_docs.collect! { |doc| @pk_factory.create_pk(doc) }
- safe = opts.fetch(:safe, @safe)
- result = insert_documents(doc_or_docs, @name, true, safe, opts)
+ write_concern = get_write_concern(opts, self)
+ result = insert_documents(doc_or_docs, @name, true, write_concern, opts)
result.size > 1 ? result : result.first
end
alias_method :<<, :insert
@@ -379,13 +389,14 @@ def insert(doc_or_docs, opts={})
# @param [Hash] selector
# If specified, only matching documents will be removed.
#
- # @option opts [Boolean, Hash] :safe (+false+)
- # run the operation in safe mode, which runs a +getlasterror+ command on the
- # database to report any assertion. In addition, a hash can be provided to
- # run an fsync and/or wait for replication of the remove (>= 1.5.1). Safe
- # options provided here will override any safe options set on this collection,
+ # @option opts [Hash] :w, :j, :wtimeout, :fsync Set the write concern for this operation.
+ # :w > 0 will run a +getlasterror+ command on the database to report any assertion.
+ # :j will confirm a write has been committed to the journal
+ # :wtimeout specifies how long to wait for write confirmation
+ # :fsync will confirm that a write has been fsynced
+ # options provided here will override any write concern options set on this collection,
# its database object, or the current connection. See the options
- # for +DB#get_last_error+.
+ # for +DB#get_last_error+.
#
# @example remove all documents from the 'users' collection:
# users.remove
@@ -394,23 +405,22 @@ def insert(doc_or_docs, opts={})
# @example remove only documents that have expired:
# users.remove({:expire => {"$lte" => Time.now}})
#
- # @return [Hash, true] Returns a Hash containing the last error object if running in safe mode.
+ # @return [Hash, true] Returns a Hash containing the last error object if acknowledging writes
# Otherwise, returns true.
#
- # @raise [Mongo::OperationFailure] will be raised iff safe mode is enabled and the operation fails.
+ # @raise [Mongo::OperationFailure] will be raised iff :w > 0 and the operation fails.
#
# @core remove remove-instance_method
def remove(selector={}, opts={})
- # Initial byte is 0.
- safe = opts.fetch(:safe, @safe)
+ write_concern = get_write_concern(opts, self)
message = BSON::ByteBuffer.new("\0\0\0\0")
BSON::BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@name}")
message.put_int(0)
message.put_binary(BSON::BSON_CODER.serialize(selector, false, true, @connection.max_bson_size).to_s)
instrument(:remove, :database => @db.name, :collection => @name, :selector => selector) do
- if safe
- @connection.send_message_with_safe_check(Mongo::Constants::OP_DELETE, message, @db.name, nil, safe)
+ if Mongo::WriteConcern.gle?(write_concern)
+ @connection.send_message_with_acknowledge(Mongo::Constants::OP_DELETE, message, @db.name, nil, write_concern)
else
@connection.send_message(Mongo::Constants::OP_DELETE, message)
true
@@ -432,23 +442,24 @@ def remove(selector={}, opts={})
# @option opts [Boolean] :upsert (+false+) if true, performs an upsert (update or insert)
# @option opts [Boolean] :multi (+false+) update all documents matching the selector, as opposed to
# just the first matching document. Note: only works in MongoDB 1.1.3 or later.
- # @option opts [Boolean, Hash] :safe (+false+)
- # run the operation in safe mode, which runs a +getlasterror+ command on the
- # database to report any assertion. In addition, a hash can be provided to
- # run an fsync and/or wait for replication of the update (>= 1.5.1). Safe
- # options provided here will override any safe options set on this collection,
+ # @option opts [Hash] :w, :j, :wtimeout, :fsync Set the write concern for this operation.
+ # :w > 0 will run a +getlasterror+ command on the database to report any assertion.
+ # :j will confirm a write has been committed to the journal
+ # :wtimeout specifies how long to wait for write confirmation
+ # :fsync will confirm that a write has been fsynced
+ # options provided here will override any write concern options set on this collection,
# its database object, or the current connection. See the options
- # for +DB#get_last_error+.
+ # for +DB#get_last_error+.
#
- # @return [Hash, true] Returns a Hash containing the last error object if running in safe mode.
+ # @return [Hash, true] Returns a Hash containing the last error object if acknowledging writes.
# Otherwise, returns true.
#
- # @raise [Mongo::OperationFailure] will be raised iff safe mode is enabled and the operation fails.
+ # @raise [Mongo::OperationFailure] will be raised iff :w > 0 and the operation fails.
#
# @core update update-instance_method
def update(selector, document, opts={})
# Initial byte is 0.
- safe = opts.fetch(:safe, @safe)
+ write_concern = get_write_concern(opts, self)
message = BSON::ByteBuffer.new("\0\0\0\0")
BSON::BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@name}")
update_options = 0
@@ -463,8 +474,8 @@ def update(selector, document, opts={})
message.put_binary(BSON::BSON_CODER.serialize(document, check_keys, true, @connection.max_bson_size).to_s)
instrument(:update, :database => @db.name, :collection => @name, :selector => selector, :document => document) do
- if safe
- @connection.send_message_with_safe_check(Mongo::Constants::OP_UPDATE, message, @db.name, nil, safe)
+ if Mongo::WriteConcern.gle?(write_concern)
+ @connection.send_message_with_acknowledge(Mongo::Constants::OP_UPDATE, message, @db.name, nil, write_concern)
else
@connection.send_message(Mongo::Constants::OP_UPDATE, message)
end
@@ -987,7 +998,7 @@ def generate_indexes(field_spec, name, opts)
selector.merge!(opts)
begin
- insert_documents([selector], Mongo::DB::SYSTEM_INDEX_COLLECTION, false, true)
+ insert_documents([selector], Mongo::DB::SYSTEM_INDEX_COLLECTION, false, {:w => 1})
rescue Mongo::OperationFailure => e
if selector[:dropDups] && e.message =~ /^11000/
@@ -1004,7 +1015,7 @@ def generate_indexes(field_spec, name, opts)
# Sends a Mongo::Constants::OP_INSERT message to the database.
# Takes an array of +documents+, an optional +collection_name+, and a
# +check_keys+ setting.
- def insert_documents(documents, collection_name=@name, check_keys=true, safe=false, flags={})
+ def insert_documents(documents, collection_name=@name, check_keys=true, write_concern={}, flags={})
if flags[:continue_on_error]
message = BSON::ByteBuffer.new
message.put_int(1)
@@ -1036,8 +1047,8 @@ def insert_documents(documents, collection_name=@name, check_keys=true, safe=fal
raise InvalidOperation, "Exceded maximum insert size of 16,777,216 bytes" if message.size > @connection.max_bson_size
instrument(:insert, :database => @db.name, :collection => collection_name, :documents => documents) do
- if safe
- @connection.send_message_with_safe_check(Mongo::Constants::OP_INSERT, message, @db.name, nil, safe)
+ if Mongo::WriteConcern.gle?(write_concern)
+ @connection.send_message_with_acknowledge(Mongo::Constants::OP_INSERT, message, @db.name, nil, write_concern)
else
@connection.send_message(Mongo::Constants::OP_INSERT, message)
end
View
34 lib/mongo/db.rb
@@ -23,13 +23,14 @@ module Mongo
# A MongoDB database.
class DB
+ include Mongo::WriteConcern
- SYSTEM_NAMESPACE_COLLECTION = "system.namespaces"
- SYSTEM_INDEX_COLLECTION = "system.indexes"
- SYSTEM_PROFILE_COLLECTION = "system.profile"
- SYSTEM_USER_COLLECTION = "system.users"
- SYSTEM_JS_COLLECTION = "system.js"
- SYSTEM_COMMAND_COLLECTION = "$cmd"
+ SYSTEM_NAMESPACE_COLLECTION = 'system.namespaces'
+ SYSTEM_INDEX_COLLECTION = 'system.indexes'
+ SYSTEM_PROFILE_COLLECTION = 'system.profile'
+ SYSTEM_USER_COLLECTION = 'system.users'
+ SYSTEM_JS_COLLECTION = 'system.js'
+ SYSTEM_COMMAND_COLLECTION = '$cmd'
# Counter for generating unique request ids.
@@current_request_id = 0
@@ -44,8 +45,8 @@ class DB
# Returns the value of the +strict+ flag.
def strict?; @strict; end
- # The name of the database and the local safe option.
- attr_reader :name, :safe
+ # The name of the database and the local write concern options.
+ attr_reader :name, :write_concern
# The Mongo::Client instance connecting to the MongoDB server.
attr_reader :connection
@@ -70,10 +71,10 @@ def strict?; @strict; end
# fields the factory wishes to inject. (NOTE: if the object already has a primary key,
# the factory should not inject a new key).
#
- # @option opts [Boolean, Hash] :safe (false) Set the default safe-mode options
- # propagated to Collection objects instantiated off of this DB. If no
- # value is provided, the default value set on this instance's Client object will be used. This
- # default can be overridden upon instantiation of any collection by explicitly setting a :safe value
+ # @option opts [Hash] :w, :j, :wtimeout, :fsync Set the default write concern for this database.
+ # Propagated to Collection objects instantiated off of this DB. If no
+ # options are provided, the default write concern set on this instance's Client object will be used. This
+ # default can be overridden upon instantiation of any collection by explicitly setting write concern values
# on initialization
#
# @option opts [Integer] :cache_time (300) Set the time that all ensure_index calls should cache the command.
@@ -84,7 +85,9 @@ def initialize(name, client, opts={})
@connection = client
@strict = opts[:strict]
@pk_factory = opts[:pk]
- @safe = opts.fetch(:safe, @connection.safe)
+
+ @write_concern = get_write_concern(opts, client)
+
if value = opts[:read]
Mongo::Support.validate_read_preference(value)
else
@@ -173,7 +176,7 @@ def add_stored_function(function_name, code)
# @return [Boolean]
def remove_stored_function(function_name)
if self[SYSTEM_JS_COLLECTION].find_one({"_id" => function_name})
- self[SYSTEM_JS_COLLECTION].remove({"_id" => function_name}, :safe => true)
+ self[SYSTEM_JS_COLLECTION].remove({"_id" => function_name}, :w => 1)
else
return false
end
@@ -205,7 +208,7 @@ def add_user(username, password, read_only = false)
# @return [Boolean]
def remove_user(username)
if self[SYSTEM_USER_COLLECTION].find_one({:user => username})
- self[SYSTEM_USER_COLLECTION].remove({:user => username}, :safe => true)
+ self[SYSTEM_USER_COLLECTION].remove({:user => username}, :w => 1)
else
return false
end
@@ -323,7 +326,6 @@ def collection(name, opts={})
"Currently in strict mode."
else
opts = opts.dup
- opts[:safe] = opts.fetch(:safe, @safe)
opts.merge!(:pk => @pk_factory) unless opts[:pk]
Collection.new(name, self, opts)
end
View
5 lib/mongo/gridfs/grid.rb
@@ -62,8 +62,9 @@ def initialize(db, fs_name=DEFAULT_FS_NAME)
# the content type will may be inferred from the filename extension if the mime-types gem can be
# loaded. Otherwise, the content type 'binary/octet-stream' will be used.
# @option opts [Integer] (262144) :chunk_size size of file chunks in bytes.
- # @option opts [Boolean] :safe (false) When safe mode is enabled, the chunks sent to the server
- # will be validated using an md5 hash. If validation fails, an exception will be raised.
+ # @option opts [Boolean] :w (1), :j (false), :wtimeout (false), :fsync (false) Set the default write concern
+ # When :w > 0, the chunks sent to the server are validated using an md5 hash.
+ # If validation fails, an exception will be raised.
#
# @return [BSON::ObjectId] the file's id.
def put(data, opts={})
View
3  lib/mongo/gridfs/grid_file_system.rb
@@ -69,7 +69,8 @@ def initialize(db, fs_name=Grid::DEFAULT_FS_NAME)
# @option opts [Boolean] :delete_old (false) ensure that old versions of the file are deleted. This option
# only work in 'w' mode. Certain precautions must be taken when deleting GridFS files. See the notes under
# GridFileSystem#delete.
- # @option opts [Boolean] :safe (false) When safe mode is enabled, the chunks sent to the server
+ # @option opts [Boolean] :w (1), :j (false), :wtimeout (false), :fsync (false) Set the default write concern
+ # When :w > 0, the chunks sent to the server
# will be validated using an md5 hash. If validation fails, an exception will be raised.
# @option opts [Integer] :versions (false) deletes all versions which exceed the number specified to
# retain ordered by uploadDate. This option only works in 'w' mode. Certain precautions must be taken when
View
3  lib/mongo/gridfs/grid_io.rb
@@ -48,7 +48,8 @@ class GridIO
# @option opts [String] :content_type ('binary/octet-stream') If no content type is specified,
# the content type will may be inferred from the filename extension if the mime-types gem can be
# loaded. Otherwise, the content type 'binary/octet-stream' will be used.
- # @option opts [Boolean] :safe (false) When safe mode is enabled, the chunks sent to the server
+ # @option opts [Boolean] :w (1), :j (false), :wtimeout (false), :fsync (false) Set the default write concern
+ # When :w > 0, the chunks sent to the server
# will be validated using an md5 hash. If validation fails, an exception will be raised.
def initialize(files, chunks, filename, mode, opts={})
@files = files
View
51 lib/mongo/legacy.rb
@@ -17,26 +17,65 @@
# ++
module Mongo
- # @deprecated Use Mongo::Client instead.
+ module LegacyWriteConcern
+ @legacy_write_concern = true
+
+ def safe=(value)
+ @write_concern = value
+ end
+
+ def safe
+ if @write_concern[:w] == 0
+ return false
+ elsif @write_concern[:w] == 1
+ return true
+ else
+ return @write_concern
+ end
+ end
+
+ def self.from_uri(uri = ENV['MONGODB_URI'], extra_opts={})
+ parser = URIParser.new uri
+ parser.connection(extra_opts, true)
+ end
+ end
+end
+
+module Mongo
+ # @deprecated Use Mongo::Client instead. Support will be removed after v2.0
class Connection < Client
+ include Mongo::LegacyWriteConcern
+
def initialize(host=nil, port=nil, opts={})
- warn '[DEPRECATED] Mongo::Connection has been replaced with Mongo::Client.'
+ write_concern_from_legacy(opts)
super
end
end
- # @deprecated Use Mongo::ReplSetClient instead.
+ # @deprecated Use Mongo::ReplSetClient instead. Support will be removed after v2.0
class ReplSetConnection < ReplSetClient
+ include Mongo::LegacyWriteConcern
+
def initialize(*args)
- warn '[DEPRECATED] Mongo::ReplSetConnection has been replaced with Mongo::ReplSetClient.'
+ if args.last.is_a?(Hash)
+ opts = args.pop
+ write_concern_from_legacy(opts)
+ args.push(opts)
+ end
super
end
end
- # @deprecated Use Mongo::ShardedClient instead.
+ # @deprecated Use Mongo::ShardedClient instead. Support will be removed after v2.0
class ShardedConnection < ShardedClient
+ include Mongo::LegacyWriteConcern
+
def initialize(*args)
- warn '[DEPRECATED] Mongo::ShardedConnection has been replaced with Mongo::ShardedClient.'
+ if args.last.is_a?(Hash)
+ opts = args.pop
+ write_concern_from_legacy(opts)
+ args.push(opts)
+ end
super
end
end
View
13 lib/mongo/networking.rb
@@ -56,7 +56,7 @@ def send_message(operation, message, opts={})
# @see DB#get_last_error for valid last error params.
#
# @return [Hash] The document returned by the call to getlasterror.
- def send_message_with_safe_check(operation, message, db_name, log_message=nil, last_error_params=false)
+ def send_message_with_acknowledge(operation, message, db_name, log_message=nil, last_error_params=false)
docs = num_received = cursor_id = ''
add_message_headers(message, operation)
@@ -210,14 +210,23 @@ def read_documents(number_received, sock)
end
# Constructs a getlasterror message. This method is used exclusively by
- # Client#send_message_with_safe_check.
+ # Client#send_message_with_acknowledge.
#
# Because it modifies message by reference, we don't need to return it.
def build_last_error_message(message, db_name, opts)
+
+ # flags bit vector
message.put_int(0)
+
+ # namespace
BSON::BSON_RUBY.serialize_cstr(message, "#{db_name}.$cmd")
+
+ # number to skip
message.put_int(0)
+
+ # numer to return (-1 closes cursor)
message.put_int(-1)
+
cmd = BSON::OrderedHash.new
cmd[:getlasterror] = 1
if opts.is_a?(Hash)
View
48 lib/mongo/repl_set_client.rb
@@ -21,11 +21,25 @@ module Mongo
# Instantiates and manages connections to a MongoDB replica set.
class ReplSetClient < Client
- REPL_SET_OPTS = [:read, :refresh_mode, :refresh_interval, :read_secondary,
- :rs_name, :name, :tag_sets, :secondary_acceptable_latency_ms]
-
- attr_reader :replica_set_name, :seeds, :refresh_interval, :refresh_mode,
- :refresh_version, :manager, :tag_sets, :acceptable_latency
+ REPL_SET_OPTS = [
+ :read,
+ :refresh_mode,
+ :refresh_interval,
+ :read_secondary,
+ :rs_name,
+ :name,
+ :tag_sets,
+ :secondary_acceptable_latency_ms
+ ]
+
+ attr_reader :replica_set_name,
+ :seeds,
+ :refresh_interval,
+ :refresh_mode,
+ :refresh_version,
+ :manager,
+ :tag_sets,
+ :acceptable_latency
# Create a connection to a MongoDB replica set.
#
@@ -39,9 +53,9 @@ class ReplSetClient < Client
# @overload initialize(seeds=ENV["MONGODB_URI"], opts={})
# @param [Array<String>, Array<Array(String, Integer)>] seeds
#
- # @option opts [Boolean, Hash] :safe (false) Set the default safe-mode options
+ # @option opts [Hash] :w (1), :j (false), :wtimeout (false), :fsync (false) Set the default write concern
# propagated to DB objects instantiated off of this Client. This
- # default can be overridden upon instantiation of any DB by explicitly setting a :safe value
+ # default can be overridden upon instantiation of any DB by explicitly setting write concern values
# on initialization.
# @option opts [:primary, :primary_preferred, :secondary, :secondary_preferred, :nearest] :read_preference (:primary)
# A "read preference" determines the candidate replica set members to which a query or command can be sent.
@@ -150,7 +164,7 @@ def initialize(*args)
end
def valid_opts
- GENERIC_OPTS + REPL_SET_OPTS
+ super + REPL_SET_OPTS - CLIENT_ONLY_OPTS
end
def inspect
@@ -437,8 +451,8 @@ def max_bson_size
# Parse option hash
def setup(opts)
# Refresh
- @refresh_mode = opts.fetch(:refresh_mode, false)
- @refresh_interval = opts.fetch(:refresh_interval, 90)
+ @refresh_mode = opts.delete(:refresh_mode) || false
+ @refresh_interval = opts.delete(:refresh_interval) || 90
if @refresh_mode && @refresh_interval < 60
@refresh_interval = 60 unless ENV['TEST_MODE'] = 'TRUE'
@@ -456,26 +470,26 @@ def setup(opts)
if opts[:read_secondary]
warn ":read_secondary options has now been deprecated and will " +
"be removed in driver v2.0. Use the :read option instead."
- @read_secondary = opts.fetch(:read_secondary, false)
+ @read_secondary = opts.delete(:read_secondary) || false
@read = :secondary_preferred
else
- @read = opts.fetch(:read, :primary)
+ @read = opts.delete(:read) || :primary
Mongo::Support.validate_read_preference(@read)
end
- @tag_sets = opts.fetch(:tag_sets, [])
- @acceptable_latency = opts.fetch(:secondary_acceptable_latency_ms, 15)
+ @tag_sets = opts.delete(:tag_sets) || []
+ @acceptable_latency = opts.delete(:secondary_acceptable_latency_ms) || 15
# Replica set name
if opts[:rs_name]
warn ":rs_name option has been deprecated and will be removed in v2.0. " +
"Please use :name instead."
- @replica_set_name = opts[:rs_name]
+ @replica_set_name = opts.delete(:rs_name)
else
- @replica_set_name = opts[:name]
+ @replica_set_name = opts.delete(:name)
end
- opts[:connect_timeout] = opts[:connect_timeout] || 30
+ opts[:connect_timeout] = opts.delete(:connect_timeout) || 30
super opts
end
View
4 lib/mongo/sharded_client.rb
@@ -34,9 +34,9 @@ class ShardedClient < ReplSetClient
#
# @option opts [String] :name (nil) The name of the sharded cluster to connect to. You
# can use this option to verify that you're connecting to the right sharded cluster.
- # @option opts [Boolean, Hash] :safe (false) Set the default safe-mode options
+ # @option opts [Hash] ::w (1), :j (false), :wtimeout (false), :fsync (false) Set the default write concern
# propagated to DB objects instantiated off of this Client. This
- # default can be overridden upon instantiation of any DB by explicitly setting a :safe value
+ # default can be overridden upon instantiation of any DB by explicitly setting a write concern values
# on initialization.
# @option opts [Logger] :logger (nil) Logger instance to receive driver operation log.
# @option opts [Integer] :pool_size (1) The maximum number of socket connections allowed per
View
2  lib/mongo/util/ssl_socket.rb
@@ -20,7 +20,7 @@ def initialize(host, port, op_timeout=nil, connect_timeout=nil)
@ssl = OpenSSL::SSL::SSLSocket.new(@socket)
@ssl.sync_close = true
-
+
connect
end
View
22 lib/mongo/util/support.rb
@@ -20,15 +20,31 @@
module Mongo
module Support
+
include Mongo::Conversions
extend self
- READ_PREFERENCES = [:primary, :primary_preferred, :secondary, :secondary_preferred, :nearest]
+ READ_PREFERENCES = [
+ :primary,
+ :primary_preferred,
+ :secondary,
+ :secondary_preferred,
+ :nearest
+ ]
# Commands that may be sent to replica-set secondaries, depending on
# read preference and tags. All other commands are always run on the primary.
- SECONDARY_OK_COMMANDS = ['group', 'aggregate', 'collstats', 'dbstats', 'count', 'distinct',
- 'geonear', 'geosearch', 'geowalk']
+ SECONDARY_OK_COMMANDS = [
+ 'group',
+ 'aggregate',
+ 'collstats',
+ 'dbstats',
+ 'count',
+ 'distinct',
+ 'geonear',
+ 'geosearch',
+ 'geowalk'
+ ]
# Generate an MD5 for authentication.
#
View
76 lib/mongo/util/uri_parser.rb
@@ -35,7 +35,20 @@ class URIParser
MONGODB_URI_SPEC = "mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]"
SPEC_ATTRS = [:nodes, :auths]
- OPT_ATTRS = [:connect, :replicaset, :slaveok, :safe, :w, :wtimeout, :fsync, :journal, :connecttimeoutms, :sockettimeoutms, :wtimeoutms]
+
+ OPT_ATTRS = [
+ :connect,
+ :replicaset,
+ :slaveok,
+ :safe,
+ :w,
+ :wtimeout,
+ :fsync,
+ :journal,
+ :connecttimeoutms,
+ :sockettimeoutms,
+ :wtimeoutms
+ ]
OPT_VALID = {:connect => lambda {|arg| ['direct', 'replicaset', 'true', 'false', true, false].include?(arg)},
:replicaset => lambda {|arg| arg.length > 0},
@@ -76,7 +89,18 @@ class URIParser
:wtimeoutms => lambda {|arg| arg.to_i }
}
- attr_reader :auths, :connect, :replicaset, :slaveok, :safe, :w, :wtimeout, :fsync, :journal, :connecttimeoutms, :sockettimeoutms, :wtimeoutms
+ attr_reader :auths,
+ :connect,
+ :replicaset,
+ :slaveok,
+ :safe,
+ :w,
+ :wtimeout,
+ :fsync,
+ :journal,
+ :connecttimeoutms,
+ :sockettimeoutms,
+ :wtimeoutms
# Parse a MongoDB URI. This method is used by Client.from_uri.
# Returns an array of nodes and an array of db authorizations, if applicable.
@@ -105,12 +129,20 @@ def initialize(uri)
# @note Don't confuse this with attribute getter method #connect.
#
# @return [Client,ReplSetClient]
- def connection(extra_opts)
+ def connection(extra_opts, legacy=false)
opts = connection_options.merge! extra_opts
- if replicaset?
- ReplSetClient.new(nodes, opts)
+ if(legacy)
+ if replicaset?
+ ReplSetConnection.new(nodes, opts)
+ else
+ Connection.new(host, port, opts)
+ end
else
- Client.new(host, port, opts)
+ if replicaset?
+ ReplSetClient.new(nodes, opts)
+ else
+ Client.new(host, port, opts)
+ end
end
end
@@ -152,32 +184,16 @@ def port
def connection_options
opts = {}
- if (@w || @journal || @wtimeout || @fsync || @wtimeoutms) && !@safe
- raise MongoArgumentError, "Safe must be true if w, journal, wtimeoutMS, or fsync is specified"
+ if @wtimeout
+ warn "Using wtimeout in a URI is deprecated, please use wtimeoutMS. It will be removed in v2.0."
+ opts[:wtimeout] = @wtimeout
end
+ opts[:wtimeout] = @wtimeoutms
- if @safe
- if @w || @journal || @wtimeout || @fsync || @wtimeoutms
- safe_opts = {}
- safe_opts[:w] = @w if @w
- safe_opts[:j] = @journal if @journal
-
- if @wtimeout
- warn "Using wtimeout in a URI is deprecated, please use wtimeoutMS. It will be removed in v2.0."
- safe_opts[:wtimeout] = @wtimeout
- end
-
- if @wtimeoutms
- safe_opts[:wtimeout] = @wtimeoutms
- end
-
- safe_opts[:fsync] = @fsync if @fsync
- else
- safe_opts = true
- end
-
- opts[:safe] = safe_opts
- end
+ opts[:w] = 1 if @safe
+ opts[:w] = @w if @w
+ opts[:j] = @journal
+ opts[:fsync] = @fsync
if @connecttimeoutms
opts[:connect_timeout] = @connecttimeoutms
View
67 lib/mongo/util/write_concern.rb
@@ -0,0 +1,67 @@
+# encoding: UTF-8
+
+# --
+# Copyright (C) 2008-2011 10gen Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ++
+
+module Mongo
+ module WriteConcern
+
+ attr_reader :legacy_write_concern
+
+ @@safe_warn = nil
+ def write_concern_from_legacy(opts)
+ # Warn if 'safe' parameter is being used,
+ if opts.key?(:safe) && !@@safe_warn && !ENV['TEST_MODE']
+ warn "[DEPRECATED] The 'safe' write concern option has been deprecated in favor of 'w'."
+ @@safe_warn = true
+ end
+
+ # nil: set :w => 0
+ # false: set :w => 0
+ # true: set :w => 1
+ # hash: set :w => 0 and merge with opts
+
+ unless opts.has_key?(:w)
+ opts[:w] = 0 # legacy default, unacknowledged
+ safe = opts.delete(:safe)
+ if(safe && safe.is_a?(Hash))
+ opts.merge!(safe)
+ elsif(safe == true)
+ opts[:w] = 1
+ end
+ end
+ end
+
+ # todo: throw exception for conflicting write concern options
+ def get_write_concern(opts, parent=nil)
+ write_concern_from_legacy(opts) if opts.key?(:safe) || @legacy_write_concern
+ write_concern = {
+ :w => 1,
+ :j => false,
+ :fsync => false,
+ :wtimeout => false
+ }
+ write_concern.merge!(parent.write_concern) if parent
+ write_concern.merge!(opts.select {|k| write_concern.keys.include?(k)})
+ write_concern
+ end
+
+ def self.gle?(write_concern)
+ write_concern[:w] > 0 || write_concern[:j] || write_concern[:fsync] || write_concern[:wtimeout]
+ end
+
+ end
+end
View
3  lib/mongo/version.rb
@@ -1,3 +0,0 @@
-module Mongo
- VERSION = "1.7.0"
-end
View
10 tasks/testing.rake
@@ -17,6 +17,12 @@ namespace :test do
desc "Runs default test suites"
task :ruby do
+ SimpleCov.start do
+ add_group "Mongo", 'lib/mongo'
+ add_group "BSON", 'lib/bson'
+ add_filter "/test/"
+ end
+
if ENV['TEST']
Rake::Task['test:functional'].invoke
else
@@ -55,12 +61,12 @@ namespace :test do
require 'mongo'
client = Mongo::Client.new(
ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
- ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Client::DEFAULT_PORT, :safe => true)
+ ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Client::DEFAULT_PORT)
client.database_names.each {|name| client.drop_database(name) if name =~ /^ruby-test/ }
if File.directory?('data')
puts "[CLEAN-UP] Removing replica set data files..."
- Dir.delete('data')
+ FileUtils.rm_rf 'data'
end
end
View
22 test/auxillary/authentication_test.rb
@@ -28,18 +28,18 @@ def test_authenticate
@admin.logout
assert_raise Mongo::OperationFailure do
- @db1['stuff'].insert({:a => 2}, :safe => true)
+ @db1['stuff'].insert({:a => 2})
end
assert_raise Mongo::OperationFailure do
- @db2['stuff'].insert({:a => 2}, :safe => true)
+ @db2['stuff'].insert({:a => 2})
end
@db1.authenticate('user1', 'secret')
@db2.authenticate('user2', 'secret')
- assert @db1['stuff'].insert({:a => 2}, :safe => true)
- assert @db2['stuff'].insert({:a => 2}, :safe => true)
+ assert @db1['stuff'].insert({:a => 2})
+ assert @db2['stuff'].insert({:a => 2})
puts "Please bounce the server."
gets
@@ -50,24 +50,24 @@ def test_authenticate
rescue Mongo::ConnectionFailure
end
- assert @db1['stuff'].insert({:a => 2}, :safe => true)
- assert @db2['stuff'].insert({:a => 2}, :safe => true)
- assert @db2['stuff'].find({}, :safe => true)
+ assert @db1['stuff'].insert({:a => 2})
+ assert @db2['stuff'].insert({:a => 2})
+ assert @db2['stuff'].find({})
@db1.logout
assert_raise Mongo::OperationFailure do
- @db1['stuff'].insert({:a => 2}, :safe => true)
+ @db1['stuff'].insert({:a => 2})
end
@db2.logout
assert_raise Mongo::OperationFailure do
- assert @db2['stuff'].insert({:a => 2}, :safe => true)
+ assert @db2['stuff'].insert({:a => 2})
end
@db2.authenticate('userRO', 'secret')
- assert @db2['stuff'].find({}, :safe => true)
+ assert @db2['stuff'].find({})
assert_raise Mongo::OperationFailure do
- assert @db2['stuff'].insert({:a => 2}, :safe => true)
+ assert @db2['stuff'].insert({:a => 2})
end
end
View
4 test/auxillary/repl_set_auth_test.rb
@@ -21,14 +21,14 @@ def test_repl_set_auth
# Ensure that insert fails
assert_raise_error Mongo::OperationFailure, "unauthorized" do
- @client['foo']['stuff'].insert({:a => 2}, :safe => {:w => 3})
+ @client['foo']['stuff'].insert({:a => 2}, {:w => 3})
end
# Then authenticate
assert @client['admin'].authenticate("me", "secret")
# Insert should succeed now
- assert @client['foo']['stuff'].insert({:a => 2}, :safe => {:w => 3})
+ assert @client['foo']['stuff'].insert({:a => 2}, {:w => 3})
# So should a query
assert @client['foo']['stuff'].find_one
View
16 test/auxillary/threaded_authentication_test.rb
@@ -43,13 +43,13 @@ def test_authenticate
threaded_exec do
assert_raise Mongo::OperationFailure do
- @db1['stuff'].insert({:a => 2}, :safe => true)
+ @db1['stuff'].insert({:a => 2})
end
end
threaded_exec do
assert_raise Mongo::OperationFailure do
- @db2['stuff'].insert({:a => 2}, :safe => true)
+ @db2['stuff'].insert({:a => 2})
end
end
@@ -57,11 +57,11 @@ def test_authenticate
@db2.authenticate('user2', 'secret')
threaded_exec do
- assert @db1['stuff'].insert({:a => 2}, :safe => true)
+ assert @db1['stuff'].insert({:a => 2})
end
threaded_exec do
- assert @db2['stuff'].insert({:a => 2}, :safe => true)
+ assert @db2['stuff'].insert({:a => 2})
end
puts "Please bounce the server."
@@ -74,24 +74,24 @@ def test_authenticate
end
threaded_exec do
- assert @db1['stuff'].insert({:a => 2}, :safe => true)
+ assert @db1['stuff'].insert({:a => 2})
end
threaded_exec do
- assert @db2['stuff'].insert({:a => 2}, :safe => true)
+ assert @db2['stuff'].insert({:a => 2})
end
@db1.logout
threaded_exec do
assert_raise Mongo::OperationFailure do
- @db1['stuff'].insert({:a => 2}, :safe => true)
+ @db1['stuff'].insert({:a => 2})
end
end
@db2.logout
threaded_exec do
assert_raise Mongo::OperationFailure do
- assert @db2['stuff'].insert({:a => 2}, :safe => true)
+ assert @db2['stuff'].insert({:a => 2})
end
end
end
View
70 test/functional/collection_test.rb
@@ -92,23 +92,23 @@ def test_rename_collection
end
def test_nil_id
- assert_equal 5, @@test.insert({"_id" => 5, "foo" => "bar"}, {:safe => true})
- assert_equal 5, @@test.save({"_id" => 5, "foo" => "baz"}, {:safe => true})
+ assert_equal 5, @@test.insert({"_id" => 5, "foo" => "bar"})
+ assert_equal 5, @@test.save({"_id" => 5, "foo" => "baz"})
assert_equal nil, @@test.find_one("foo" => "bar")
assert_equal "baz", @@test.find_one(:_id => 5)["foo"]
assert_raise OperationFailure do
- @@test.insert({"_id" => 5, "foo" => "bar"}, {:safe => true})
+ @@test.insert({"_id" => 5, "foo" => "bar"})
end
- assert_equal nil, @@test.insert({"_id" => nil, "foo" => "bar"}, {:safe => true})
- assert_equal nil, @@test.save({"_id" => nil, "foo" => "baz"}, {:safe => true})
+ assert_equal nil, @@test.insert({"_id" => nil, "foo" => "bar"})
+ assert_equal nil, @@test.save({"_id" => nil, "foo" => "baz"})
assert_equal nil, @@test.find_one("foo" => "bar")
assert_equal "baz", @@test.find_one(:_id => nil)["foo"]
assert_raise OperationFailure do
- @@test.insert({"_id" => nil, "foo" => "bar"}, {:safe => true})
+ @@test.insert({"_id" => nil, "foo" => "bar"})
end
assert_raise OperationFailure do
- @@test.insert({:_id => nil, "foo" => "bar"}, {:safe => true})
+ @@test.insert({:_id => nil, "foo" => "bar"})
end
end
@@ -146,11 +146,11 @@ def test_safe_insert
@@test.create_index("hello", :unique => true)
a = {"hello" => "world"}
@@test.insert(a)
- @@test.insert(a)
+ @@test.insert(a, :w => 0)
assert(@@db.get_last_error['err'].include?("11000"))
assert_raise OperationFailure do
- @@test.insert(a, :safe => true)
+ @@test.insert(a)
end
end
@@ -174,7 +174,7 @@ def test_bulk_insert_with_continue_on_error
docs << {:foo => 2}
docs << {:foo => 3}
assert_raise OperationFailure do
- @@test.insert(docs, :safe => true)
+ @@test.insert(docs)
end
assert_equal 1, @@test.count
@@test.remove
@@ -185,7 +185,7 @@ def test_bulk_insert_with_continue_on_error
docs << {:foo => 2}
docs << {:foo => 3}
assert_raise OperationFailure do
- @@test.insert(docs, :safe => true, :continue_on_error => true)
+ @@test.insert(docs, :continue_on_error => true)
end
assert_equal 3, @@test.count
@@ -255,26 +255,26 @@ def test_maximum_insert_size
end
end
- if @@version >= "1.5.1"
- def test_safe_mode_with_advanced_safe_with_invalid_options
- assert_raise_error ArgumentError, "Unknown key(s): wtime" do
- @@test.insert({:foo => 1}, :safe => {:w => 2, :wtime => 1, :fsync => true})
- end
- assert_raise_error ArgumentError, "Unknown key(s): wtime" do
- @@test.update({:foo => 1}, {:foo => 2}, :safe => {:w => 2, :wtime => 1, :fsync => true})
- end
-
- assert_raise_error ArgumentError, "Unknown key(s): wtime" do
- @@test.remove({:foo => 2}, :safe => {:w => 2, :wtime => 1, :fsync => true})
- end
- end
- end
+ #if @@version >= "1.5.1"
+ # def test_safe_mode_with_advanced_safe_with_invalid_options
+ # assert_raise_error ArgumentError, "Unknown key(s): wtime" do
+ # @@test.insert({:foo => 1}, :w => 2, :wtime => 1, :fsync => true)
+ # end
+ # assert_raise_error ArgumentError, "Unknown key(s): wtime" do
+ # @@test.update({:foo => 1}, {:foo => 2}, :w => 2, :wtime => 1, :fsync => true)
+ # end
+ #
+ # assert_raise_error ArgumentError, "Unknown key(s): wtime" do
+ # @@test.remove({:foo => 2}, :w => 2, :wtime => 1, :fsync => true)
+ # end
+ # end
+ #end
if @@version >= "2.0.0"
def test_safe_mode_with_journal_commit_option
- @@test.insert({:foo => 1}, :safe => {:j => true})
- @@test.update({:foo => 1}, {:foo => 2}, :safe => {:j => true})
- @@test.remove({:foo => 2}, :safe => {:j => true})
+ @@test.insert({:foo => 1}, :j => true)
+ @@test.update({:foo => 1}, {:foo => 2}, :j => true)
+ @@test.remove({:foo => 2}, :j => true)
end
end
@@ -332,7 +332,7 @@ def test_safe_update
# Can't change an index.
assert_raise OperationFailure do
- @@test.update({}, {"$inc" => {"x" => 1}}, :safe => true)
+ @@test.update({}, {"$inc" => {"x" => 1}})
end
@@test.drop
end
@@ -348,7 +348,7 @@ def test_safe_update
# Can't duplicate an index.
assert_raise OperationFailure do
- @@test.update({}, {"x" => 10}, :safe => true)
+ @@test.update({}, {"x" => 10})
end
@@test.drop
end
@@ -358,10 +358,10 @@ def test_safe_save
@@test.create_index("hello", :unique => true)
@@test.save("hello" => "world")
- @@test.save("hello" => "world")
+ @@test.save({"hello" => "world"}, :w => 0)
assert_raise OperationFailure do
- @@test.save({"hello" => "world"}, :safe => true)
+ @@test.save({"hello" => "world"})
end
@@test.drop
end
@@ -374,7 +374,7 @@ def test_mocked_safe_remove
@client.stubs(:receive).returns([[{'ok' => 0, 'err' => 'failed'}], 1, 0])
assert_raise OperationFailure do
- @test.remove({}, :safe => true)
+ @test.remove({})
end
@test.drop
end
@@ -385,12 +385,12 @@ def test_safe_remove
@test = @db['test-safe-remove']
@test.remove
@test.save({:a => 50})
- assert_equal 1, @test.remove({}, :safe => true)["n"]
+ assert_equal 1, @test.remove({})["n"]
@test.drop
end
def test_remove_return_value
- assert_equal true, @@test.remove({})
+ assert_equal true, @@test.remove({}, :w => 0)
end
def test_count
View
2  test/functional/connection_test.rb
@@ -361,7 +361,7 @@ def test_connection_activity
end
end
- should "close the connection on send_message_with_safe_check for major exceptions" do
+ should "close the connection on send_message_with_acknowledge for major exceptions" do
@con.expects(:checkout_writer).raises(SystemStackError)
@con.expects(:close)
begin
View
30 test/functional/cursor_fail_test.rb
@@ -11,37 +11,11 @@ class CursorFailTest < Test::Unit::TestCase
@@version = @@connection.server_version
def setup
- @@coll.remove({}, :safe => true)
- @@coll.insert({'a' => 1}, :safe => true) # collection not created until it's used
+ @@coll.remove({})
+ @@coll.insert({'a' => 1}) # collection not created until it's used
@@coll_full_name = "#{MONGO_TEST_DB}.test"
end
- #def test_refill_via_get_more
- # assert_equal 1, @@coll.count
- # 1000.times { |i|
- # assert_equal 1 + i, @@coll.count
- # @@coll.insert({'a' => i}, :safe => true)
- # }
- #
- # assert_equal 1001, @@coll.count
- # count = 0
- # @@coll.find.each { |obj|
- # count += obj['a']
- # }
- # assert_equal 1001, @@coll.count
-
- # # do the same thing again for debugging
- # assert_equal 1001, @@coll.count
- # count2 = 0
- # @@coll.find.each { |obj|
- # count2 += obj['a']
- # }
- # assert_equal 1001, @@coll.count
- #
- # assert_equal count, count2
- # assert_equal 499501, count
- #end
-
def test_refill_via_get_more_alt_coll
coll = @@db.collection('test-alt-coll')
coll.remove
View
12 test/functional/cursor_test.rb
@@ -195,15 +195,15 @@ def test_id_range_queries
t1 = Time.now
t1_id = ObjectId.from_time(t1)
- @@coll.save({:t => 't1'}, {:safe => true})
- @@coll.save({:t => 't1'}, {:safe => true})
- @@coll.save({:t => 't1'}, {:safe => true})
+ @@coll.save({:t => 't1'})
+ @@coll.save({:t => 't1'})
+ @@coll.save({:t => 't1'})
sleep(2)
t2 = Time.now
t2_id = ObjectId.from_time(t2)
- @@coll.save({:t => 't2'}, {:safe => true})
- @@coll.save({:t => 't2'}, {:safe => true})
- @@coll.save({:t => 't2'}, {:safe => true})
+ @@coll.save({:t => 't2'})
+ @@coll.save({:t => 't2'})
+ @@coll.save({:t => 't2'})
assert_equal 3, @@coll.find({'_id' => {'$gt' => t1_id, '$lt' => t2_id}}).count
@@coll.find({'_id' => {'$gt' => t2_id}}).each do |doc|
View
9 test/functional/db_api_test.rb
@@ -329,8 +329,7 @@ def test_collection_options_are_passed_to_the_existing_ones
@@db.create_collection('foobar')
- opts = {:safe => true}
- coll = @@db.create_collection('foobar', opts)
+ coll = @@db.create_collection('foobar')
assert_equal true, coll.safe
end
@@ -429,9 +428,9 @@ def test_index_on_subfield
end
def test_array
- @@coll.remove({'$atomic' => true}, :safe => true)
- @@coll.insert({'b' => [1, 2, 3]}, :safe => true)
- @@coll.insert({'b' => [1, 2, 3]}, :safe => true)
+ @@coll.remove({'$atomic' => true})
+ @@coll.insert({'b' => [1, 2, 3]})
+ @@coll.insert({'b' => [1, 2, 3]})
rows = @@coll.find({}, {:fields => ['b']}).to_a
assert_equal 2, rows.length
assert_equal [1, 2, 3], rows[1]['b']
View
2  test/functional/grid_file_system_test.rb
@@ -16,7 +16,7 @@ class GridFileSystemTest < Test::Unit::TestCase
setup do
@chunks_data = "CHUNKS" * 50000
@grid = GridFileSystem.new(@db)
- @opts = {:safe => true}
+ @opts = {:w => 1}
@original_opts = @opts.dup
@grid.open('sample.file', 'w', @opts) do |f|
f.write @chunks_data
View
6 test/functional/grid_io_test.rb
@@ -186,7 +186,7 @@ class GridIOTest < Test::Unit::TestCase
context "Grid MD5 check" do
should "run in safe mode" do
- file = GridIO.new(@files, @chunks, 'smallfile', 'w', :safe => true)
+ file = GridIO.new(@files, @chunks, 'smallfile', 'w')
file.write("DATA" * 100)
assert file.close
assert_equal file.server_md5, file.client_md5
@@ -194,7 +194,7 @@ class GridIOTest < Test::Unit::TestCase
should "validate with a large file" do
io = File.open(File.join(TEST_DATA, 'sample_file.pdf'), 'r')
- file = GridIO.new(@files, @chunks, 'bigfile', 'w', :safe => true)
+ file = GridIO.new(@files, @chunks, 'bigfile', 'w')
file.write(io)
assert file.close
assert_equal file.server_md5, file.client_md5
@@ -203,7 +203,7 @@ class GridIOTest < Test::Unit::TestCase
should "raise an exception when check fails" do
io = File.open(File.join(TEST_DATA, 'sample_file.pdf'), 'r')
@db.stubs(:command).returns({