inventory_refresh 0.1.1 → 0.2.2

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.
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)