inventory_refresh 0.2.2 → 0.3.0

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 +0 -1
  3. data/.travis.yml +6 -8
  4. data/inventory_refresh.gemspec +2 -4
  5. data/lib/inventory_refresh.rb +0 -2
  6. data/lib/inventory_refresh/application_record_iterator.rb +9 -26
  7. data/lib/inventory_refresh/exception.rb +8 -0
  8. data/lib/inventory_refresh/inventory_collection.rb +36 -110
  9. data/lib/inventory_refresh/inventory_collection/builder.rb +6 -6
  10. data/lib/inventory_refresh/inventory_collection/data_storage.rb +0 -9
  11. data/lib/inventory_refresh/inventory_collection/helpers/initialize_helper.rb +34 -143
  12. data/lib/inventory_refresh/inventory_collection/helpers/questions_helper.rb +1 -44
  13. data/lib/inventory_refresh/inventory_collection/index/proxy.rb +6 -34
  14. data/lib/inventory_refresh/inventory_collection/index/type/base.rb +0 -8
  15. data/lib/inventory_refresh/inventory_collection/references_storage.rb +0 -17
  16. data/lib/inventory_refresh/inventory_collection/scanner.rb +1 -87
  17. data/lib/inventory_refresh/inventory_collection/serialization.rb +10 -16
  18. data/lib/inventory_refresh/inventory_object.rb +34 -68
  19. data/lib/inventory_refresh/inventory_object_lazy.rb +10 -17
  20. data/lib/inventory_refresh/persister.rb +63 -29
  21. data/lib/inventory_refresh/save_collection/base.rb +2 -4
  22. data/lib/inventory_refresh/save_collection/saver/base.rb +8 -108
  23. data/lib/inventory_refresh/save_collection/saver/concurrent_safe_batch.rb +48 -126
  24. data/lib/inventory_refresh/save_collection/saver/partial_upsert_helper.rb +19 -1
  25. data/lib/inventory_refresh/save_collection/saver/retention_helper.rb +3 -68
  26. data/lib/inventory_refresh/save_collection/saver/sql_helper.rb +0 -125
  27. data/lib/inventory_refresh/save_collection/saver/sql_helper_update.rb +5 -9
  28. data/lib/inventory_refresh/save_collection/saver/sql_helper_upsert.rb +9 -17
  29. data/lib/inventory_refresh/save_collection/sweeper.rb +91 -18
  30. data/lib/inventory_refresh/save_collection/topological_sort.rb +5 -5
  31. data/lib/inventory_refresh/save_inventory.rb +12 -5
  32. data/lib/inventory_refresh/version.rb +1 -1
  33. metadata +9 -45
  34. data/lib/inventory_refresh/save_collection/saver/batch.rb +0 -17
  35. data/lib/inventory_refresh/save_collection/saver/default.rb +0 -57
  36. data/lib/inventory_refresh/target.rb +0 -73
  37. data/lib/inventory_refresh/target_collection.rb +0 -92
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f587f79d9daabcefff5a1b479301971cdd31f940d517cfc058480a0c17bc3abc
4
- data.tar.gz: ac2962c044aba21b8bb5963ae06c788fdeb3863d29a34b33933360ffef432438
3
+ metadata.gz: 666554cf2d3928670cd6772706360d2c2523fc0475b63f2aead1e7e2bec675d7
4
+ data.tar.gz: 992aa863fdd2d020878f3bd18e0c83d7bf37aa79d6363814e10fdc5cd311a35a
5
5
  SHA512:
6
- metadata.gz: 9487530c2f5a42191a61c632fb85d30cbf6113ba6b5208bc2a0f3f2bee6c7634e8018d5896e9ba4bbe5db4bf12c1c5b41084f1eb1e36c66a89d6a5e7b4ef082e
7
- data.tar.gz: 66381139ddee4c8f29bc0b0b1e08864f71b4805c6fc354c4dfb372641c5bb56ebb43cc4670b2958d6431a7a0195ef4a60c468a39ace586178b7cc760259f2369
6
+ metadata.gz: 1ce760c31989eaa2ec3d230031c176007953b9d5790d7d5e0c85d7b62e43f6d7d45cbe88651a70df3ca77c11f4d7b5587348e2ba0d951add5d8d76a90ba9d0e0
7
+ data.tar.gz: 4084a11ccd8ab92142e44399159290d5b14be05aa111f5c681571c6ba034a4ecb55587f0d3c2dda9d0ef765e4c5aefa9f2ea9e4183c27b81d4547b3719ae8393
@@ -34,7 +34,6 @@ engines:
34
34
  rubocop:
35
35
  enabled: true
36
36
  config: '.rubocop_cc.yml'
37
- channel: rubocop-0-69
38
37
  prepare:
39
38
  fetch:
40
39
  - url: "https://raw.githubusercontent.com/ManageIQ/guides/master/.rubocop_base.yml"
@@ -1,12 +1,12 @@
1
- ---
1
+ dist: xenial
2
2
  sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
