automatically re-index journals

We should now be able to address all inconsistencies that occured before introduction of our new index
pull/3263/head
Jan Sandbrink 9 years ago
parent a21335db36
commit 5c5b1c3c46
  1. 64
      db/migrate/20150716133712_add_unique_index_on_journals.rb

@ -26,6 +26,16 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
# Adds a unique index over three columns of the journals table that should form a composite key.
# Because we know that production data will not fit that assumption, we perform an automatic cleanup
# before adding the Index:
# 1. remove perfectly identical duplicates:
# Those may appear by clicking multiple times on the submit button of a form
# 2. issue new version numbers for duplicates with different content
# May arise due to quick automated updates (e.g. bots posting comments)
# In general we learned that up until now OpenProject was not very good at handling concurrent
# updates to journables. By adding the index we ensure data consistency, but at the same time
# provoke errors in those update scenarios.
class AddUniqueIndexOnJournals < ActiveRecord::Migration
def up
cleanup_duplicate_journals
@ -43,8 +53,9 @@ class AddUniqueIndexOnJournals < ActiveRecord::Migration
if duplicate_pairs.any?
say "Found #{duplicate_pairs.count} journals with at least one duplicate!"
inconsistent_journables = []
say_with_time 'Safely removing duplicates...' do
undeleted_pairs = []
duplicate_pairs.each do |current_id, duplicate_id|
sub_say "Comparing journals ##{current_id} & ##{duplicate_id} for equality"
@ -55,12 +66,12 @@ class AddUniqueIndexOnJournals < ActiveRecord::Migration
sub_say "Deleting journal ##{current.id}..."
current.destroy
else
undeleted_pairs << [current_id, duplicate_id]
inconsistent_journables << [current.journable_type, current.journable_id]
end
end
abort_on_undeleted_pairs undeleted_pairs
end
reorder_journals_for inconsistent_journables
end
end
@ -77,6 +88,24 @@ class AddUniqueIndexOnJournals < ActiveRecord::Migration
.map { |pair| [pair.id, pair.duplicate_id] }
end
def reorder_journals_for(journables)
if journables.any?
say_with_time 'Reordering journables with duplicate journals...' do
journables.uniq!
journables.each do |type, id|
sub_say "Reordering journals for #{type} ##{id}"
journals = Journal
.where(journable_type: type, journable_id: id)
.order('version ASC, created_at ASC')
journals.each_with_index do |journal, index|
version = index + 1
Journal.where(id: journal.id).update_all(version: version)
end
end
end
end
end
def journals_equivalent?(a, b)
base_journals_equivalent?(a, b) &&
specific_journals_equivalent?(a, b) &&
@ -128,33 +157,6 @@ class AddUniqueIndexOnJournals < ActiveRecord::Migration
}
end
def abort_on_undeleted_pairs(undeleted_pairs)
return unless undeleted_pairs.any?
sub_say ''
sub_say 'There were journals that had a duplicate, but were not deleted.'
sub_say 'You have to manually decide how to proceed with these journals.'
sub_say 'Please compare the corresponding entries in the following tables:'
sub_say ' * journals'
sub_say ' * attachable_journals'
sub_say ' * customizable_journals'
sub_say ' * {type}_journals, with {type} being indicated by the journable_type'
sub_say ''
sub_say 'The following table lists the remaining duplicate pairs,'
sub_say 'note that only one entry per pair is supposed to be deleted:'
column_width = 20
sub_say '-' * (column_width * 2 + 7)
sub_say "| #{'journal 1'.rjust(column_width)} | #{'journal 2'.rjust(column_width)} |"
sub_say '-' * (column_width * 2 + 7)
undeleted_pairs.each do |dup_a, dup_b|
sub_say "| #{dup_a.to_s.rjust(column_width)} | #{dup_b.to_s.rjust(column_width)} |"
end
sub_say '-' * (column_width * 2 + 7)
raise "Can't continue migration safely because of duplicate journals!"
end
def sub_say(message)
say message, subitem: true
end

Loading…
Cancel
Save