backfill conversations_* tables

* read from `conversations`, which already has root_account_ids
* copy those, which are comma-separated values in a string, to:
  * conversation_messages
  * conversation_participants
  * conversation_message_participants, through conversation_messages

closes INTEROP-5853, INTEROP-5854, INTEROP-5855, INTEROP-5856
flag=none

test plan
* specs
* in the UI, create a conversation chain between two users
* in a rails console, note that the conversation has a root_account_ids field
* note that the Conversation's `conversation_messages`,
`conversation_participants`, and
`conversation_message_participants`
all have those root account ids as well, since there is logic to populate those on save
* for each of those associations, run `association.update_all("root_account_ids=NULL")`
* then run `DataFixup:PopulateRootAccountIdsOnConversationsTables.run`
* note that the root_account_ids fields for all associations
match the field on the conversation

Change-Id: I98ac281291c2105240610b0a0690a544c7d082a7
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/241978
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Cody Cutrer <cody@instructure.com>
QA-Review: Xander Moffatt <xmoffatt@instructure.com>
Product-Review: Xander Moffatt <xmoffatt@instructure.com>
This commit is contained in:
Xander Moffatt 2020-07-03 13:49:55 -06:00
parent a27c202a63
commit 24153c85c9
3 changed files with 196 additions and 0 deletions

View File

@ -0,0 +1,32 @@
#
# Copyright (C) 2020 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
class PopulateRootAccountIdsOnConversationsTables < ActiveRecord::Migration[5.2]
tag :postdeploy
def up
Conversation.find_ids_in_ranges(batch_size: 100_000) do |min, max|
DataFixup::PopulateRootAccountIdsOnConversationsTables.send_later_if_production_enqueue_args(
:run,
{:priority => Delayed::LOWER_PRIORITY, :n_strand => ["root_account_id_backfill_strand", Shard.current.database_server.id]},
min, max
)
end
end
def down; end
end

View File

@ -0,0 +1,34 @@
#
# Copyright (C) 2020 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
module DataFixup::PopulateRootAccountIdsOnConversationsTables
def self.run(min, max)
Conversation.find_ids_in_ranges(start_at: min, end_at: max) do |batch_min, batch_max|
ConversationParticipant.joins(:conversation).
where(conversation: batch_min..batch_max).
update_all("root_account_ids=conversations.root_account_ids")
messages = ConversationMessage.joins(:conversation).where(conversation: batch_min..batch_max)
messages.update_all("root_account_ids=conversations.root_account_ids")
# only has FK to ConversationMessage and ConversationParticipant, not Conversation
ConversationMessageParticipant.joins(:conversation_message).
where(conversation_message: messages).
update_all("root_account_ids=conversation_messages.root_account_ids")
end
end
end

View File

@ -0,0 +1,130 @@
#
# Copyright (C) 2020 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
require 'spec_helper'
describe DataFixup::PopulateRootAccountIdsOnConversationsTables do
def ids_to_string(*ids)
ids.sort.join(',')
end
def reset_root_account_ids(*models)
models.each { |m| m.update_column(:root_account_ids, nil) }
end
def check_root_account_ids(expected, *models)
models.each { |m| expect(m.reload.root_account_ids).to eq expected}
end
before :once do
@root_account1 = account_model
@root_account2 = account_model
@user1 = user_model
@user2 = user_model
end
def check_conversation_messages(conversation, ids)
cm1 = ConversationMessage.create!(conversation: conversation)
cm2 = ConversationMessage.create!(conversation: conversation)
reset_root_account_ids(cm1, cm2)
DataFixup::PopulateRootAccountIdsOnConversationsTables.run(conversation.id, conversation.id)
check_root_account_ids(ids, cm1, cm2)
end
def check_conversation_participants(conversation, ids)
cp1 = ConversationParticipant.create!(conversation: conversation, user: @user1)
cp2 = ConversationParticipant.create!(conversation: conversation, user: @user2)
reset_root_account_ids(cp1, cp2)
DataFixup::PopulateRootAccountIdsOnConversationsTables.run(conversation.id, conversation.id)
check_root_account_ids(ids, cp1, cp2)
end
def check_conversation_message_participants(conversation, ids)
cm1 = ConversationMessage.create!(conversation: conversation)
cm2 = ConversationMessage.create!(conversation: conversation)
cmp1 = ConversationMessageParticipant.create!(conversation_message: cm1, user: @user1)
cmp2 = ConversationMessageParticipant.create!(conversation_message: cm2, user: @user2)
reset_root_account_ids(cm1, cm2, cmp1, cmp2)
DataFixup::PopulateRootAccountIdsOnConversationsTables.run(conversation.id, conversation.id)
check_root_account_ids(ids, cmp1, cmp2)
end
context 'single-account Conversation' do
before :once do
@ids = [@root_account1.id]
@c = Conversation.create!(root_account_ids: ids_to_string(@root_account1.id))
end
it 'sets root account id on all associated ConversationMessages' do
check_conversation_messages(@c, @ids)
end
it 'sets root account id on all associated ConversationParticipants' do
check_conversation_participants(@c, @ids)
end
it 'sets root account id on all ConversationMessageParticipants through ConversationMessage' do
check_conversation_message_participants(@c, @ids)
end
end
context 'multiple-account Conversation' do
before :once do
@ids = [@root_account1.id, @root_account2.id]
@c = Conversation.create!(root_account_ids: ids_to_string(@root_account1.id, @root_account2.id))
end
it 'sets root account ids on all associated ConversationMessages' do
check_conversation_messages(@c, @ids)
end
it 'sets root account ids on all associated ConversationParticipants' do
check_conversation_participants(@c, @ids)
end
it 'sets root account ids on all ConversationMessageParticipants through ConversationMessage' do
check_conversation_message_participants(@c, @ids)
end
end
context 'cross-shard Conversation' do
specs_require_sharding
before :once do
cross_shard_account = @shard1.activate do
account_model
end
@ids = [@root_account1.id, cross_shard_account.id]
@c = Conversation.create!(root_account_ids: ids_to_string(@root_account1.id, cross_shard_account.id))
end
it 'sets root account ids on all associated ConversationMessages' do
check_conversation_messages(@c, @ids)
end
it 'sets root account ids on all associated ConversationParticipants' do
check_conversation_participants(@c, @ids)
end
it 'sets root account ids on all ConversationMessageParticipants through ConversationMessage' do
check_conversation_message_participants(@c, @ids)
end
end
end