Scope clear_search_records to the account being destroyed (#2847)

Account::Searchable#clear_search_records called
Search::Record.for(id).destroy_all which deleted every row in the
shard's table, wiping search records belonging to every other account
that happened to hash to the same shard.

ref: https://3.basecamp.com/2914079/buckets/27/card_tables/cards/9782824728
This commit is contained in:
Mike Dalessio
2026-04-16 15:43:55 -04:00
committed by GitHub
parent 8f3086daa9
commit 9a27ca8b42
2 changed files with 42 additions and 1 deletions
+41
View File
@@ -176,4 +176,45 @@ class Account::IncineratableTest < ActiveSupport::TestCase
# Search records (sharded)
assert_empty Search::Record.for(account_id).where(account_id: account_id)
end
test "incinerating an account clears its own search records but preserves others on the same shard" do
doomed = accounts(:initech)
shard = Search::Record.for(doomed.id)
doomed_card = cards(:radio)
doomed_record = shard.create!(
account_id: doomed_card.account_id,
searchable_type: "Card",
searchable_id: doomed_card.id,
card_id: doomed_card.id,
board_id: doomed_card.board_id,
title: "doomed",
content: "should be destroyed",
created_at: Time.current
)
# Simulate a shard collision: plant a record belonging to a different
# account directly on doomed's shard, as would happen in production when
# two accounts' UUIDs hash to the same shard.
foreign_card = cards(:logo)
foreign_record = shard.create!(
account_id: foreign_card.account_id,
searchable_type: "Card",
searchable_id: foreign_card.id,
card_id: foreign_card.id,
board_id: foreign_card.board_id,
title: "foreign",
content: "should survive",
created_at: Time.current
)
Current.account = doomed
Current.session = Session.new(identity: identities(:mike))
doomed.incinerate
assert_not shard.exists?(id: doomed_record.id),
"the incinerated account's search record should be destroyed"
assert shard.exists?(id: foreign_record.id),
"another account's search record on the same shard should survive"
end
end