- - 2.5.7
7
- - 2.6.5
6
+ - 2.4.4
7
+ - 2.3.6
8
8
  addons:
9
- postgresql: '9.5'
9
+ postgresql: '10'
10
10
  env:
11
11
  global:
12
12
  - CC_TEST_REPORTER_ID=1ef1a3a3d007395b11083d634a6fdac1e3d979b6428c39d2cd8d58556cdd68f7
@@ -14,12 +14,10 @@ before_install:
14
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
- # Code Climate
18
- - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
17
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64
18
+ > ./cc-test-reporter
19
19
  - chmod +x ./cc-test-reporter
20
20
  - "./cc-test-reporter before-build"
21
- # inventory_refresh
22
21
  - bundle exec rake spec:setup
23
22
  after_script:
24
- # Code Climate
25
23
  - "./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT"
@@ -23,15 +23,13 @@ Gem::Specification.new do |spec|
23
23
  spec.require_paths = ["lib"]
24
24
 
25
25
  spec.add_dependency "activerecord", "~> 5.0"
26
- spec.add_dependency "more_core_extensions", ">=3.5", "< 5"
26
+ spec.add_dependency "more_core_extensions", "~> 3.5"
27
27
  spec.add_dependency "pg", "> 0"
28
28
 
29
29
  spec.add_development_dependency "ancestry"
30
- spec.add_development_dependency "bundler", "~> 2.0"
30
+ spec.add_development_dependency "bundler", "~> 2.0.1"
31
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"
36
34
  spec.add_development_dependency "simplecov"
37
35
  end
@@ -6,6 +6,4 @@ require "inventory_refresh/logging"
6
6
  require "inventory_refresh/null_logger"
7
7
  require "inventory_refresh/persister"
8
8
  require "inventory_refresh/save_inventory"
9
- require "inventory_refresh/target"
10
- require "inventory_refresh/target_collection"
11
9
  require "inventory_refresh/version"
@@ -1,20 +1,12 @@
1
1
  module InventoryRefresh
2
2
  class ApplicationRecordIterator
3
- attr_reader :inventory_collection, :manager_uuids_set, :iterator, :query
3
+ attr_reader :inventory_collection
4
4
 
5
- # An iterator that can fetch batches of the AR objects based on a set of manager refs, or just mimics AR relation
6
- # when given an iterator. Or given query, acts as iterator by selecting batches.
5
+ # An iterator that can fetch batches of the AR objects based on a set of attribute_indexes
7
6
  #
8
7
  # @param inventory_collection [InventoryRefresh::InventoryCollection] Inventory collection owning the iterator
9
- # @param manager_uuids_set [Array<InventoryRefresh::InventoryCollection::Reference>] Array of references we want to
10
- # fetch from the DB
11
- # @param iterator [Proc] Block based iterator
12
- # @query query [ActiveRecord::Relation] Existing query we want to use for querying the db
13
- def initialize(inventory_collection: nil, manager_uuids_set: nil, iterator: nil, query: nil)
8
+ def initialize(inventory_collection: nil)
14
9
  @inventory_collection = inventory_collection
15
- @manager_uuids_set = manager_uuids_set
16
- @iterator = iterator
17
- @query = query
18
10
  end
19
11
 
20
12
  # Iterator that mimics find_in_batches of ActiveRecord::Relation. This iterator serves for making more optimized query
@@ -25,28 +17,19 @@ module InventoryRefresh
25
17
  # and relation.where(:id => 500ids)
26
18
  #
27
19
  # @param batch_size [Integer] A batch size we want to fetch from DB
20
+ # @param attributes_index [Hash{String => Hash}] Indexed hash with data we will be saving
28
21
  # @yield Code processing the batches
29
- def find_in_batches(batch_size: 1000)
30
- if iterator
31
- iterator.call do |batch|
32
- yield(batch)
33
- end
34
- elsif query
35
- manager_uuids_set.each_slice(batch_size) do |batch|
36
- yield(query.where(inventory_collection.targeted_selection_for(batch)))
37
- end
38
- else
39
- manager_uuids_set.each_slice(batch_size) do |batch|
40
- yield(inventory_collection.db_collection_for_comparison_for(batch))
41
- end
22
+ def find_in_batches(batch_size: 1000, attributes_index: {})
23
+ attributes_index.each_slice(batch_size) do |batch|
24
+ yield(inventory_collection.db_collection_for_comparison_for(batch))
42
25
  end
43
26
  end
44
27
 
45
28
  # Iterator that mimics find_each of ActiveRecord::Relation using find_in_batches (see #find_in_batches)
46
29
  #
47
30
  # @yield Code processing the batches
48
- def find_each
49
- find_in_batches do |batch|
31
+ def find_each(attributes_index: {})
32
+ find_in_batches(:attributes_index => attributes_index) do |batch|
50
33
  batch.each do |item|
51
34
  yield(item)
52
35
  end
