inventory_refresh 0.1.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +1 -0
  3. data/.gitignore +6 -0
  4. data/.travis.yml +3 -3
  5. data/Gemfile +4 -0
  6. data/inventory_refresh.gemspec +7 -5
  7. data/lib/inventory_refresh.rb +1 -0
  8. data/lib/inventory_refresh/inventory_collection.rb +115 -646
  9. data/lib/inventory_refresh/inventory_collection/builder.rb +249 -0
  10. data/lib/inventory_refresh/inventory_collection/graph.rb +0 -15
  11. data/lib/inventory_refresh/inventory_collection/helpers.rb +6 -0
  12. data/lib/inventory_refresh/inventory_collection/helpers/associations_helper.rb +80 -0
  13. data/lib/inventory_refresh/inventory_collection/helpers/initialize_helper.rb +456 -0
  14. data/lib/inventory_refresh/inventory_collection/helpers/questions_helper.rb +132 -0
  15. data/lib/inventory_refresh/inventory_collection/index/proxy.rb +1 -1
  16. data/lib/inventory_refresh/inventory_collection/index/type/skeletal.rb +5 -5
  17. data/lib/inventory_refresh/inventory_collection/reference.rb +4 -0
  18. data/lib/inventory_refresh/inventory_collection/scanner.rb +111 -18
  19. data/lib/inventory_refresh/inventory_collection/serialization.rb +7 -7
  20. data/lib/inventory_refresh/inventory_collection/unconnected_edge.rb +19 -0
  21. data/lib/inventory_refresh/inventory_object.rb +17 -11
  22. data/lib/inventory_refresh/inventory_object_lazy.rb +20 -10
  23. data/lib/inventory_refresh/persister.rb +212 -0
  24. data/lib/inventory_refresh/save_collection/base.rb +18 -3
  25. data/lib/inventory_refresh/save_collection/saver/base.rb +27 -64
  26. data/lib/inventory_refresh/save_collection/saver/concurrent_safe_batch.rb +73 -225
  27. data/lib/inventory_refresh/save_collection/saver/partial_upsert_helper.rb +226 -0
  28. data/lib/inventory_refresh/save_collection/saver/retention_helper.rb +115 -0
  29. data/lib/inventory_refresh/save_collection/saver/sql_helper.rb +122 -0
  30. data/lib/inventory_refresh/save_collection/saver/sql_helper_update.rb +24 -5
  31. data/lib/inventory_refresh/save_collection/saver/sql_helper_upsert.rb +6 -6
  32. data/lib/inventory_refresh/save_collection/sweeper.rb +69 -0
  33. data/lib/inventory_refresh/save_inventory.rb +18 -8
  34. data/lib/inventory_refresh/target_collection.rb +12 -0
  35. data/lib/inventory_refresh/version.rb +1 -1
  36. metadata +61 -19
  37. data/lib/inventory_refresh/save_collection/recursive.rb +0 -52
  38. data/lib/inventory_refresh/save_collection/saver/concurrent_safe.rb +0 -71
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ade3208f2b15b5f3ddd1eb2ba062bc011ba4ce8fbd4bbc838ba9ccef62fb19f
4
- data.tar.gz: 6aecb2c6ea69ba72b48819eb8a4af6cbffa9f3c9d475bcca8c88e6e5f5fa0cf1
3
+ metadata.gz: f587f79d9daabcefff5a1b479301971cdd31f940d517cfc058480a0c17bc3abc
4
+ data.tar.gz: ac2962c044aba21b8bb5963ae06c788fdeb3863d29a34b33933360ffef432438
5
5
  SHA512:
6
- metadata.gz: e2d87b6841db8fd728269e4234ae05ca0c80a5612980ee08b83815d626b2945bca34644a972133eedb8f76bce19c627cc93a3d812a41c13edd8d74c9ff5d7175
7
- data.tar.gz: c811d7fad848a207228595ede6cdb251a7096192ae5984bf3be0af667956553035c22c45ece7b7962807339b9e45cf06ed4f823561a7c3ccd40fa9a631db1c50
6
+ metadata.gz: 9487530c2f5a42191a61c632fb85d30cbf6113ba6b5208bc2a0f3f2bee6c7634e8018d5896e9ba4bbe5db4bf12c1c5b41084f1eb1e36c66a89d6a5e7b4ef082e
7
+ data.tar.gz: 66381139ddee4c8f29bc0b0b1e08864f71b4805c6fc354c4dfb372641c5bb56ebb43cc4670b2958d6431a7a0195ef4a60c468a39ace586178b7cc760259f2369
@@ -34,6 +34,7 @@ engines:
34
34
  rubocop:
35
35
  enabled: true
36
36
  config: '.rubocop_cc.yml'
37
+ channel: rubocop-0-69
37
38
  prepare:
38
39
  fetch:
39
40
  - url: "https://raw.githubusercontent.com/ManageIQ/guides/master/.rubocop_base.yml"
data/.gitignore CHANGED
@@ -10,5 +10,11 @@
10
10
 
11
11
  /Gemfile.lock
12
12
 
