inventory_refresh 0.1.2 → 0.2.3

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