@@ -0,0 +1,8 @@
1
+ module InventoryRefresh
2
+ module Exception
3
+ class SweeperError < StandardError; end
4
+ class SweeperNonExistentScopeKeyFoundError < SweeperError; end
5
+ class SweeperNonUniformScopeKeyFoundError < SweeperError; end
6
+ class SweeperScopeBadFormat < SweeperError; end
7
+ end
8
+ end
@@ -71,32 +71,14 @@ module InventoryRefresh
71
71
  # InventoryCollection, since it is already persisted into the DB.
72
72
  attr_accessor :saved
73
73
 
74
- # If present, InventoryCollection switches into delete_complement mode, where it will
75
- # delete every record from the DB, that is not present in this list. This is used for the batch processing,
76
- # where we don't know which InventoryObject should be deleted, but we know all manager_uuids of all
77
- # InventoryObject objects that exists in the provider.
78
- #
79
- # @return [Array, nil] nil or a list of all :manager_uuids that are present in the Provider's InventoryCollection.
80
- attr_accessor :all_manager_uuids
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
-
88
74
  # @return [Set] A set of InventoryCollection objects that depends on this InventoryCollection object.
89
75
  attr_accessor :dependees
90
76
 
91
- # @return [Array<Symbol>] @see #parent_inventory_collections documentation of InventoryCollection.new's initialize_ic_relations()
92
- # parameters
93
- attr_accessor :parent_inventory_collections
94
-
95
77
  attr_reader :model_class, :strategy, :attributes_blacklist, :attributes_whitelist, :custom_save_block, :parent,
96
- :internal_attributes, :delete_method, :dependency_attributes, :manager_ref, :create_only,
78
+ :internal_attributes, :dependency_attributes, :manager_ref, :secondary_refs, :create_only,
97
79
  :association, :complete, :update_only, :transitive_dependency_attributes, :check_changed, :arel,
98
- :inventory_object_attributes, :name, :saver_strategy, :targeted_scope, :default_values,
99
- :targeted_arel, :targeted, :manager_ref_allowed_nil, :use_ar_object,
80
+ :inventory_object_attributes, :name, :saver_strategy, :default_values,
81
+ :manager_ref_allowed_nil, :use_ar_object,
100
82
  :created_records, :updated_records, :deleted_records, :retention_strategy,
101
83
  :custom_reconnect_block, :batch_extra_attributes, :references_storage, :unconnected_edges,
102
84
  :assert_graph_integrity
@@ -130,7 +112,6 @@ module InventoryRefresh
130
112
  :lazy_find_by,
131
113
  :named_ref,
132
114
  :primary_index,
133
- :reindex_secondary_indexes!,
134
115
  :skeletal_primary_index,
135
116
  :to => :index_proxy
136
117
 
@@ -153,28 +134,18 @@ module InventoryRefresh
153
134
  properties[:check_changed],
154
135
  properties[:update_only],
155
136
  properties[:use_ar_object],
156
- properties[:targeted],
157
137
  properties[:assert_graph_integrity])
158
138
 
159
139
  init_strategies(properties[:strategy],
160
- properties[:saver_strategy],
161
- properties[:retention_strategy],
162
- properties[:delete_method])
140
+ properties[:retention_strategy])
163
141
 
164
142
  init_references(properties[:manager_ref],
165
143
  properties[:manager_ref_allowed_nil],
166
- properties[:secondary_refs],
167
- properties[:manager_uuids])
144
+ properties[:secondary_refs])
168
145
 
169
- init_all_manager_uuids(properties[:all_manager_uuids],
170
- properties[:all_manager_uuids_scope],
171
- properties[:all_manager_uuids_timestamp])
146
+ init_ic_relations(properties[:dependency_attributes])
172
147
 
173
- init_ic_relations(properties[:dependency_attributes],
174
- properties[:parent_inventory_collections])
175
-
176
- init_arels(properties[:arel],
177
- properties[:targeted_arel])
148
+ init_arels(properties[:arel])
178
149
 
179
150
  init_custom_procs(properties[:custom_save_block],
180
151
  properties[:custom_reconnect_block])
@@ -311,6 +282,11 @@ module InventoryRefresh
311
282
  @base_columns ||= (unique_index_columns + internal_columns + not_null_columns).uniq
312
283
  end
313
284
 
285
+ # @return [Array<Symbol>] Array of all column names on the InventoryCollection
286
+ def all_column_names
287
+ @all_column_names ||= model_class.columns.map { |x| x.name.to_sym }
288
+ end
289
+
314
290
  # @param value [Object] Object we want to test
315
291
  # @return [Boolean] true is value is kind of InventoryRefresh::InventoryObject
316
292
  def inventory_object?(value)
@@ -335,7 +311,7 @@ module InventoryRefresh
335
311
 
336
312
  # Convert manager_ref list of attributes to list of DB columns
337
313
  #
338
- # @return [Array<String>] true is processing of this InventoryCollection will be in targeted mode
314
+ # @return [Array<String>] Converted manager_ref list of attributes to list of DB columns
339
315
  def manager_ref_to_cols
340
316
  # TODO(lsmola) this should contain the polymorphic _type, otherwise the IC with polymorphic unique key will get
341
317
  # conflicts
