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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +0 -1
- data/.travis.yml +6 -8
- data/inventory_refresh.gemspec +2 -4
- data/lib/inventory_refresh.rb +0 -2
- data/lib/inventory_refresh/application_record_iterator.rb +9 -26
- data/lib/inventory_refresh/exception.rb +8 -0
- data/lib/inventory_refresh/inventory_collection.rb +36 -110
- data/lib/inventory_refresh/inventory_collection/builder.rb +6 -6
- data/lib/inventory_refresh/inventory_collection/data_storage.rb +0 -9
- data/lib/inventory_refresh/inventory_collection/helpers/initialize_helper.rb +34 -143
- data/lib/inventory_refresh/inventory_collection/helpers/questions_helper.rb +1 -44
- data/lib/inventory_refresh/inventory_collection/index/proxy.rb +6 -34
- data/lib/inventory_refresh/inventory_collection/index/type/base.rb +0 -8
- data/lib/inventory_refresh/inventory_collection/references_storage.rb +0 -17
- data/lib/inventory_refresh/inventory_collection/scanner.rb +1 -87
- data/lib/inventory_refresh/inventory_collection/serialization.rb +10 -16
- data/lib/inventory_refresh/inventory_object.rb +34 -68
- data/lib/inventory_refresh/inventory_object_lazy.rb +10 -17
- data/lib/inventory_refresh/persister.rb +63 -29
- data/lib/inventory_refresh/save_collection/base.rb +2 -4
- data/lib/inventory_refresh/save_collection/saver/base.rb +8 -108
- data/lib/inventory_refresh/save_collection/saver/concurrent_safe_batch.rb +48 -126
- data/lib/inventory_refresh/save_collection/saver/partial_upsert_helper.rb +19 -1
- data/lib/inventory_refresh/save_collection/saver/retention_helper.rb +3 -68
- data/lib/inventory_refresh/save_collection/saver/sql_helper.rb +0 -125
- data/lib/inventory_refresh/save_collection/saver/sql_helper_update.rb +5 -9
- data/lib/inventory_refresh/save_collection/saver/sql_helper_upsert.rb +9 -17
- data/lib/inventory_refresh/save_collection/sweeper.rb +91 -18
- data/lib/inventory_refresh/save_collection/topological_sort.rb +5 -5
- data/lib/inventory_refresh/save_inventory.rb +12 -5
- data/lib/inventory_refresh/version.rb +1 -1
- metadata +9 -45
- data/lib/inventory_refresh/save_collection/saver/batch.rb +0 -17
- data/lib/inventory_refresh/save_collection/saver/default.rb +0 -57
- data/lib/inventory_refresh/target.rb +0 -73
- data/lib/inventory_refresh/target_collection.rb +0 -92
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 666554cf2d3928670cd6772706360d2c2523fc0475b63f2aead1e7e2bec675d7
|
4
|
+
data.tar.gz: 992aa863fdd2d020878f3bd18e0c83d7bf37aa79d6363814e10fdc5cd311a35a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ce760c31989eaa2ec3d230031c176007953b9d5790d7d5e0c85d7b62e43f6d7d45cbe88651a70df3ca77c11f4d7b5587348e2ba0d951add5d8d76a90ba9d0e0
|
7
|
+
data.tar.gz: 4084a11ccd8ab92142e44399159290d5b14be05aa111f5c681571c6ba034a4ecb55587f0d3c2dda9d0ef765e4c5aefa9f2ea9e4183c27b81d4547b3719ae8393
|
data/.codeclimate.yml
CHANGED
data/.travis.yml
CHANGED
@@ -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.
|
7
|
-
- 2.6
|
6
|
+
- 2.4.4
|
7
|
+
- 2.3.6
|
8
8
|
addons:
|
9
|
-
postgresql: '
|
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
|
-
|
18
|
-
|
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"
|
data/inventory_refresh.gemspec
CHANGED
@@ -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", "
|
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
|
data/lib/inventory_refresh.rb
CHANGED
@@ -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
|
3
|
+
attr_reader :inventory_collection
|
4
4
|
|
5
|
-
# An iterator that can fetch batches of the AR objects based on a set of
|
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
|
-
|
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
|
-
|
31
|
-
|
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, :
|
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, :
|
99
|
-
:
|
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[:
|
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
|
-
|
170
|
-
properties[:all_manager_uuids_scope],
|
171
|
-
properties[:all_manager_uuids_timestamp])
|
146
|
+
init_ic_relations(properties[:dependency_attributes])
|
172
147
|
|
173
|
-
|
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>]
|
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 =
|
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
|
-
#
|
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
|
-
# @
|
539
|
-
|
540
|
-
|
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(
|
483
|
+
build_multi_selection_condition(references.map(&:second))
|
556
484
|
end
|
557
485
|
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
# @return [
|
563
|
-
def
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
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(
|
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
|
10
|
+
custom_reconnect_block default_values
|
11
11
|
dependency_attributes check_changed inventory_object_attributes
|
12
|
-
manager_ref manager_ref_allowed_nil
|
12
|
+
manager_ref manager_ref_allowed_nil
|
13
13
|
model_class name parent
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
51
|
-
|
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
|
-
|
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
|
-
|
123
|
-
|
124
|
-
|
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
|
-
|
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
|
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
|
-
#
|
252
|
-
#
|
253
|
-
#
|
254
|
-
#
|
255
|
-
#
|
256
|
-
#
|
257
|
-
#
|
258
|
-
#
|
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
|
-
|
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
|
-
|
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
|