mongoid 6.3.0 → 6.4.0
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.
- 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
|