@@ -434,7 +410,6 @@ module InventoryRefresh
434
410
  :parent => parent,
435
411
  :arel => arel,
436
412
  :strategy => strategy,
437
- :saver_strategy => saver_strategy,
438
413
  :custom_save_block => custom_save_block,
439
414
  # We want cloned IC to be update only, since this is used for cycle resolution
440
415
  :update_only => true,
@@ -472,78 +447,31 @@ module InventoryRefresh
472
447
 
473
448
  # @return [Integer] default batch size for talking to the DB
474
449
  def batch_size
475
- # TODO(lsmola) mode to the settings
476
450
  1000
477
451
  end
478
452
 
479
453
  # @return [Integer] default batch size for talking to the DB if not using ApplicationRecord objects
480
454
  def batch_size_pure_sql
481
- # TODO(lsmola) mode to the settings
482
455
  10_000
483
456
  end
484
457
 
485
- # Returns a list of stringified uuids of all scoped InventoryObjects, which is used for scoping in targeted mode
486
- #
487
- # @return [Array<String>] list of stringified uuids of all scoped InventoryObjects
488
- def manager_uuids
489
- # TODO(lsmola) LEGACY: this is still being used by :targetel_arel definitions and it expects array of strings
490
- raise "This works only for :manager_ref size 1" if manager_ref.size > 1
491
- key = manager_ref.first
492
- transform_references_to_hashes(targeted_scope.primary_references).map { |x| x[key] }
493
- end
494
-
495
458
  # Builds a multiselection conditions like (table1.a = a1 AND table2.b = b1) OR (table1.a = a2 AND table2.b = b2)
496
459
  #
497
460
  # @param hashes [Array<Hash>] data we want to use for the query
498
461
  # @param keys [Array<Symbol>] keys of attributes involved
499
462
  # @return [String] A condition usable in .where of an ActiveRecord relation
500
- def build_multi_selection_condition(hashes, keys = manager_ref)
463
+ def build_multi_selection_condition(hashes, keys = unique_index_keys)
501
464
  arel_table = model_class.arel_table
502
465
  # We do pure SQL OR, since Arel is nesting every .or into another parentheses, otherwise this would be just
503
466
  # inject(:or) instead of to_sql with .join(" OR ")
504
467
  hashes.map { |hash| "(#{keys.map { |key| arel_table[key].eq(hash[key]) }.inject(:and).to_sql})" }.join(" OR ")
505
468
  end
506
469
 
507
- # @return [ActiveRecord::Relation] A relation that can fetch all data of this InventoryCollection from the DB
508
- def db_collection_for_comparison
509
- if targeted?
510
- if targeted_arel.respond_to?(:call)
511
- targeted_arel.call(self)
512
- elsif parent_inventory_collections.present?
513
- targeted_arel_default
514
- else
515
- targeted_iterator_for(targeted_scope.primary_references)
516
- end
517
- else
518
- full_collection_for_comparison
519
- end
520
- end
521
-
522
- # Builds targeted query limiting the results by the :references defined in parent_inventory_collections
523
- #
524
- # @return [InventoryRefresh::ApplicationRecordIterator] an iterator for default targeted arel
525
- def targeted_arel_default
526
- if parent_inventory_collections.collect { |x| x.model_class.base_class }.uniq.count > 1
527
- raise "Multiple :parent_inventory_collections with different base class are not supported by default. Write "\
528
- ":targeted_arel manually, or separate [#{self}] into 2 InventoryCollection objects."
529
- end
530
- parent_collection = parent_inventory_collections.first
531
- references = parent_inventory_collections.map { |x| x.targeted_scope.primary_references }.reduce({}, :merge!)
532
-
533
- parent_collection.targeted_iterator_for(references, full_collection_for_comparison)
534
- end
535
-
536
- # Gets targeted references and transforms them into list of hashes
470
+ # Returns iterator for the passed references and a query
537
471
  #
538
- # @param references [Array, InventoryRefresh::InventoryCollection::TargetedScope] passed references
539
- # @return [Array<Hash>] References transformed into the array of hashes
540
- def transform_references_to_hashes(references)
541
- if references.kind_of?(Array)
542
- # Sliced InventoryRefresh::InventoryCollection::TargetedScope
543
- references.map { |x| x.second.full_reference }
544
- else
545
- references.values.map(&:full_reference)
546
- end
472
+ # @return [InventoryRefresh::ApplicationRecordIterator] Iterator for the references and query
473
+ def db_collection_for_comparison
474
+ InventoryRefresh::ApplicationRecordIterator.new(:inventory_collection => self)
547
475
  end
548
476
 
549
477
  # Builds a multiselection conditions like (table1.a = a1 AND table2.b = b1) OR (table1.a = a2 AND table2.b = b2)
@@ -552,20 +480,20 @@ module InventoryRefresh
552
480
  # @param references [Hash{String => InventoryRefresh::InventoryCollection::Reference}] passed references
553
481
  # @return [String] A condition usable in .where of an ActiveRecord relation
554
482
  def targeted_selection_for(references)
