inventory_refresh 0.2.2 → 0.3.0

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 +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