diff --git a/app/models/manager_refresh/save_collection/saver/base.rb b/app/models/manager_refresh/save_collection/saver/base.rb index 4e56cada7ce..160ed3fde6f 100644 --- a/app/models/manager_refresh/save_collection/saver/base.rb +++ b/app/models/manager_refresh/save_collection/saver/base.rb @@ -250,26 +250,39 @@ def assign_attributes_for_create!(hash, create_time) assign_attributes_for_update!(hash, create_time) end - def unique_index_columns - return @unique_index_columns if @unique_index_columns + def unique_indexes + @unique_indexes_cache if @unique_indexes_cache - if model_class.respond_to?(:manager_refresh_unique_index_columns) - return @unique_index_columns = model_class.manager_refresh_unique_index_columns - end - - unique_indexes = model_class.connection.indexes(model_class.table_name).select(&:unique) - if unique_indexes.count > 1 - raise "Cannot infer unique index automatically, since the table #{model_class.table_name}"\ - " of the #{inventory_collection} contains more than 1 unique index: '#{unique_indexes.collect(&:name)}'."\ - " Please define the unique index columns explicitly on a model as a class method"\ - " self.manager_refresh_unique_index_columns returning [:column1, :column2, etc.]" - end + @unique_indexes_cache = model_class.connection.indexes(model_class.table_name).select(&:unique) - if unique_indexes.blank? + if @unique_indexes_cache.blank? raise "#{inventory_collection} and its table #{model_class.table_name} must have a unique index defined, to"\ " be able to use saver_strategy :concurrent_safe or :concurrent_safe_batch." end - @unique_index_columns = unique_indexes.first.columns.map(&:to_sym) + + @unique_indexes_cache + end + + def unique_index_for(keys) + @unique_index_for_keys_cache ||= {} + @unique_index_for_keys_cache[keys] if @unique_index_for_keys_cache[keys] + + # Find all uniq indexes that that are covering our keys + uniq_key_candidates = unique_indexes.each_with_object([]) { |i, obj| obj << i if (keys - i.columns.map(&:to_sym)).empty? } + + if @unique_indexes_cache.blank? + raise "#{inventory_collection} and its table #{model_class.table_name} must have a unique index defined "\ + "covering columns #{keys} to be able to use saver_strategy :concurrent_safe or :concurrent_safe_batch." + end + + # Take the uniq key having the least number of columns + @unique_index_for_keys_cache[keys] = uniq_key_candidates.min_by { |x| x.columns.count } + end + + def unique_index_columns + return @unique_index_columns if @unique_index_columns + + @unique_index_columns = unique_index_for(unique_index_keys).columns.map(&:to_sym) end def supports_sti? diff --git a/app/models/manager_refresh/save_collection/saver/sql_helper.rb b/app/models/manager_refresh/save_collection/saver/sql_helper.rb index 796aaba166a..f009071b7a8 100644 --- a/app/models/manager_refresh/save_collection/saver/sql_helper.rb +++ b/app/models/manager_refresh/save_collection/saver/sql_helper.rb @@ -34,8 +34,11 @@ def build_insert_query(all_attribute_keys, hashes, on_conflict: nil) ON CONFLICT DO NOTHING } elsif on_conflict_update + index_where_condition = unique_index_for(unique_index_keys).where + where_to_sql = index_where_condition ? "WHERE #{index_where_condition}" : "" + insert_query += %{ - ON CONFLICT (#{unique_index_columns.map { |x| quote_column_name(x) }.join(",")}) + ON CONFLICT (#{unique_index_columns.map { |x| quote_column_name(x) }.join(",")}) #{where_to_sql} DO UPDATE SET #{all_attribute_keys_array.map { |key| build_insert_set_cols(key) }.join(", ")} diff --git a/app/models/vm_or_template.rb b/app/models/vm_or_template.rb index dedb1dcbd7d..789849dd4a1 100644 --- a/app/models/vm_or_template.rb +++ b/app/models/vm_or_template.rb @@ -294,10 +294,6 @@ def self.model_suffix manager_class.short_token end - def self.manager_refresh_unique_index_columns - [:ems_id, :ems_ref] - end - def to_s name end