555
- build_multi_selection_condition(transform_references_to_hashes(references))
483
+ build_multi_selection_condition(references.map(&:second))
556
484
  end
557
485
 
558
- # Returns iterator for the passed references and a query
559
- #
560
- # @param references [Hash{String => InventoryRefresh::InventoryCollection::Reference}] Passed references
561
- # @param query [ActiveRecord::Relation] relation that can fetch all data of this InventoryCollection from the DB
562
- # @return [InventoryRefresh::ApplicationRecordIterator] Iterator for the references and query
563
- def targeted_iterator_for(references, query = nil)
564
- InventoryRefresh::ApplicationRecordIterator.new(
565
- :inventory_collection => self,
566
- :manager_uuids_set => references,
567
- :query => query
568
- )
486
+ def select_keys
487
+ @select_keys ||= [@model_class.primary_key] + manager_ref_to_cols.map(&:to_s) + internal_columns.map(&:to_s)
488
+ end
489
+
490
+ # @return [ActiveRecord::ConnectionAdapters::AbstractAdapter] ActiveRecord connection
491
+ def get_connection
492
+ ActiveRecord::Base.connection
493
+ end
494
+
495
+ def pure_sql_record_fetching?
496
+ !use_ar_object?
569
497
  end
570
498
 
571
499
  # Builds an ActiveRecord::Relation that can fetch all the references from the DB
@@ -573,14 +501,18 @@ module InventoryRefresh
573
501
  # @param references [Hash{String => InventoryRefresh::InventoryCollection::Reference}] passed references
574
502
  # @return [ActiveRecord::Relation] relation that can fetch all the references from the DB
575
503
  def db_collection_for_comparison_for(references)
576
- full_collection_for_comparison.where(targeted_selection_for(references))
504
+ query = full_collection_for_comparison.where(targeted_selection_for(references))
505
+ if pure_sql_record_fetching?
506
+ return get_connection.query(query.select(*select_keys).to_sql)
507
+ end
508
+
509
+ query
577
510
  end
578
511
 
579
512
  # @return [ActiveRecord::Relation] relation that can fetch all the references from the DB
580
513
  def full_collection_for_comparison
581
514
  return arel unless arel.nil?
582
515
  rel = parent.send(association)
583
- rel = rel.active if rel && supports_column?(:archived_at) && retention_strategy == :archive
584
516
  rel
585
517
  end
586
518
 
@@ -634,11 +566,5 @@ module InventoryRefresh
634
566
  :id => identity
635
567
  }
636
568
  end
637
-
638
- # TODO: Not used!
639
- # @return [Array<Symbol>] all association attributes and foreign keys of the model class
640
- def association_attributes
641
- model_class.reflect_on_all_associations.map { |x| [x.name, x.foreign_key] }.flatten.compact.map(&:to_sym)
642
- end
643
569
  end
644
570
  end
@@ -4,16 +4,16 @@ module InventoryRefresh
4
4
  class MissingModelClassError < StandardError; end
5
5
 
6
6
  def self.allowed_properties
