diff --git a/app/models/collection_item.rb b/app/models/collection_item.rb index 8877f3f8da..c2ba2e9b68 100644 --- a/app/models/collection_item.rb +++ b/app/models/collection_item.rb @@ -27,6 +27,7 @@ class CollectionItem < ApplicationRecord delegate :local?, :remote?, to: :collection + validates :account_id, uniqueness: { scope: :collection_id } validates :position, numericality: { only_integer: true, greater_than: 0 } validates :activity_uri, presence: true, if: :local_item_with_remote_account? validates :approval_uri, presence: true, unless: -> { local? || account&.local? || !accepted? } diff --git a/db/migrate/20260410083500_add_index_to_collection_items_account_id_collection_id.rb b/db/migrate/20260410083500_add_index_to_collection_items_account_id_collection_id.rb new file mode 100644 index 0000000000..35acd93ec0 --- /dev/null +++ b/db/migrate/20260410083500_add_index_to_collection_items_account_id_collection_id.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +class AddIndexToCollectionItemsAccountIdCollectionId < ActiveRecord::Migration[8.1] + disable_ddl_transaction! + + def up + add_index_to_table + remove_index :collection_items, [:account_id] + end + + def down + add_index :collection_items, [:account_id] + remove_index_from_table + end + + private + + def add_index_to_table + add_index :collection_items, [:account_id, :collection_id], unique: true, algorithm: :concurrently + rescue ActiveRecord::RecordNotUnique + remove_index_from_table + deduplicate_records + retry + end + + def remove_index_from_table + remove_index :collection_items, [:account_id, :collection_id] + end + + def deduplicate_records + safety_assured do + execute <<~SQL.squish + DELETE FROM collection_items + WHERE id NOT IN ( + SELECT DISTINCT ON(account_id, collection_id) id FROM collection_items + ORDER BY account_id, collection_id, id ASC + ) + SQL + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 70810b10d9..6dddbc0efa 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2026_03_26_112324) do +ActiveRecord::Schema[8.1].define(version: 2026_04_10_083500) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -370,7 +370,7 @@ ActiveRecord::Schema[8.1].define(version: 2026_03_26_112324) do t.integer "state", default: 0, null: false t.datetime "updated_at", null: false t.string "uri" - t.index ["account_id"], name: "index_collection_items_on_account_id" + t.index ["account_id", "collection_id"], name: "index_collection_items_on_account_id_and_collection_id", unique: true t.index ["approval_uri"], name: "index_collection_items_on_approval_uri", unique: true, where: "(approval_uri IS NOT NULL)" t.index ["collection_id"], name: "index_collection_items_on_collection_id" t.index ["uri"], name: "index_collection_items_on_uri", unique: true, where: "(uri IS NOT NULL)"