13
+ # ignore included plugins in bundler.d
14
+ bundler.d/*
15
+
13
16
  # rspec failure tracking
14
17
  .rspec_status
18
+
19
+ # generated rubocop file
20
+ .rubocop-https*
@@ -3,15 +3,15 @@ sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
- - 2.4.4
7
- - 2.3.6
6
+ - 2.5.7
7
+ - 2.6.5
8
8
  addons:
9
9
  postgresql: '9.5'
10
10
  env:
11
11
  global:
12
12
  - CC_TEST_REPORTER_ID=1ef1a3a3d007395b11083d634a6fdac1e3d979b6428c39d2cd8d58556cdd68f7
13
13
  before_install:
14
- - gem install bundler -v 1.16.4
14
+ - gem install bundler -v 2.0.1
15
15
  - psql -c "CREATE USER root SUPERUSER PASSWORD 'smartvm';" -U postgres
16
16
  before_script:
17
17
  # Code Climate
data/Gemfile CHANGED
@@ -4,3 +4,7 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  # Specify your gem's dependencies in inventory_refresh.gemspec
6
6
  gemspec
7
+
8
+ # Load other additional Gemfiles
9
+ # Developers can create a file ending in .rb under bundler.d/ to specify additional development dependencies
10
+ Dir.glob(File.join(__dir__, 'bundler.d/*.rb')).each { |f| eval_gemfile(File.expand_path(f, __dir__)) }
@@ -22,14 +22,16 @@ Gem::Specification.new do |spec|
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ["lib"]
24
24
 
25
- spec.add_dependency "activerecord", "~> 5.0.6"
26
- spec.add_dependency "more_core_extensions", "~> 3.5"
27
- spec.add_dependency "pg", "~> 0.18.2"
25
+ spec.add_dependency "activerecord", "~> 5.0"
26
+ spec.add_dependency "more_core_extensions", ">=3.5", "< 5"
27
+ spec.add_dependency "pg", "> 0"
28
28
 
29
29
  spec.add_development_dependency "ancestry"
30
- spec.add_development_dependency "bundler", "~> 1.16"
31
- spec.add_development_dependency "factory_girl", "~> 4.5.0"
30
+ spec.add_development_dependency "bundler", "~> 2.0"
31
+ spec.add_development_dependency "factory_bot", "~> 4.11.1"
32
32
  spec.add_development_dependency "rake", "~> 10.0"
33
33
  spec.add_development_dependency "rspec", "~> 3.0"
34
+ spec.add_development_dependency "rubocop"
35
+ spec.add_development_dependency "rubocop-performance"
34
36
  spec.add_development_dependency "simplecov"
35
37
  end
@@ -4,6 +4,7 @@ require "inventory_refresh/inventory_object"
4
4
  require "inventory_refresh/inventory_object_lazy"
5
5
  require "inventory_refresh/logging"
6
6
  require "inventory_refresh/null_logger"
7
+ require "inventory_refresh/persister"
7
8
  require "inventory_refresh/save_inventory"
8
9
  require "inventory_refresh/target"
9
10
  require "inventory_refresh/target_collection"
@@ -1,9 +1,14 @@
1
+ require "inventory_refresh/inventory_collection/builder"
1
2
  require "inventory_refresh/inventory_collection/data_storage"
2
3
  require "inventory_refresh/inventory_collection/index/proxy"
3
4
  require "inventory_refresh/inventory_collection/reference"
4
5
  require "inventory_refresh/inventory_collection/references_storage"
5
6
  require "inventory_refresh/inventory_collection/scanner"
6
7
  require "inventory_refresh/inventory_collection/serialization"
8
+ require "inventory_refresh/inventory_collection/unconnected_edge"
9
+ require "inventory_refresh/inventory_collection/helpers/initialize_helper"
10
+ require "inventory_refresh/inventory_collection/helpers/associations_helper"
11
+ require "inventory_refresh/inventory_collection/helpers/questions_helper"
7
12
 
8
13
  require "active_support/core_ext/module/delegation"
9
14
 
@@ -74,10 +79,16 @@ module InventoryRefresh
74
79
  # @return [Array, nil] nil or a list of all :manager_uuids that are present in the Provider's InventoryCollection.
75
80
  attr_accessor :all_manager_uuids
76
81
 
82
+ # @return [Array, nil] Scope for applying :all_manager_uuids
83
+ attr_accessor :all_manager_uuids_scope
84
+
85
+ # @return [String] Timestamp in UTC before fetching :all_manager_uuids
86
+ attr_accessor :all_manager_uuids_timestamp
87
+
77
88
  # @return [Set] A set of InventoryCollection objects that depends on this InventoryCollection object.
78
89
  attr_accessor :dependees
79
90
 
80
- # @return [Array<Symbol>] @see #parent_inventory_collections documentation of InventoryCollection.new kwargs
91
+ # @return [Array<Symbol>] @see #parent_inventory_collections documentation of InventoryCollection.new's initialize_ic_relations()
81
92
  # parameters
82
93
  attr_accessor :parent_inventory_collections
83
94
 
@@ -86,8 +97,9 @@ module InventoryRefresh
86
97
  :association, :complete, :update_only, :transitive_dependency_attributes, :check_changed, :arel,
87
98
  :inventory_object_attributes, :name, :saver_strategy, :targeted_scope, :default_values,
88
99
  :targeted_arel, :targeted, :manager_ref_allowed_nil, :use_ar_object,
89
- :created_records, :updated_records, :deleted_records,
90
- :custom_reconnect_block, :batch_extra_attributes, :references_storage
100
+ :created_records, :updated_records, :deleted_records, :retention_strategy,
101
+ :custom_reconnect_block, :batch_extra_attributes, :references_storage, :unconnected_edges,
102
+ :assert_graph_integrity
91
103
 
92
104
  delegate :<<,
93
105
  :build,
@@ -125,346 +137,65 @@ module InventoryRefresh
125
137
  delegate :table_name,
126
138
  :to => :model_class
127
139
 
128
- # @param model_class [Class] A class of an ApplicationRecord model, that we want to persist into the DB or load from
129
- # the DB.
130
- # @param manager_ref [Array] Array of Symbols, that are keys of the InventoryObject's data, inserted into this
131
- # InventoryCollection. Using these keys, we need to be able to uniquely identify each of the InventoryObject
132
- # objects inside.
133
- # @param association [Symbol] A Rails association callable on a :parent attribute is used for comparing with the
134
- # objects in the DB, to decide if the InventoryObjects will be created/deleted/updated or used for obtaining
135
- # the data from a DB, if a DB strategy is used. It returns objects of the :model_class class or its sub STI.
136
- # @param parent [ApplicationRecord] An ApplicationRecord object that has a callable :association method returning
137
- # the objects of a :model_class.
138
- # @param strategy [Symbol] A strategy of the InventoryCollection that will be used for saving/loading of the
139
- # InventoryObject objects.
140
- # Allowed strategies are:
141
- # - nil => InventoryObject objects of the InventoryCollection will be saved to the DB, only these objects
142
- # will be referable from the other InventoryCollection objects.
143
- # - :local_db_cache_all => Loads InventoryObject objects from the database, it loads all the objects that
144
- # are a result of a [<:parent>.<:association>, :arel] taking
145
- # first defined in this order. This strategy will not save any objects in the DB.
146
- # - :local_db_find_references => Loads InventoryObject objects from the database, it loads only objects that
147
- # were referenced by the other InventoryCollections using a filtered result
148
- # of a [<:parent>.<:association>, :arel] taking first
149
- # defined in this order. This strategy will not save any objects in the DB.
150
- # - :local_db_find_missing_references => InventoryObject objects of the InventoryCollection will be saved to
151
- # the DB. Then if we reference an object that is not present, it will
152
- # load them from the db using :local_db_find_references strategy.
153
- # @param custom_save_block [Proc] A custom lambda/proc for persisting in the DB, for cases where it's not enough
154
- # to just save every InventoryObject inside by the defined rules and default saving algorithm.
155
- #
156
- # Example1 - saving SomeModel in my own ineffective way :-) :
157
- #
158
- # custom_save = lambda do |_ems, inventory_collection|
159
- # inventory_collection.each |inventory_object| do
160
- # hash = inventory_object.attributes # Loads possible dependencies into saveable hash
161
- # obj = SomeModel.find_by(:attr => hash[:attr]) # Note: doing find_by for many models produces N+1
162
- # # queries, avoid this, this is just a simple example :-)
163
- # obj.update_attributes(hash) if obj
164
- # obj ||= SomeModel.create(hash)
165
- # inventory_object.id = obj.id # If this InventoryObject is referenced elsewhere, we need to store its
166
- # primary key back to the InventoryObject
167
- # end
168
- #
169
- # Example2 - saving parent OrchestrationStack in a more effective way, than the default saving algorithm can
170
- # achieve. Ancestry gem requires an ActiveRecord object for association and is not defined as a proper
171
- # ActiveRecord association. That leads in N+1 queries in the default saving algorithm, so we can do better
172
- # with custom saving for now. The InventoryCollection is defined as a custom dependencies processor,
173
- # without its own :model_class and InventoryObjects inside:
174
- #
175
- # InventoryRefresh::InventoryCollection.new({
176
- # :association => :orchestration_stack_ancestry,
177
- # :custom_save_block => orchestration_stack_ancestry_save_block,
178
- # :dependency_attributes => {
179
- # :orchestration_stacks => [collections[:orchestration_stacks]],
180
- # :orchestration_stacks_resources => [collections[:orchestration_stacks_resources]]
181
- # }
182
- # })
183
- #
184
- # And the lambda is defined as:
185
- #
186
- # orchestration_stack_ancestry_save_block = lambda do |_ems, inventory_collection|
187
- # stacks_inventory_collection = inventory_collection.dependency_attributes[:orchestration_stacks].try(:first)
188
- #
189
- # return if stacks_inventory_collection.blank?
190
- #
191
- # stacks_parents = stacks_inventory_collection.data.each_with_object({}) do |x, obj|
192
- # parent_id = x.data[:parent].load.try(:id)
193
- # obj[x.id] = parent_id if parent_id
194
- # end
195
- #
196
- # model_class = stacks_inventory_collection.model_class
197
- #
198
- # stacks_parents_indexed = model_class
199
- # .select([:id, :ancestry])
200
- # .where(:id => stacks_parents.values).find_each.index_by(&:id)
201
- #
202
- # model_class
203
- # .select([:id, :ancestry])
204
- # .where(:id => stacks_parents.keys).find_each do |stack|
205
- # parent = stacks_parents_indexed[stacks_parents[stack.id]]
206
- # stack.update_attribute(:parent, parent)
207
- # end
208
- # end
209
- # @param custom_reconnect_block [Proc] A custom lambda for reconnect logic of previously disconnected records
210
- #
211
- # Example - Reconnect disconnected Vms
212
- # InventoryRefresh::InventoryCollection.new({
213
- # :association => :orchestration_stack_ancestry,
214
- # :custom_reconnect_block => vms_custom_reconnect_block,
215
- # })
216
- #
217
- # And the lambda is defined as:
218
- #
219
- # vms_custom_reconnect_block = lambda do |inventory_collection, inventory_objects_index, attributes_index|
220
- # inventory_objects_index.each_slice(1000) do |batch|
221
- # Vm.where(:ems_ref => batch.map(&:second).map(&:manager_uuid)).each do |record|
222
- # index = inventory_collection.object_index_with_keys(inventory_collection.manager_ref_to_cols, record)
223
- #
224
- # # We need to delete the record from the inventory_objects_index and attributes_index, otherwise it
225
- # # would be sent for create.
226
- # inventory_object = inventory_objects_index.delete(index)
227
- # hash = attributes_index.delete(index)
228
- #
229
- # record.assign_attributes(hash.except(:id, :type))
230
- # if !inventory_collection.check_changed? || record.changed?
231
- # record.save!
232
- # inventory_collection.store_updated_records(record)
233
- # end
234
- #
235
- # inventory_object.id = record.id
236
- # end
237
- # end
238
- # @param delete_method [Symbol] A delete method that will be used for deleting of the InventoryObject, if the
239
- # object is marked for deletion. A default is :destroy, the instance method must be defined on the
240
- # :model_class.
241
- # @param dependency_attributes [Hash] Manually defined dependencies of this InventoryCollection. We can use this
242
- # by manually place the InventoryCollection into the graph, to make sure the saving is invoked after the
243
- # dependencies were saved. The dependencies itself are InventoryCollection objects. For a common use-cases
244
- # we do not need to define dependencies manually, since those are inferred automatically by scanning of the
245
- # data.
246
- #
247
- # Example:
248
- # :dependency_attributes => {
249
- # :orchestration_stacks => [collections[:orchestration_stacks]],
250
- # :orchestration_stacks_resources => [collections[:orchestration_stacks_resources]]
251
- # }
252
- # This example is used in Example2 of the <param custom_save_block> and it means that our :custom_save_block
253
- # will be invoked after the InventoryCollection :orchestration_stacks and :orchestration_stacks_resources
254
- # are saved.
255
- # @param attributes_blacklist [Array] Attributes we do not want to include into saving. We cannot blacklist an
256
- # attribute that is needed for saving of the object.
257
- # Note: attributes_blacklist is also used for internal resolving of the cycles in the graph.
258
- #
259
- # In the Example2 of the <param custom_save_block>, we have a custom saving code, that saves a :parent
260
- # attribute of the OrchestrationStack. That means we don't want that attribute saved as a part of
261
- # InventoryCollection for OrchestrationStack, so we would set :attributes_blacklist => [:parent]. Then the
262
- # :parent will be ignored while saving.
263
- # @param attributes_whitelist [Array] Same usage as the :attributes_blacklist, but defining full set of attributes
264
- # that should be saved. Attributes that are part of :manager_ref and needed validations are automatically
265
- # added.
266
- # @param complete [Boolean] By default true, :complete is marking we are sending a complete dataset and therefore
267
- # we can create/update/delete the InventoryObject objects. If :complete is false we will only do
268
- # create/update without delete.
269
- # @param update_only [Boolean] By default false. If true we only update the InventoryObject objects, if false we do
270
- # create/update/delete.
271
- # @param check_changed [Boolean] By default true. If true, before updating the InventoryObject, we call Rails
272
- # 'changed?' method. This can optimize speed of updates heavily, but it can fail to recognize the change for
273
- # e.g. Ancestry and Relationship based columns. If false, we always update the InventoryObject.
274
- # @param arel [ActiveRecord::Associations::CollectionProxy|Arel::SelectManager] Instead of :parent and :association
275
- # we can provide Arel directly to say what records should be compared to check if InventoryObject will be
276
- # doing create/update/delete.
277
- #
278
- # Example:
279
- # for a targeted refresh, we want to delete/update/create only a list of vms specified with a list of
280
- # ems_refs:
281
- # :arel => manager.vms.where(:ems_ref => manager_refs)
282
- # Then we want to do the same for the hardwares of only those vms:
283
- # :arel => manager.hardwares.joins(:vm_or_template).where(
284
- # 'vms' => {:ems_ref => manager_refs}
285
- # )
286
- # And etc. for the other Vm related records.
287
- # @param default_values [Hash] A hash of an attributes that will be added to every inventory object created by
288
- # inventory_collection.build(hash)
289
- #
290
- # Example: Given
291
- # inventory_collection = InventoryCollection.new({
292
- # :model_class => ::Vm,
293
- # :arel => @ems.vms,
294
- # :default_values => {:ems_id => 10}
295
- # })
296
- # And building the inventory_object like:
297
- # inventory_object = inventory_collection.build(:ems_ref => "vm_1", :name => "vm1")
298
- # The inventory_object.data will look like:
299
- # {:ems_ref => "vm_1", :name => "vm1", :ems_id => 10}
300
- # @param inventory_object_attributes [Array] Array of attribute names that will be exposed as readers/writers on the
301
- # InventoryObject objects inside.
302
- #
303
- # Example: Given
304
- # inventory_collection = InventoryCollection.new({
305
- # :model_class => ::Vm,
306
- # :arel => @ems.vms,
307
- # :inventory_object_attributes => [:name, :label]
308
- # })
309
- # And building the inventory_object like:
310
- # inventory_object = inventory_collection.build(:ems_ref => "vm1", :name => "vm1")
311
- # We can use inventory_object_attributes as setters and getters:
312
- # inventory_object.name = "Name"
313
- # inventory_object.label = inventory_object.name
314
- # Which would be equivalent to less nicer way:
315
- # inventory_object[:name] = "Name"
316
- # inventory_object[:label] = inventory_object[:name]
317
- # So by using inventory_object_attributes, we will be guarding the allowed attributes and will have an
318
- # explicit list of allowed attributes, that can be used also for documentation purposes.
319
- # @param name [Symbol] A unique name of the InventoryCollection under a Persister. If not provided, the :association
320
- # attribute is used. If :association is nil as well, the :name will be inferred from the :model_class.
321
- # @param saver_strategy [Symbol] A strategy that will be used for InventoryCollection persisting into the DB.
322
- # Allowed saver strategies are:
323
- # - :default => Using Rails saving methods, this way is not safe to run in multiple workers concurrently,
324
- # since it will lead to non consistent data.
325
- # - :batch => Using batch SQL queries, this way is not safe to run in multiple workers
326
- # concurrently, since it will lead to non consistent data.
327
- # - :concurrent_safe => This method is designed for concurrent saving. It uses atomic upsert to avoid
328
- # data duplication and it uses timestamp based atomic checks to avoid new data being overwritten by the
329
- # the old data.
330
- # - :concurrent_safe_batch => Same as :concurrent_safe, but the upsert/update queries are executed as
331
- # batched SQL queries, instead of sending 1 query per record.
332
- # @param parent_inventory_collections [Array] Array of symbols having a name pointing to the
333
- # InventoryRefresh::InventoryCollection objects, that serve as parents to this InventoryCollection. There are
334
- # several scenarios to consider, when deciding if InventoryCollection has parent collections, see the example.
335
- #
336
- # Example:
337
- # taking inventory collections :vms and :disks (local disks), if we write that:
338
- # inventory_collection = InventoryCollection.new({
339
- # :model_class => ::Disk,
340
- # :association => :disks,
341
- # :manager_ref => [:vm, :location]
342
- # :parent_inventory_collection => [:vms],
343
- # })
344
- #
345
- # Then the decision for having :parent_inventory_collection => [:vms] was probably driven by these
346
- # points:
347
- # 1. We can get list of all disks only by doing SQL query through the parent object (so there will be join
348
- # from vms to disks table).
349
- # 2. There is no API query for getting all disks from the provider API, we get them inside VM data, or as
350
- # a Vm subquery
351
- # 3. Part of the manager_ref of the IC is the VM object (foreign key), so the disk's location is unique
352
- # only under 1 Vm. (In current models, this modeled going through Hardware model)
353
- # 4. In targeted refresh, we always expect that each Vm will be saved with all its disks.
354
- #
355
- # Then having the above points, adding :parent_inventory_collection => [:vms], will bring these
356
- # implications:
357
- # 1. By archiving/deleting Vm, we can no longer see the disk, because those were owned by the Vm. Any
358
- # archival/deletion of the Disk model, must be then done by cascade delete/hooks logic.
359
- # 2. Having Vm as a parent ensures we always process it first. So e.g. when providing no Vms for saving
360
- # we would have no graph dependency (no data --> no edges --> no dependencies) and Disk could be
361
- # archived/removed before the Vm, while we always want to archive the VM first.
362
- # 3. For targeted refresh, we always expect that all disks are saved with a VM. So for targeting :disks,
363
- # we are not using #manager_uuids attribute, since the scope is "all disks of all targeted VMs", so we
364
- # always use #manager_uuids of the parent. (that is why :parent_inventory_collections and
365
- # :manager_uuids are mutually exclusive attributes)
366
- # 4. For automatically building the #targeted_arel query, we need the parent to know what is the root node.
367
- # While this information can be introspected from the data, it creates a scope for create&update&delete,
368
- # which means it has to work with no data provided (causing delete all). So with no data we cannot
369
- # introspect anything.
370
- # @param manager_uuids [Array|Proc] Array of manager_uuids of the InventoryObjects we want to create/update/delete. Using
371
- # this attribute, the db_collection_for_comparison will be automatically limited by the manager_uuids, in a
372
- # case of a simple relation. In a case of a complex relation, we can leverage :manager_uuids in a
373
- # custom :targeted_arel. We can pass also lambda, for lazy_evaluation.
374
- # @param all_manager_uuids [Array] Array of all manager_uuids of the InventoryObjects. With the :targeted true,
375
- # having this parameter defined will invoke only :delete_method on a complement of this set, making sure
376
- # the DB has only this set of data after. This :attribute serves for deleting of top level
377
- # InventoryCollections, i.e. InventoryCollections having parent_inventory_collections nil. The deleting of
378
- # child collections is already handled by the scope of the parent_inventory_collections and using Rails
379
- # :dependent => :destroy,
380
- # @param targeted_arel [Proc] A callable block that receives this InventoryCollection as a first argument. In there
381
- # we can leverage a :parent_inventory_collections or :manager_uuids to limit the query based on the
382
- # manager_uuids available.
383
- # Example:
384
- # targeted_arel = lambda do |inventory_collection|
385
- # # Getting ems_refs of parent :vms and :miq_templates
386
- # manager_uuids = inventory_collection.parent_inventory_collections.collect(&:manager_uuids).flatten
387
- # inventory_collection.db_collection_for_comparison.hardwares.joins(:vm_or_template).where(
388
- # 'vms' => {:ems_ref => manager_uuids}
389
- # )
390
- # end
391
- #
392
- # inventory_collection = InventoryCollection.new({
393
- # :model_class => ::Hardware,
394
- # :association => :hardwares,
395
- # :parent_inventory_collection => [:vms, :miq_templates],
396
- # :targeted_arel => targeted_arel,
397
- # })
398
- # @param targeted [Boolean] True if the collection is targeted, in that case it will be leveraging :manager_uuids
399
- # :parent_inventory_collections and :targeted_arel to save a subgraph of a data.
400
- # @param manager_ref_allowed_nil [Array] Array of symbols having manager_ref columns, that are a foreign key an can
401
- # be nil. Given the table are shared by many providers, it can happen, that the table is used only partially.
402
- # Then it can happen we want to allow certain foreign keys to be nil, while being sure the referential
403
- # integrity is not broken. Of course the DB Foreign Key can't be created in this case, so we should try to
404
- # avoid this usecase by a proper modeling.
405
- # @param use_ar_object [Boolean] True or False. Whether we need to initialize AR object as part of the saving
406
- # it's needed if the model have special setters, serialize of columns, etc. This setting is relevant only
407
- # for the batch saver strategy.
408
- # @param batch_extra_attributes [Array] Array of symbols marking which extra attributes we want to store into the
409
- # db. These extra attributes might be a product of :use_ar_object assignment and we need to specify them
410
- # manually, if we want to use a batch saving strategy and we have models that populate attributes as a side
411
- # effect.
412
- def initialize(model_class: nil, manager_ref: nil, association: nil, parent: nil, strategy: nil,
413
- custom_save_block: nil, delete_method: nil, dependency_attributes: nil,
414
- attributes_blacklist: nil, attributes_whitelist: nil, complete: nil, update_only: nil,
415
- check_changed: nil, arel: nil, default_values: {}, create_only: nil,
416
- inventory_object_attributes: nil, name: nil, saver_strategy: nil,
417
- parent_inventory_collections: nil, manager_uuids: [], all_manager_uuids: nil, targeted_arel: nil,
418
- targeted: nil, manager_ref_allowed_nil: nil, secondary_refs: {}, use_ar_object: nil,
419
- custom_reconnect_block: nil, batch_extra_attributes: [])
420
- @model_class = model_class
421
- @manager_ref = manager_ref || [:ems_ref]
422
- @secondary_refs = secondary_refs
423
- @association = association
424
- @parent = parent || nil
425
- @arel = arel
426
- @dependency_attributes = dependency_attributes || {}
427
- @strategy = process_strategy(strategy)
428
- @delete_method = delete_method || :destroy
429
- @custom_save_block = custom_save_block
430
- @custom_reconnect_block = custom_reconnect_block
431
- @check_changed = check_changed.nil? ? true : check_changed
432
- @internal_attributes = %i(__feedback_edge_set_parent __parent_inventory_collections)
433
- @complete = complete.nil? ? true : complete
434
- @update_only = update_only.nil? ? false : update_only
435
- @create_only = create_only.nil? ? false : create_only
436
- @default_values = default_values
437
- @name = name || association || model_class.to_s.demodulize.tableize
438
- @saver_strategy = process_saver_strategy(saver_strategy)
439
- @use_ar_object = use_ar_object || false
440
- @batch_extra_attributes = batch_extra_attributes
441
-
442
- @manager_ref_allowed_nil = manager_ref_allowed_nil || []
443
-
444
- # Targeted mode related attributes
445
- # TODO(lsmola) Should we refactor this to use references too?
446
- @all_manager_uuids = all_manager_uuids
447
- @parent_inventory_collections = parent_inventory_collections
448
- @targeted_arel = targeted_arel
449
- @targeted = !!targeted
450
-
451
- @inventory_object_attributes = inventory_object_attributes
452
-
453
- @saved ||= false
454
- @attributes_blacklist = Set.new
455
- @attributes_whitelist = Set.new
456
- @transitive_dependency_attributes = Set.new
457
- @dependees = Set.new
458
- @data_storage = ::InventoryRefresh::InventoryCollection::DataStorage.new(self, secondary_refs)
459
- @references_storage = ::InventoryRefresh::InventoryCollection::ReferencesStorage.new(index_proxy)
460
- @targeted_scope = ::InventoryRefresh::InventoryCollection::ReferencesStorage.new(index_proxy).merge!(manager_uuids)
461
-
462
- @created_records = []
463
- @updated_records = []
464
- @deleted_records = []
465
-
466
- blacklist_attributes!(attributes_blacklist) if attributes_blacklist.present?
467
- whitelist_attributes!(attributes_whitelist) if attributes_whitelist.present?
140
+ include ::InventoryRefresh::InventoryCollection::Helpers::AssociationsHelper
141
+ include ::InventoryRefresh::InventoryCollection::Helpers::InitializeHelper
142
+ include ::InventoryRefresh::InventoryCollection::Helpers::QuestionsHelper
143
+
144
+ # @param [Hash] properties - see init methods for params description
145
+ def initialize(properties = {})
146
+ init_basic_properties(properties[:association],
147
+ properties[:model_class],
148
+ properties[:name],
149
+ properties[:parent])
150
+
151
+ init_flags(properties[:complete],
152
+ properties[:create_only],
153
+ properties[:check_changed],
154
+ properties[:update_only],
155
+ properties[:use_ar_object],
156
+ properties[:targeted],
157
+ properties[:assert_graph_integrity])
158
+
159
+ init_strategies(properties[:strategy],
160
+ properties[:saver_strategy],
161
+ properties[:retention_strategy],
162
+ properties[:delete_method])
163
+
164
+ init_references(properties[:manager_ref],
165
+ properties[:manager_ref_allowed_nil],
166
+ properties[:secondary_refs],
167
+ properties[:manager_uuids])
168
+
169
+ init_all_manager_uuids(properties[:all_manager_uuids],
170
+ properties[:all_manager_uuids_scope],
171
+ properties[:all_manager_uuids_timestamp])
172
+
173
+ init_ic_relations(properties[:dependency_attributes],
174
+ properties[:parent_inventory_collections])
175
+
176
+ init_arels(properties[:arel],
177
+ properties[:targeted_arel])
178
+
179
+ init_custom_procs(properties[:custom_save_block],
180
+ properties[:custom_reconnect_block])
181
+
182
+ init_model_attributes(properties[:attributes_blacklist],
183
+ properties[:attributes_whitelist],
184
+ properties[:inventory_object_attributes],
185
+ properties[:batch_extra_attributes])
186
+
187
+ init_data(properties[:default_values])
188
+
189
+ init_storages
190
+
191
+ init_changed_records_stats
192
+ end
193
+
194
+ def store_unconnected_edges(inventory_object, inventory_object_key, inventory_object_lazy)
195
+ (@unconnected_edges ||= []) <<
196
+ InventoryRefresh::InventoryCollection::UnconnectedEdge.new(
197
+ inventory_object, inventory_object_key, inventory_object_lazy
198
+ )
468
199
  end
469
200
 
470
201
  # Caches what records were created, for later use, e.g. post provision behavior
@@ -488,166 +219,6 @@ module InventoryRefresh
488
219
  @deleted_records.concat(records_identities(records))
489
220
  end
490
221
 
491
- # Processes passed saver strategy
492
- #
493
- # @param saver_strategy [Symbol] Passed saver strategy
494
- # @return [Symbol] Returns back the passed strategy if supported, or raises exception
495
- def process_saver_strategy(saver_strategy)
496
- return :default unless saver_strategy
497
-
498
- saver_strategy = saver_strategy.to_sym
499
- case saver_strategy
500
- when :default, :batch, :concurrent_safe, :concurrent_safe_batch
501
- saver_strategy
502
- else
503
- raise "Unknown InventoryCollection saver strategy: :#{saver_strategy}, allowed strategies are "\
504
- ":default, :batch, :concurrent_safe and :concurrent_safe_batch"
505
- end
506
- end
507
-
508
- # Processes passed strategy, modifies :data_collection_finalized and :saved attributes for db only strategies
509
- #
510
- # @param strategy_name [Symbol] Passed saver strategy
511
- # @return [Symbol] Returns back the passed strategy if supported, or raises exception
512
- def process_strategy(strategy_name)
513
- self.data_collection_finalized = false
514
-
515
- return unless strategy_name
516
-
517
- strategy_name = strategy_name.to_sym
518
- case strategy_name
519
- when :local_db_cache_all
520
- self.data_collection_finalized = true
521
- self.saved = true
522
- when :local_db_find_references
523
- self.saved = true
524
- when :local_db_find_missing_references
525
- else
526
- raise "Unknown InventoryCollection strategy: :#{strategy_name}, allowed strategies are :local_db_cache_all, "\
527
- ":local_db_find_references and :local_db_find_missing_references."
528
- end
529
- strategy_name
530
- end
531
-
532
- # @return [Boolean] true means we want to call .changed? on every ActiveRecord object before saving it
533
- def check_changed?
534
- check_changed
535
- end
536
-
537
- # @return [Boolean] true means we want to use ActiveRecord object for writing attributes and we want to perform
538
- # casting on all columns
539
- def use_ar_object?
540
- use_ar_object
541
- end
542
-
543
- # @return [Boolean] true means the data is not complete, leading to only creating and updating data
544
- def complete?
545
- complete
546
- end
547
-
548
- # @return [Boolean] true means we want to only update data
549
- def update_only?
550
- update_only
551
- end
552
-
553
- # @return [Boolean] true means we will delete/soft-delete data
554
- def delete_allowed?
555
- complete? && !update_only?
556
- end
557
-
558
- # @return [Boolean] true means we will delete/soft-delete data
559
- def create_allowed?
560
- !update_only?
561
- end
562
-
563
- # @return [Boolean] true means that only create of new data is allowed
564
- def create_only?
565
- create_only
566
- end
567
-
568
- # @return [Boolean] true if the whole InventoryCollection object has all data persisted
569
- def saved?
570
- saved
571
- end
572
-
573
- # @return [Boolean] true if all dependencies have all data persisted
574
- def saveable?
575
- dependencies.all?(&:saved?)
576
- end
577
-
578
- # @return [Boolean] true if we are using a saver strategy that allows saving in parallel processes
579
- def parallel_safe?
580
- @parallel_safe_cache ||= %i(concurrent_safe concurrent_safe_batch).include?(saver_strategy)
581
- end
582
-
583
- # @return [Boolean] true if the model_class supports STI
584
- def supports_sti?
585
- @supports_sti_cache = model_class.column_names.include?("type") if @supports_sti_cache.nil?
586
- @supports_sti_cache
587
- end
588
-
589
- # @return [Boolean] true if the model_class has created_on column
590
- def supports_created_on?
591
- if @supports_created_on_cache.nil?
592
- @supports_created_on_cache = (model_class.column_names.include?("created_on") && ActiveRecord::Base.record_timestamps)
593
- end
594
- @supports_created_on_cache
595
- end
596
-
597
- # @return [Boolean] true if the model_class has updated_on column
598
- def supports_updated_on?
599
- if @supports_updated_on_cache.nil?
600
- @supports_updated_on_cache = (model_class.column_names.include?("updated_on") && ActiveRecord::Base.record_timestamps)
601
- end
602
- @supports_updated_on_cache
603
- end
604
-
605
- # @return [Boolean] true if the model_class has created_at column
606
- def supports_created_at?
607
- if @supports_created_at_cache.nil?
608
- @supports_created_at_cache = (model_class.column_names.include?("created_at") && ActiveRecord::Base.record_timestamps)
609
- end
610
- @supports_created_at_cache
611
- end
612
-
613
- # @return [Boolean] true if the model_class has updated_at column
614
- def supports_updated_at?
615
- if @supports_updated_at_cache.nil?
616
- @supports_updated_at_cache = (model_class.column_names.include?("updated_at") && ActiveRecord::Base.record_timestamps)
617
- end
618
- @supports_updated_at_cache
619
- end
620
-
621
- # @return [Boolean] true if the model_class has resource_timestamps_max column
622
- def supports_resource_timestamps_max?
623
- @supports_resource_timestamps_max_cache ||= model_class.column_names.include?("resource_timestamps_max")
624
- end
625
-
626
- # @return [Boolean] true if the model_class has resource_timestamps column
627
- def supports_resource_timestamps?
628
- @supports_resource_timestamps_cache ||= model_class.column_names.include?("resource_timestamps")
629
- end
630
-
631
- # @return [Boolean] true if the model_class has resource_timestamp column
632
- def supports_resource_timestamp?
633
- @supports_resource_timestamp_cache ||= model_class.column_names.include?("resource_timestamp")
634
- end
635
-
636
- # @return [Boolean] true if the model_class has resource_versions_max column
637
- def supports_resource_versions_max?
638
- @supports_resource_versions_max_cache ||= model_class.column_names.include?("resource_versions_max")
639
- end
640
-
641
- # @return [Boolean] true if the model_class has resource_versions column
642
- def supports_resource_versions?
643
- @supports_resource_versions_cache ||= model_class.column_names.include?("resource_versions")
644
- end
645
-
646
- # @return [Boolean] true if the model_class has resource_version column
647
- def supports_resource_version?
648
- @supports_resource_version_cache ||= model_class.column_names.include?("resource_version")
649
- end
650
-
651
222
  # @return [Array<Symbol>] all columns that are part of the best fit unique index
652
223
  def unique_index_columns
653
224
  return @unique_index_columns if @unique_index_columns
@@ -667,7 +238,7 @@ module InventoryRefresh
667
238
 
668
239
  if @unique_indexes_cache.blank?
669
240
  raise "#{self} and its table #{model_class.table_name} must have a unique index defined, to"\
670
- " be able to use saver_strategy :concurrent_safe or :concurrent_safe_batch."
241
+ " be able to use saver_strategy :concurrent_safe_batch."
671
242
  end
672
243
 
673
244
  @unique_indexes_cache
@@ -682,16 +253,29 @@ module InventoryRefresh
682
253
  @unique_index_for_keys_cache ||= {}
683
254
  @unique_index_for_keys_cache[keys] if @unique_index_for_keys_cache[keys]
684
255
 
256
+ # Take the uniq key having the least number of columns
257
+ @unique_index_for_keys_cache[keys] = uniq_keys_candidates(keys).min_by { |x| x.columns.count }
258
+ end
259
+
260
+ # Find candidates for unique key. Candidate must cover all columns we are passing as keys.
261
+ #
262
+ # @param keys [Array<Symbol>]
263
+ # @raise [Exception] if the unique index for the columns was not found
264
+ # @return [Array<ActiveRecord::ConnectionAdapters::IndexDefinition>] Array of unique indexes fitting the keys
265
+ def uniq_keys_candidates(keys)
685
266
  # Find all uniq indexes that that are covering our keys
686
267
  uniq_key_candidates = unique_indexes.each_with_object([]) { |i, obj| obj << i if (keys - i.columns.map(&:to_sym)).empty? }
687
268
 
688
- if @unique_indexes_cache.blank?
269
+ if unique_indexes.blank? || uniq_key_candidates.blank?
689
270
  raise "#{self} and its table #{model_class.table_name} must have a unique index defined "\
690
- "covering columns #{keys} to be able to use saver_strategy :concurrent_safe or :concurrent_safe_batch."
271
+ "covering columns #{keys} to be able to use saver_strategy :concurrent_safe_batch."
691
272
  end
692
273
 
693
- # Take the uniq key having the least number of columns
694
- @unique_index_for_keys_cache[keys] = uniq_key_candidates.min_by { |x| x.columns.count }
274
+ uniq_key_candidates
275
+ end
276
+
277
+ def resource_version_column
278
+ :resource_version
695
279
  end
696
280
 
697
281
  def internal_columns
@@ -699,35 +283,32 @@ module InventoryRefresh
699
283
 
700
284
  @internal_columns = [] + internal_timestamp_columns
701
285
  @internal_columns << :type if supports_sti?
702
- @internal_columns << :resource_timestamps_max if supports_resource_timestamps_max?
703
- @internal_columns << :resource_timestamps if supports_resource_timestamps?
704
- @internal_columns << :resource_timestamp if supports_resource_timestamp?
705
- @internal_columns << :resource_versions_max if supports_resource_versions_max?
706
- @internal_columns << :resource_versions if supports_resource_versions?
707
- @internal_columns << :resource_version if supports_resource_version?
708
- @internal_columns
286
+ @internal_columns += [resource_version_column,
287
+ :resource_timestamps_max,
288
+ :resource_timestamps,
289
+ :resource_timestamp,
290
+ :resource_counters_max,
291
+ :resource_counters,
292
+ :resource_counter].collect do |col|
293
+ col if supports_column?(col)
294
+ end.compact
709
295
  end
710
296
 
711
297
  def internal_timestamp_columns
712
298
  return @internal_timestamp_columns if @internal_timestamp_columns
713
299
 
714
- @internal_timestamp_columns = []
715
- @internal_timestamp_columns << :created_on if supports_created_on?
716
- @internal_timestamp_columns << :created_at if supports_created_at?
717
- @internal_timestamp_columns << :updated_on if supports_updated_on?
718
- @internal_timestamp_columns << :updated_at if supports_updated_at?
719
-
720
- @internal_timestamp_columns
300
+ @internal_timestamp_columns = %i(created_at created_on updated_at updated_on).collect do |timestamp_col|
301
+ timestamp_col if supports_column?(timestamp_col)
302
+ end.compact
721
303
  end
722
304
 
723
- def base_columns
724
- @base_columns ||= unique_index_columns + internal_columns
305
+ # @return [Array] Array of column names that have not null constraint
306
+ def not_null_columns
307
+ @not_null_constraint_columns ||= model_class.columns.reject(&:null).map { |x| x.name.to_sym } - [model_class.primary_key.to_sym]
725
308
  end
726
309
 
727
- # @return [Boolean] true if no more data will be added to this InventoryCollection object, that usually happens
728
- # after the parsing step is finished
729
- def data_collection_finalized?
730
- data_collection_finalized
310
+ def base_columns
311
+ @base_columns ||= (unique_index_columns + internal_columns + not_null_columns).uniq
731
312
  end
732
313
 
733
314
  # @param value [Object] Object we want to test
@@ -752,38 +333,6 @@ module InventoryRefresh
752
333
  build_stringified_reference_for_record(record, keys)
753
334
  end
754
335
 
755
- # True if processing of this InventoryCollection object would lead to no operations. Then we use this marker to
756
- # stop processing of the InventoryCollector object very soon, to avoid a lot of unnecessary Db queries, etc.
757
- #
758
- # @return [Boolean] true if processing of this InventoryCollection object would lead to no operations.
759
- def noop?
760
- # If this InventoryCollection doesn't do anything. it can easily happen for targeted/batched strategies.
761
- if targeted?
762
- if parent_inventory_collections.nil? && targeted_scope.primary_references.blank? &&
763
- all_manager_uuids.nil? && parent_inventory_collections.blank? && custom_save_block.nil? &&
764
- skeletal_primary_index.blank?
765
- # It's a noop Parent targeted InventoryCollection
766
- true
767
- elsif !parent_inventory_collections.nil? && parent_inventory_collections.all? { |x| x.targeted_scope.primary_references.blank? } &&
768
- skeletal_primary_index.blank?
769
- # It's a noop Child targeted InventoryCollection
770
- true
771
- else
772
- false
773
- end
774
- elsif data.blank? && !delete_allowed? && skeletal_primary_index.blank?
775
- # If we have no data to save and delete is not allowed, we can just skip
776
- true
777
- else
778
- false
779
- end
780
- end
781
-
782
- # @return [Boolean] true is processing of this InventoryCollection will be in targeted mode
783
- def targeted?
784
- targeted
785
- end
786
-
787
336
  # Convert manager_ref list of attributes to list of DB columns
788
337
  #
789
338
  # @return [Array<String>] true is processing of this InventoryCollection will be in targeted mode
@@ -897,77 +446,6 @@ module InventoryRefresh
897
446
  cloned
898
447
  end
899
448
 
900
- # @return [Array<ActiveRecord::Reflection::BelongsToReflection">] All belongs_to associations
901
- def belongs_to_associations
902
- model_class.reflect_on_all_associations.select { |x| x.kind_of?(ActiveRecord::Reflection::BelongsToReflection) }
903
- end
904
-
905
- # @return [Hash{Symbol => String}] Hash with association name mapped to foreign key column name
906
- def association_to_foreign_key_mapping
907
- return {} unless model_class
908
-
909
- @association_to_foreign_key_mapping ||= belongs_to_associations.each_with_object({}) do |x, obj|
910
- obj[x.name] = x.foreign_key
911
- end
912
- end
913
-
914
- # @return [Hash{String => Hash}] Hash with foreign_key column name mapped to association name
915
- def foreign_key_to_association_mapping
916
- return {} unless model_class
917
-
918
- @foreign_key_to_association_mapping ||= belongs_to_associations.each_with_object({}) do |x, obj|
919
- obj[x.foreign_key] = x.name
920
- end
921
- end
922
-
923
- # @return [Hash{Symbol => String}] Hash with association name mapped to polymorphic foreign key type column name
924
- def association_to_foreign_type_mapping
925
- return {} unless model_class
926
-
927
- @association_to_foreign_type_mapping ||= model_class.reflect_on_all_associations.each_with_object({}) do |x, obj|
928
- obj[x.name] = x.foreign_type if x.polymorphic?
929
- end
930
- end
931
-
932
- # @return [Hash{Symbol => String}] Hash with polymorphic foreign key type column name mapped to association name
933
- def foreign_type_to_association_mapping
934
- return {} unless model_class
935
-
936
- @foreign_type_to_association_mapping ||= model_class.reflect_on_all_associations.each_with_object({}) do |x, obj|
937
- obj[x.foreign_type] = x.name if x.polymorphic?
938
- end
939
- end
940
-
941
- # @return [Hash{Symbol => String}] Hash with association name mapped to base class of the association
942
- def association_to_base_class_mapping
943
- return {} unless model_class
944
-
945
- @association_to_base_class_mapping ||= model_class.reflect_on_all_associations.each_with_object({}) do |x, obj|
946
- obj[x.name] = x.klass.base_class.name unless x.polymorphic?
947
- end
948
- end
949
-
950
- # @return [Array<Symbol>] List of all column names that are foreign keys
951
- def foreign_keys
952
- return [] unless model_class
953
-
954
- @foreign_keys_cache ||= belongs_to_associations.map(&:foreign_key).map!(&:to_sym)
955
- end
956
-
957
- # @return [Array<Symbol>] List of all column names that are foreign keys and cannot removed, otherwise we couldn't
958
- # save the record
959
- def fixed_foreign_keys
960
- # Foreign keys that are part of a manager_ref must be present, otherwise the record would get lost. This is a
961
- # minimum check we can do to not break a referential integrity.
962
- return @fixed_foreign_keys_cache unless @fixed_foreign_keys_cache.nil?
963
-
964
- manager_ref_set = (manager_ref - manager_ref_allowed_nil)
965
- @fixed_foreign_keys_cache = manager_ref_set.map { |x| association_to_foreign_key_mapping[x] }.compact
966
- @fixed_foreign_keys_cache += foreign_keys & manager_ref
967
- @fixed_foreign_keys_cache.map!(&:to_sym)
968
- @fixed_foreign_keys_cache
969
- end
970
-
971
449
  # @return [String] Base class name of the model_class of this InventoryCollection
972
450
  def base_class_name
973
451
  return "" unless model_class
@@ -1057,11 +535,11 @@ module InventoryRefresh
1057
535
 
1058
536
  # Gets targeted references and transforms them into list of hashes
1059
537
  #
1060
- # @param references [Array, InventoryRefresh::Inventorycollection::TargetedScope] passed references
538
+ # @param references [Array, InventoryRefresh::InventoryCollection::TargetedScope] passed references
1061
539
  # @return [Array<Hash>] References transformed into the array of hashes
1062
540
  def transform_references_to_hashes(references)
1063
541
  if references.kind_of?(Array)
1064
- # Sliced InventoryRefresh::Inventorycollection::TargetedScope
542
+ # Sliced InventoryRefresh::InventoryCollection::TargetedScope
1065
543
  references.map { |x| x.second.full_reference }
1066
544
  else
1067
545
  references.values.map(&:full_reference)
@@ -1098,22 +576,12 @@ module InventoryRefresh
1098
576
  full_collection_for_comparison.where(targeted_selection_for(references))
1099
577
  end
1100
578
 
1101
- # Builds an ActiveRecord::Relation that can fetch complement of all the references from the DB
1102
- #
1103
- # @param manager_uuids_set [Array<String>] passed references
1104
- # @return [ActiveRecord::Relation] relation that can fetch complement of all the references from the DB
1105
- def db_collection_for_comparison_for_complement_of(manager_uuids_set)
1106
- # TODO(lsmola) this should have the build_multi_selection_condition, like in the method above
1107
- # TODO(lsmola) this query will be highly ineffective, we will try approach with updating a timestamp of all
1108
- # records, then we can get list of all records that were not update. That would be equivalent to result of this
1109
- # more effective query and without need of all manager_uuids
1110
- full_collection_for_comparison.where.not(manager_ref.first => manager_uuids_set)
1111
- end
1112
-
1113
579
  # @return [ActiveRecord::Relation] relation that can fetch all the references from the DB
1114
580
  def full_collection_for_comparison
1115
581
  return arel unless arel.nil?
1116
- parent.send(association)
582
+ rel = parent.send(association)
583
+ rel = rel.active if rel && supports_column?(:archived_at) && retention_strategy == :archive
584
+ rel
1117
585
  end
1118
586
 
1119
587
  # Creates InventoryRefresh::InventoryObject object from passed hash data
@@ -1167,6 +635,7 @@ module InventoryRefresh
1167
635
  }
1168
636
  end
1169
637
 
638
+ # TODO: Not used!
1170
639
  # @return [Array<Symbol>] all association attributes and foreign keys of the model class
1171
640
  def association_attributes
1172
641
  model_class.reflect_on_all_associations.map { |x| [x.name, x.foreign_key] }.flatten.compact.map(&:to_sym)