mongoid 6.3.0 → 6.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/config/locales/en.yml +21 -0
- data/lib/mongoid.rb +1 -1
- data/lib/mongoid/clients.rb +2 -0
- data/lib/mongoid/clients/sessions.rb +113 -0
- data/lib/mongoid/clients/storage_options.rb +1 -0
- data/lib/mongoid/contextual/aggregable/mongo.rb +1 -1
- data/lib/mongoid/contextual/map_reduce.rb +6 -2
- data/lib/mongoid/contextual/memory.rb +7 -2
- data/lib/mongoid/contextual/mongo.rb +11 -2
- data/lib/mongoid/criteria.rb +1 -0
- data/lib/mongoid/criteria/queryable/mergeable.rb +3 -1
- data/lib/mongoid/errors.rb +1 -0
- data/lib/mongoid/errors/invalid_session_use.rb +24 -0
- data/lib/mongoid/indexable.rb +4 -4
- data/lib/mongoid/persistable.rb +1 -1
- data/lib/mongoid/persistable/creatable.rb +4 -2
- data/lib/mongoid/persistable/deletable.rb +4 -2
- data/lib/mongoid/persistable/destroyable.rb +1 -5
- data/lib/mongoid/persistable/updatable.rb +2 -2
- data/lib/mongoid/persistable/upsertable.rb +2 -1
- data/lib/mongoid/relations/embedded/batchable.rb +10 -4
- data/lib/mongoid/relations/many.rb +4 -0
- data/lib/mongoid/relations/referenced/many.rb +1 -1
- data/lib/mongoid/relations/touchable.rb +1 -1
- data/lib/mongoid/reloadable.rb +1 -1
- data/lib/mongoid/tasks/database.rb +3 -2
- data/lib/mongoid/threaded.rb +38 -0
- data/lib/mongoid/version.rb +1 -1
- data/spec/mongoid/attributes/nested_spec.rb +4 -0
- data/spec/mongoid/clients/sessions_spec.rb +325 -0
- data/spec/mongoid/contextual/mongo_spec.rb +38 -0
- data/spec/mongoid/criteria/queryable/selectable_spec.rb +32 -3
- data/spec/mongoid/interceptable_spec.rb +1 -1
- data/spec/mongoid/persistable/deletable_spec.rb +19 -0
- data/spec/mongoid/persistable/destroyable_spec.rb +19 -0
- data/spec/mongoid/persistable_spec.rb +16 -16
- data/spec/spec_helper.rb +70 -0
- metadata +17 -7
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 496715e8829978d85bd903477e9d6d7961fa4ffe8d9529d6720fbfdb2b3ec9a8
|
4
|
+
data.tar.gz: 907296ce5256555834c738f5fdf8d7277b57d6c09da3c733eb7cb0ba6526d0a2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d16c8cff475066981b0736ed07ebe0b463c8ee4e8c43049087f43ad8ee6789ff88a5c2e73e3fe6a31a52f7732cc6b8614de72d8c76df2dd3c54ee48a5977a41
|
7
|
+
data.tar.gz: d15deebe3406b501b95fa864306c03cfa7b106f810909dffeda8d7402d14de5aa1dad3d1129685b859ef9d537e656108a015b77f066ea5affd9e32785e018b0a
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/lib/config/locales/en.yml
CHANGED
@@ -206,6 +206,20 @@ en:
|
|
206
206
|
\_\_\_\_include Mongoid::Document\n
|
207
207
|
\_\_\_\_scope :inactive, ->{ where(active: false) }\n
|
208
208
|
\_\_end\n\n"
|
209
|
+
invalid_session_use:
|
210
|
+
message: "A session was attempted to be used with a model whose client cannot use
|
211
|
+
that session."
|
212
|
+
summary: "Sessions are started via driver clients (Model#mongo_client) and, in most cases, driver
|
213
|
+
clients are shared across models. When different models have their own clients, a session cannot
|
214
|
+
be obtained via one model and used for operations on another model."
|
215
|
+
resolution: "Only execute operations on the model class or instances of the model through which
|
216
|
+
the session was created. Otherwise, ensure that all models on which operations are executed
|
217
|
+
in the session block share the same driver client. For example, a model may have a different
|
218
|
+
client specified in its 'store_in' options.\n\n"
|
219
|
+
invalid_session_nesting:
|
220
|
+
message: "A session was started while another session was being used."
|
221
|
+
summary: "Sessions cannot be nested. Only one session can be used in a thread at once."
|
222
|
+
resolution: "Only use one session at a time; sessions cannot be nested."
|
209
223
|
invalid_storage_options:
|
210
224
|
message: "Invalid options passed to %{klass}.store_in: %{options}."
|
211
225
|
summary: "The :store_in macro takes only a hash of parameters with
|
@@ -452,6 +466,13 @@ en:
|
|
452
466
|
with the already defined method %{model_name}, or set the
|
453
467
|
configuration option Mongoid.scope_overwrite_exception to false,
|
454
468
|
which is its default. In this case a warning will be logged."
|
469
|
+
sessions_not_supported:
|
470
|
+
message: "Sessions are not supported by the connected server(s)."
|
471
|
+
summary: "A session was attempted to be used with a MongoDB server version
|
472
|
+
that doesn't support sessions. Sessions are supported in MongoDB
|
473
|
+
server versions 3.6 and higher."
|
474
|
+
resolution: "Verify that all servers in your deployment are at least
|
475
|
+
version 3.6 or don't attempt to use sessions with older server versions."
|
455
476
|
taken:
|
456
477
|
"is already taken"
|
457
478
|
too_many_nested_attribute_records:
|
data/lib/mongoid.rb
CHANGED
data/lib/mongoid/clients.rb
CHANGED
@@ -3,12 +3,14 @@ require "mongoid/clients/factory"
|
|
3
3
|
require "mongoid/clients/validators"
|
4
4
|
require "mongoid/clients/storage_options"
|
5
5
|
require "mongoid/clients/options"
|
6
|
+
require "mongoid/clients/sessions"
|
6
7
|
|
7
8
|
module Mongoid
|
8
9
|
module Clients
|
9
10
|
extend ActiveSupport::Concern
|
10
11
|
include StorageOptions
|
11
12
|
include Options
|
13
|
+
include Sessions
|
12
14
|
|
13
15
|
class << self
|
14
16
|
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Clients
|
3
|
+
|
4
|
+
# Encapsulates behavior for getting a session from the client of a model class or instance,
|
5
|
+
# setting the session on the current thread, and yielding to a block.
|
6
|
+
# The session will be closed after the block completes or raises an error.
|
7
|
+
#
|
8
|
+
# @since 6.4.0
|
9
|
+
module Sessions
|
10
|
+
|
11
|
+
# Execute a block within the context of a session.
|
12
|
+
#
|
13
|
+
# @example Execute some operations in the context of a session.
|
14
|
+
# band.with_session(causal_consistency: true) do
|
15
|
+
# band.records << Record.create
|
16
|
+
# band.name = 'FKA Twigs'
|
17
|
+
# band.save
|
18
|
+
# band.reload
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# @param [ Hash ] options The session options. Please see the driver
|
22
|
+
# documentation for the available session options.
|
23
|
+
#
|
24
|
+
# @note You cannot do any operations in the block using models or objects
|
25
|
+
# that use a different client; the block will execute all operations
|
26
|
+
# in the context of the implicit session and operations on any models using
|
27
|
+
# another client will fail. For example, if you set a client using store_in on a
|
28
|
+
# particular model and execute an operation on it in the session context block,
|
29
|
+
# that operation can't use the block's session and an error will be raised.
|
30
|
+
# An error will also be raised if sessions are nested.
|
31
|
+
#
|
32
|
+
# @raise [ Errors::InvalidSessionUse ] If an operation is attempted on a model using another
|
33
|
+
# client from which the session was started or if sessions are nested.
|
34
|
+
#
|
35
|
+
# @return [ Object ] The result of calling the block.
|
36
|
+
#
|
37
|
+
# @yieldparam [ Mongo::Session ] The session being used for the block.
|
38
|
+
#
|
39
|
+
# @since 6.4.0
|
40
|
+
def with_session(options = {})
|
41
|
+
raise Mongoid::Errors::InvalidSessionUse.new(:invalid_session_nesting) if Threaded.get_session
|
42
|
+
session = persistence_context.client.start_session(options)
|
43
|
+
Threaded.set_session(session)
|
44
|
+
yield(session)
|
45
|
+
rescue Mongo::Error::InvalidSession => ex
|
46
|
+
if ex.message == Mongo::Session::SESSIONS_NOT_SUPPORTED
|
47
|
+
raise Mongoid::Errors::InvalidSessionUse.new(:sessions_not_supported)
|
48
|
+
end
|
49
|
+
raise Mongoid::Errors::InvalidSessionUse.new(:invalid_session_use)
|
50
|
+
ensure
|
51
|
+
Threaded.clear_session
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def session
|
57
|
+
Threaded.get_session
|
58
|
+
end
|
59
|
+
|
60
|
+
module ClassMethods
|
61
|
+
|
62
|
+
# Execute a block within the context of a session.
|
63
|
+
#
|
64
|
+
# @example Execute some operations in the context of a session.
|
65
|
+
# Band.with_session(causal_consistency: true) do
|
66
|
+
# band = Band.create
|
67
|
+
# band.records << Record.new
|
68
|
+
# band.save
|
69
|
+
# band.reload.records
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# @param [ Hash ] options The session options. Please see the driver
|
73
|
+
# documentation for the available session options.
|
74
|
+
#
|
75
|
+
# @note You cannot do any operations in the block using models or objects
|
76
|
+
# that use a different client; the block will execute all operations
|
77
|
+
# in the context of the implicit session and operations on any models using
|
78
|
+
# another client will fail. For example, if you set a client using store_in on a
|
79
|
+
# particular model and execute an operation on it in the session context block,
|
80
|
+
# that operation can't use the block's session and an error will be raised.
|
81
|
+
# You also cannot nest sessions.
|
82
|
+
#
|
83
|
+
# @raise [ Errors::InvalidSessionUse ] If an operation is attempted on a model using another
|
84
|
+
# client from which the session was started or if sessions are nested.
|
85
|
+
#
|
86
|
+
# @return [ Object ] The result of calling the block.
|
87
|
+
#
|
88
|
+
# @yieldparam [ Mongo::Session ] The session being used for the block.
|
89
|
+
#
|
90
|
+
# @since 6.4.0
|
91
|
+
def with_session(options = {})
|
92
|
+
raise Mongoid::Errors::InvalidSessionUse.new(:invalid_session_nesting) if Threaded.get_session
|
93
|
+
session = persistence_context.client.start_session(options)
|
94
|
+
Threaded.set_session(session)
|
95
|
+
yield(session)
|
96
|
+
rescue Mongo::Error::InvalidSession => ex
|
97
|
+
if ex.message == Mongo::Session::SESSIONS_NOT_SUPPORTED
|
98
|
+
raise Mongoid::Errors::InvalidSessionUse.new(:sessions_not_supported)
|
99
|
+
end
|
100
|
+
raise Mongoid::Errors::InvalidSessionUse.new(:invalid_session_use)
|
101
|
+
ensure
|
102
|
+
Threaded.clear_session
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def session
|
108
|
+
Threaded.get_session
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -23,7 +23,7 @@ module Mongoid
|
|
23
23
|
#
|
24
24
|
# @since 3.0.0
|
25
25
|
def aggregates(field)
|
26
|
-
result = collection.find.aggregate(pipeline(field)).to_a
|
26
|
+
result = collection.find.aggregate(pipeline(field), session: session).to_a
|
27
27
|
if result.empty?
|
28
28
|
{ "count" => 0, "sum" => nil, "avg" => nil, "min" => nil, "max" => nil }
|
29
29
|
else
|
@@ -166,12 +166,12 @@ module Mongoid
|
|
166
166
|
validate_out!
|
167
167
|
cmd = command
|
168
168
|
opts = { read: cmd.delete(:read).options } if cmd[:read]
|
169
|
-
@map_reduce.database.command(cmd, opts || {}).first
|
169
|
+
@map_reduce.database.command(cmd, (opts || {}).merge(session: session)).first
|
170
170
|
end
|
171
171
|
alias :results :raw
|
172
172
|
|
173
173
|
# Execute the map/reduce, returning the raw output.
|
174
|
-
# Useful when you don't care about map/reduce's
|
174
|
+
# Useful when you don't care about map/reduce's output.
|
175
175
|
#
|
176
176
|
# @example Run the map reduce
|
177
177
|
# map_reduce.execute
|
@@ -249,6 +249,10 @@ module Mongoid
|
|
249
249
|
def validate_out!
|
250
250
|
raise Errors::NoMapReduceOutput.new({}) unless @map_reduce.out
|
251
251
|
end
|
252
|
+
|
253
|
+
def session
|
254
|
+
criteria.send(:session)
|
255
|
+
end
|
252
256
|
end
|
253
257
|
end
|
254
258
|
end
|
@@ -48,7 +48,8 @@ module Mongoid
|
|
48
48
|
end
|
49
49
|
unless removed.empty?
|
50
50
|
collection.find(selector).update_one(
|
51
|
-
positionally(selector, "$pullAll" => { path => removed })
|
51
|
+
positionally(selector, "$pullAll" => { path => removed }),
|
52
|
+
session: session
|
52
53
|
)
|
53
54
|
end
|
54
55
|
deleted
|
@@ -303,7 +304,7 @@ module Mongoid
|
|
303
304
|
updates["$set"].merge!(doc.atomic_updates["$set"] || {})
|
304
305
|
doc.move_changes
|
305
306
|
end
|
306
|
-
collection.find(selector).update_one(updates) unless updates["$set"].empty?
|
307
|
+
collection.find(selector).update_one(updates, session: session) unless updates["$set"].empty?
|
307
308
|
end
|
308
309
|
|
309
310
|
# Get the limiting value.
|
@@ -444,6 +445,10 @@ module Mongoid
|
|
444
445
|
doc._parent.remove_child(doc)
|
445
446
|
doc.destroyed = true
|
446
447
|
end
|
448
|
+
|
449
|
+
def session
|
450
|
+
@criteria.send(:session)
|
451
|
+
end
|
447
452
|
end
|
448
453
|
end
|
449
454
|
end
|
@@ -95,7 +95,8 @@ module Mongoid
|
|
95
95
|
def destroy
|
96
96
|
each.inject(0) do |count, doc|
|
97
97
|
doc.destroy
|
98
|
-
count += 1
|
98
|
+
count += 1 if acknowledged_write?
|
99
|
+
count
|
99
100
|
end
|
100
101
|
end
|
101
102
|
alias :destroy_all :destroy
|
@@ -340,7 +341,7 @@ module Mongoid
|
|
340
341
|
@criteria, @klass, @cache = criteria, criteria.klass, criteria.options[:cache]
|
341
342
|
@collection = @klass.collection
|
342
343
|
criteria.send(:merge_type_selection)
|
343
|
-
@view = collection.find(criteria.selector)
|
344
|
+
@view = collection.find(criteria.selector, session: session)
|
344
345
|
apply_options
|
345
346
|
end
|
346
347
|
|
@@ -704,6 +705,14 @@ module Mongoid
|
|
704
705
|
yield(doc)
|
705
706
|
documents.push(doc) if cacheable?
|
706
707
|
end
|
708
|
+
|
709
|
+
def session
|
710
|
+
@criteria.send(:session)
|
711
|
+
end
|
712
|
+
|
713
|
+
def acknowledged_write?
|
714
|
+
collection.write_concern.nil? || collection.write_concern.acknowledged?
|
715
|
+
end
|
707
716
|
end
|
708
717
|
end
|
709
718
|
end
|
data/lib/mongoid/criteria.rb
CHANGED
@@ -259,7 +259,9 @@ module Mongoid
|
|
259
259
|
def prepare(field, operator, value)
|
260
260
|
unless operator =~ /exists|type|size/
|
261
261
|
value = value.__expand_complex__
|
262
|
-
|
262
|
+
field = field.to_s
|
263
|
+
name = aliases[field] || field
|
264
|
+
serializer = serializers[name]
|
263
265
|
value = serializer ? serializer.evolve(value) : value
|
264
266
|
end
|
265
267
|
selection = { operator => value }
|
data/lib/mongoid/errors.rb
CHANGED
@@ -18,6 +18,7 @@ require "mongoid/errors/invalid_path"
|
|
18
18
|
require "mongoid/errors/invalid_persistence_option"
|
19
19
|
require "mongoid/errors/invalid_relation"
|
20
20
|
require "mongoid/errors/invalid_scope"
|
21
|
+
require "mongoid/errors/invalid_session_use"
|
21
22
|
require "mongoid/errors/invalid_set_polymorphic_relation"
|
22
23
|
require "mongoid/errors/invalid_storage_options"
|
23
24
|
require "mongoid/errors/invalid_storage_parent"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid
|
3
|
+
module Errors
|
4
|
+
|
5
|
+
# This error is raised when a session is attempted to be used with a model whose client cannot use it, if
|
6
|
+
# sessions are nested, or if the mongodb deployment doesn't support sessions.
|
7
|
+
#
|
8
|
+
# @since 6.4.0
|
9
|
+
class InvalidSessionUse < MongoidError
|
10
|
+
|
11
|
+
# Create the error.
|
12
|
+
#
|
13
|
+
# @example Create the error.
|
14
|
+
# InvalidSessionUse.new(:invalid_session_use)
|
15
|
+
#
|
16
|
+
# @param [ :invalid_sesion_use, :invalid_session_nesting ] error_type The type of session misuse.
|
17
|
+
#
|
18
|
+
# @since 6.4.0
|
19
|
+
def initialize(error_type)
|
20
|
+
super(compose_message(error_type.to_s))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/mongoid/indexable.rb
CHANGED
@@ -32,10 +32,10 @@ module Mongoid
|
|
32
32
|
key, options = spec.key, spec.options
|
33
33
|
if database = options[:database]
|
34
34
|
with(database: database) do |klass|
|
35
|
-
klass.collection.indexes.create_one(key, options.except(:database))
|
35
|
+
klass.collection.indexes(session: session).create_one(key, options.except(:database))
|
36
36
|
end
|
37
37
|
else
|
38
|
-
collection.indexes.create_one(key, options)
|
38
|
+
collection.indexes(session: session).create_one(key, options)
|
39
39
|
end
|
40
40
|
end and true
|
41
41
|
end
|
@@ -53,9 +53,9 @@ module Mongoid
|
|
53
53
|
indexed_database_names.each do |database|
|
54
54
|
with(database: database) do |klass|
|
55
55
|
begin
|
56
|
-
klass.collection.indexes.each do |spec|
|
56
|
+
klass.collection.indexes(session: session).each do |spec|
|
57
57
|
unless spec["name"] == "_id_"
|
58
|
-
klass.collection.indexes.drop_one(spec["key"])
|
58
|
+
klass.collection.indexes(session: session).drop_one(spec["key"])
|
59
59
|
logger.info(
|
60
60
|
"MONGOID: Removed index '#{spec["name"]}' on collection " +
|
61
61
|
"'#{klass.collection.name}' in database '#{database}'."
|
data/lib/mongoid/persistable.rb
CHANGED
@@ -204,7 +204,7 @@ module Mongoid
|
|
204
204
|
def persist_atomic_operations(operations)
|
205
205
|
if persisted? && operations
|
206
206
|
selector = atomic_selector
|
207
|
-
_root.collection.find(selector).update_one(positionally(selector, operations))
|
207
|
+
_root.collection.find(selector).update_one(positionally(selector, operations), session: session)
|
208
208
|
end
|
209
209
|
end
|
210
210
|
end
|
@@ -61,7 +61,9 @@ module Mongoid
|
|
61
61
|
_parent.insert
|
62
62
|
else
|
63
63
|
selector = _parent.atomic_selector
|
64
|
-
_root.collection.find(selector).update_one(
|
64
|
+
_root.collection.find(selector).update_one(
|
65
|
+
positionally(selector, atomic_inserts),
|
66
|
+
session: session)
|
65
67
|
end
|
66
68
|
end
|
67
69
|
|
@@ -76,7 +78,7 @@ module Mongoid
|
|
76
78
|
#
|
77
79
|
# @since 4.0.0
|
78
80
|
def insert_as_root
|
79
|
-
collection.insert_one(as_attributes)
|
81
|
+
collection.insert_one(as_attributes, session: session)
|
80
82
|
end
|
81
83
|
|
82
84
|
# Post process an insert, which sets the new record attribute to false
|
@@ -62,7 +62,9 @@ module Mongoid
|
|
62
62
|
_parent.remove_child(self) if notifying_parent?(options)
|
63
63
|
if _parent.persisted?
|
64
64
|
selector = _parent.atomic_selector
|
65
|
-
_root.collection.find(selector).update_one(
|
65
|
+
_root.collection.find(selector).update_one(
|
66
|
+
positionally(selector, atomic_deletes),
|
67
|
+
session: session)
|
66
68
|
end
|
67
69
|
true
|
68
70
|
end
|
@@ -78,7 +80,7 @@ module Mongoid
|
|
78
80
|
#
|
79
81
|
# @since 4.0.0
|
80
82
|
def delete_as_root
|
81
|
-
collection.find(atomic_selector).delete_one
|
83
|
+
collection.find(atomic_selector).delete_one(session: session)
|
82
84
|
true
|
83
85
|
end
|
84
86
|
|
@@ -48,11 +48,7 @@ module Mongoid
|
|
48
48
|
#
|
49
49
|
# @since 1.0.0
|
50
50
|
def destroy_all(conditions = nil)
|
51
|
-
|
52
|
-
documents = where(selector)
|
53
|
-
destroyed = documents.count
|
54
|
-
documents.each { |doc| doc.destroy }
|
55
|
-
destroyed
|
51
|
+
where(conditions || {}).destroy
|
56
52
|
end
|
57
53
|
end
|
58
54
|
end
|
@@ -134,9 +134,9 @@ module Mongoid
|
|
134
134
|
unless updates.empty?
|
135
135
|
coll = collection(_root)
|
136
136
|
selector = atomic_selector
|
137
|
-
coll.find(selector).update_one(positionally(selector, updates))
|
137
|
+
coll.find(selector).update_one(positionally(selector, updates), session: session)
|
138
138
|
conflicts.each_pair do |key, value|
|
139
|
-
coll.find(selector).update_one(positionally(selector, { key => value }))
|
139
|
+
coll.find(selector).update_one(positionally(selector, { key => value }), session: session)
|
140
140
|
end
|
141
141
|
end
|
142
142
|
end
|