inventory_refresh 0.1.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
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)