7
- %i(all_manager_uuids arel association
7
+ %i(arel association
8
8
  attributes_blacklist attributes_whitelist batch_extra_attributes
9
9
  complete create_only custom_save_block
10
- custom_reconnect_block default_values delete_method
10
+ custom_reconnect_block default_values
11
11
  dependency_attributes check_changed inventory_object_attributes
12
- manager_ref manager_ref_allowed_nil manager_uuids
12
+ manager_ref manager_ref_allowed_nil
13
13
  model_class name parent
14
- parent_inventory_collections retention_strategy strategy
15
- saver_strategy secondary_refs targeted
16
- targeted_arel update_only use_ar_object
14
+ retention_strategy strategy
15
+ secondary_refs
16
+ update_only use_ar_object
17
17
  assert_graph_integrity).to_set
18
18
  end
19
19
 
@@ -70,15 +70,6 @@ module InventoryRefresh
70
70
  build(hash)
71
71
  end
72
72
 
73
- # Finds InventoryObject.
74
- #
75
- # @param hash [Hash] Hash that needs to contain attributes defined in :manager_ref of the InventoryCollection
76
- # @return [InventoryRefresh::InventoryObject] Found or built InventoryObject object
77
- def find_in_data(hash)
78
- _hash, _uuid, inventory_object = primary_index_scan(hash)
79
- inventory_object
80
- end
81
-
82
73
  # Finds of builds a new InventoryObject. By building it, we also put in into the InventoryCollection's storage.
83
74
  #
84
75
  # @param hash [Hash] Hash that needs to contain attributes defined in :manager_ref of the
@@ -35,26 +35,14 @@ module InventoryRefresh
35
35
  # - :local_db_find_missing_references => InventoryObject objects of the InventoryCollection will be saved to
36
36
  # the DB. Then if we reference an object that is not present, it will
37
37
  # load them from the db using :local_db_find_references strategy.
38
- # @param saver_strategy [Symbol] A strategy that will be used for InventoryCollection persisting into the DB.
39
- # Allowed saver strategies are:
40
- # - :default => Using Rails saving methods, this way is not safe to run in multiple workers concurrently,
41
- # since it will lead to non consistent data.
42
- # - :batch => Using batch SQL queries, this way is not safe to run in multiple workers
43
- # concurrently, since it will lead to non consistent data.
44
- # - :concurrent_safe_batch => It uses atomic upsert to avoid data duplication and it uses timestamp based
45
- # atomic checks to avoid new data being overwritten by the the old data. The upsert/update queries are
46
- # executed as batched SQL queries, instead of sending 1 query per record.
47
38
  # @param retention_strategy [Symbol] A retention strategy for this collection. Allowed values are:
48
39
  # - :destroy => Will destroy the inactive records.
49
40
  # - :archive => Will archive the inactive records by setting :archived_at timestamp.
50
- # @param delete_method [Symbol] A delete method that will be used for deleting of the InventoryObject, if the
51
- # object is marked for deletion. A default is :destroy, the instance method must be defined on the
52
- # :model_class.
53
- def init_strategies(strategy, saver_strategy, retention_strategy, delete_method)
54
- @saver_strategy = process_saver_strategy(saver_strategy)
41
+ def init_strategies(strategy, retention_strategy)
42
+ @saver_strategy = :concurrent_safe_batch
55
43
  @strategy = process_strategy(strategy)
44
+ # TODO(lsmola) why don't we just set this strategy based on :archived_at column? Lets do that
56
45
  @retention_strategy = process_retention_strategy(retention_strategy)
57
- @delete_method = delete_method || :destroy
58
46
  end
59
47
 
60
48
  # @param manager_ref [Array] Array of Symbols, that are keys of the InventoryObject's data, inserted into this
@@ -67,42 +55,10 @@ module InventoryRefresh
67
55
  # avoid this usecase by a proper modeling.
68
56
  # Note that InventoryObject's data has to be build with <foreign_key> => nil, it means that key cannot be missing!
69
57
  # @param secondary_refs [Hash] TODO
70
- # @param manager_uuids [Array|Proc] Array of manager_uuids of the InventoryObjects we want to create/update/delete. Using
71
- # this attribute, the db_collection_for_comparison will be automatically limited by the manager_uuids, in a
72
- # case of a simple relation. In a case of a complex relation, we can leverage :manager_uuids in a
73
- # custom :targeted_arel. We can pass also lambda, for lazy_evaluation.
74
- def init_references(manager_ref, manager_ref_allowed_nil, secondary_refs, manager_uuids)
58
+ def init_references(manager_ref, manager_ref_allowed_nil, secondary_refs)
75
59
  @manager_ref = manager_ref || %i(ems_ref)
76
60
  @manager_ref_allowed_nil = manager_ref_allowed_nil || []
77
61
  @secondary_refs = secondary_refs || {}
78
- @manager_uuids = manager_uuids || []
79
- end
80
-
81
- # @param all_manager_uuids [Array] Array of all manager_uuids of the InventoryObjects. With the :targeted true,
82
- # having this parameter defined will invoke only :delete_method on a complement of this set, making sure
83
- # the DB has only this set of data after. This :attribute serves for deleting of top level
84
- # InventoryCollections, i.e. InventoryCollections having parent_inventory_collections nil. The deleting of
85
- # child collections is already handled by the scope of the parent_inventory_collections and using Rails
86
- # :dependent => :destroy,
87
- # @param all_manager_uuids_scope [Array] A scope limiting the :all_manager_uuids parameter. E.g. we can send
88
- # all_manager_uuids for 1 region, leading to delete a complement of the entities just under that 1
89
- # region.
90
- # If all_manager_uuids_scope is used with :all_manager_uuids => nil, it will do delete_complement of the
91
- # scope itself. E.g. sending a list of all active regions, we will delete complement entities not
92
- # belonging to those regions.
93
- # Example:
94
- # :all_manager_uuids => [{:source_ref => x}, {:source_ref => y}],
95
- # :all_manager_uuids_scope => [{:region => regions.lazy_find(X)}, {:region => regions.lazy_find(Y)}]
96
- #
97
- # Will cause deletion/archival or all entities that don't have source_ref "x" or "y", but only under
98
- # regions X and Y.
99
- # @param all_manager_uuids_timestamp [String] A timestamp in UTC marking a time before we collected all of the
100
- # all_manager_uuids. Meaning we won't be archiving any newer entities.
101
- def init_all_manager_uuids(all_manager_uuids, all_manager_uuids_scope, all_manager_uuids_timestamp)
102
- # TODO(lsmola) Should we refactor this to use references too?
103
- @all_manager_uuids = all_manager_uuids
104
- @all_manager_uuids_scope = all_manager_uuids_scope
105
- @all_manager_uuids_timestamp = all_manager_uuids_timestamp
106
62
  end
107
63
 
108
64
  # @param dependency_attributes [Hash] Manually defined dependencies of this InventoryCollection. We can use this
@@ -119,48 +75,9 @@ module InventoryRefresh
119
75
  # This example is used in Example2 of the <param custom_save_block> and it means that our :custom_save_block
120
76
  # will be invoked after the InventoryCollection :orchestration_stacks and :orchestration_stacks_resources
121
77
  # are saved.
122
- # @param parent_inventory_collections [Array] Array of symbols having a name pointing to the
123
- # InventoryRefresh::InventoryCollection objects, that serve as parents to this InventoryCollection. There are
124
- # several scenarios to consider, when deciding if InventoryCollection has parent collections, see the example.
125
- #
126
- # Example:
127
- # taking inventory collections :vms and :disks (local disks), if we write that:
128
- # inventory_collection = InventoryCollection.new({
129
- # :model_class => ::Disk,
130
- # :association => :disks,
131
- # :manager_ref => [:vm, :location]
132
- # :parent_inventory_collection => [:vms],
133
- # })
134
- #
135
- # Then the decision for having :parent_inventory_collection => [:vms] was probably driven by these
136
- # points:
137
- # 1. We can get list of all disks only by doing SQL query through the parent object (so there will be join
138
- # from vms to disks table).
139
- # 2. There is no API query for getting all disks from the provider API, we get them inside VM data, or as
140
- # a Vm subquery
141
- # 3. Part of the manager_ref of the IC is the VM object (foreign key), so the disk's location is unique
142
- # only under 1 Vm. (In current models, this modeled going through Hardware model)
143
- # 4. In targeted refresh, we always expect that each Vm will be saved with all its disks.
144
- #
145
- # Then having the above points, adding :parent_inventory_collection => [:vms], will bring these
146
- # implications:
147
- # 1. By archiving/deleting Vm, we can no longer see the disk, because those were owned by the Vm. Any
148
- # archival/deletion of the Disk model, must be then done by cascade delete/hooks logic.
149
- # 2. Having Vm as a parent ensures we always process it first. So e.g. when providing no Vms for saving
150
- # we would have no graph dependency (no data --> no edges --> no dependencies) and Disk could be
151
- # archived/removed before the Vm, while we always want to archive the VM first.
152
- # 3. For targeted refresh, we always expect that all disks are saved with a VM. So for targeting :disks,
153
- # we are not using #manager_uuids attribute, since the scope is "all disks of all targeted VMs", so we
154
- # always use #manager_uuids of the parent. (that is why :parent_inventory_collections and
155
- # :manager_uuids are mutually exclusive attributes)
156
- # 4. For automatically building the #targeted_arel query, we need the parent to know what is the root node.
157
- # While this information can be introspected from the data, it creates a scope for create&update&delete,
158
- # which means it has to work with no data provided (causing delete all). So with no data we cannot
159
- # introspect anything.
160
- def init_ic_relations(dependency_attributes, parent_inventory_collections = nil)
161
- @dependency_attributes = dependency_attributes || {}
162
- @dependees = Set.new
163
- @parent_inventory_collections = parent_inventory_collections
78
+ def init_ic_relations(dependency_attributes)
79
+ @dependency_attributes = dependency_attributes || {}
80
+ @dependees = Set.new
164
81
  end
165
82
 
166
83
  # @param complete [Boolean] By default true, :complete is marking we are sending a complete dataset and therefore
@@ -175,18 +92,13 @@ module InventoryRefresh
175
92
  # @param use_ar_object [Boolean] True or False. Whether we need to initialize AR object as part of the saving
176
93
  # it's needed if the model have special setters, serialize of columns, etc. This setting is relevant only
177
94
  # for the batch saver strategy.
178
- # @param targeted [Boolean] True if the collection is targeted, in that case it will be leveraging :manager_uuids
179
- # :parent_inventory_collections and :targeted_arel to save a subgraph of a data.
180
- def init_flags(complete, create_only, check_changed,
181
- update_only, use_ar_object, targeted,
182
- assert_graph_integrity)
95
+ def init_flags(complete, create_only, check_changed, update_only, use_ar_object, assert_graph_integrity)
183
96
  @complete = complete.nil? ? true : complete
184
97
  @create_only = create_only.nil? ? false : create_only
185
98
  @check_changed = check_changed.nil? ? true : check_changed
186
99
  @saved = false
187
100
  @update_only = update_only.nil? ? false : update_only
188
101
  @use_ar_object = use_ar_object || false
189
- @targeted = !!targeted
190
102
  @assert_graph_integrity = assert_graph_integrity.nil? ? true : assert_graph_integrity
191
103
  end
192
104
 
@@ -230,7 +142,7 @@ module InventoryRefresh
230
142
  @attributes_whitelist = Set.new
231
143
  @batch_extra_attributes = batch_extra_attributes || []
232
144
  @inventory_object_attributes = inventory_object_attributes
233
- @internal_attributes = %i(__feedback_edge_set_parent __parent_inventory_collections __all_manager_uuids_scope)
145
+ @internal_attributes = %i(__feedback_edge_set_parent)
234
146
  @transitive_dependency_attributes = Set.new
235
147
 
236
148
  blacklist_attributes!(attributes_blacklist) if attributes_blacklist.present?
@@ -240,7 +152,6 @@ module InventoryRefresh
240
152
  def init_storages
241
153
  @data_storage = ::InventoryRefresh::InventoryCollection::DataStorage.new(self, @secondary_refs)
242
154
  @references_storage = ::InventoryRefresh::InventoryCollection::ReferencesStorage.new(index_proxy)
243
- @targeted_scope = ::InventoryRefresh::InventoryCollection::ReferencesStorage.new(index_proxy).merge!(@manager_uuids)
244
155
  end
245
156
 
246
157
  # @param arel [ActiveRecord::Associations::CollectionProxy|Arel::SelectManager] Instead of :parent and :association
@@ -248,35 +159,18 @@ module InventoryRefresh
248
159
  # doing create/update/delete.
249
160
  #
250
161
  # Example:
251
- # for a targeted refresh, we want to delete/update/create only a list of vms specified with a list of
252
- # ems_refs:
253
- # :arel => manager.vms.where(:ems_ref => manager_refs)
254
- # Then we want to do the same for the hardwares of only those vms:
255
- # :arel => manager.hardwares.joins(:vm_or_template).where(
256
- # 'vms' => {:ems_ref => manager_refs}
257
- # )
258
- # And etc. for the other Vm related records.
259
- # @param targeted_arel [Proc] A callable block that receives this InventoryCollection as a first argument. In there
260
- # we can leverage a :parent_inventory_collections or :manager_uuids to limit the query based on the
261
- # manager_uuids available.
262
- # Example:
263
- # targeted_arel = lambda do |inventory_collection|
264
- # # Getting ems_refs of parent :vms and :miq_templates
265
- # manager_uuids = inventory_collection.parent_inventory_collections.collect(&:manager_uuids).flatten
266
- # inventory_collection.db_collection_for_comparison.hardwares.joins(:vm_or_template).where(
267
- # 'vms' => {:ems_ref => manager_uuids}
162
+ # add_collection(:cross_link_vms) do |builder|
163
+ # builder.add_properties(
164
+ # :arel => Vm.where(:tenant => manager.tenant),
165
+ # :association => nil,
166
+ # :model_class => Vm,
167
+ # :name => :cross_link_vms,
168
+ # :manager_ref => [:uid_ems],
169
+ # :strategy => :local_db_find_references,
268
170
  # )
269
171
  # end
270
- #
271
- # inventory_collection = InventoryCollection.new({
272
- # :model_class => ::Hardware,
273
- # :association => :hardwares,
274
- # :parent_inventory_collection => [:vms, :miq_templates],
275
- # :targeted_arel => targeted_arel,
276
- # })
277
- def init_arels(arel, targeted_arel)
278
- @arel = arel
279
- @targeted_arel = targeted_arel
172
+ def init_arels(arel)
173
+ @arel = arel
280
174
  end
281
175
 
282
176
  # @param custom_save_block [Proc] A custom lambda/proc for persisting in the DB, for cases where it's not enough
@@ -392,23 +286,6 @@ module InventoryRefresh
392
286
  @deleted_records = []
393
287
  end
394
288
 
395
- # Processes passed saver strategy
396
- #
397
- # @param saver_strategy [Symbol] Passed saver strategy
398
- # @return [Symbol] Returns back the passed strategy if supported, or raises exception
399
- def process_saver_strategy(saver_strategy)
400
- return :default unless saver_strategy
401
-
402
- saver_strategy = saver_strategy.to_sym
403
- case saver_strategy
404
- when :default, :batch, :concurrent_safe_batch
405
- saver_strategy
406
- else
407
- raise "Unknown InventoryCollection saver strategy: :#{saver_strategy}, allowed strategies are "\
408
- ":default, :batch and :concurrent_safe_batch"
409
- end
410
- end
411
-
412
289
  # Processes passed strategy, modifies :data_collection_finalized and :saved attributes for db only strategies
413
290
  #
414
291
  # @param strategy_name [Symbol] Passed saver strategy
@@ -434,12 +311,26 @@ module InventoryRefresh
434
311
  strategy_name
435
312
  end
436
313
 
314
+ # Saves passed strategy, modifies :data_collection_finalized and :saved attributes for db only strategies
315
+ #
316
+ # @param strategy [Symbol] Passed saver strategy
317
+ # @return [Symbol] Returns back the passed strategy if supported, or raises exception
318
+ def strategy=(strategy)
319
+ @strategy = process_strategy(strategy)
320
+ end
321
+
437
322
  # Processes passed retention strategy
438
323
  #
439
324
  # @param retention_strategy [Symbol] Passed retention strategy
440
325
  # @return [Symbol] Returns back the passed strategy if supported, or raises exception
441
326
  def process_retention_strategy(retention_strategy)
442
- return unless retention_strategy
327
+ unless retention_strategy
328
+ if supports_column?(:archived_at)
329
+ return :archive
330
+ else
331
+ return :destroy
332
+ end
333
+ end
443
334
 
444
335
  retention_strategy = retention_strategy.to_sym
445
336
  case retention_strategy