[deploy] Add unique index on articles user_id, title and body_markdown (#10284)

* Add unique index on articles user_id, title and body_markdown

* Add test
This commit is contained in:
rhymes 2020-09-14 16:11:12 +02:00 committed by GitHub
parent f6e24bc85b
commit f0b124cc26
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 37 additions and 8 deletions

View file

@ -11,10 +11,3 @@ require:
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
# Offense count: 2
# Configuration parameters: Include.
# Include: app/models/**/*.rb
Rails/UniqueValidationWithoutIndex:
Exclude:
- 'app/models/article.rb'

View file

@ -0,0 +1,26 @@
class AddUniqueIndexToArticlesUserIdTitleBodyMarkdown < ActiveRecord::Migration[6.0]
disable_ddl_transaction!
def up
# to avoid the error "Values larger than 1/3 of a buffer page cannot be indexed."
# due to the fact that large text column cannot be indexed by btree indexes
# we are going to need to build an index on the hash of the `body_markdown` column.
# We also cannot use a `HASH` index as it's not supported by unique columns.
# I don't recommend using GiN or GiST as well
# See <https://www.postgresql.org/message-id/AANLkTin3p6VS1Z=TtqUV-5cG4TZpTjUMfuPNzWJFgnr5@mail.gmail.com>,
# <https://www.postgresql.org/docs/11/pgcrypto.html#id-1.11.7.34.5> and
# <https://www.postgresql.org/docs/11/indexes-types.html>
# NOTE: using SQL as I couldn't find a way to have Rails generate it correctly
ActiveRecord::Base.connection.execute(
<<~SQL
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "index_articles_on_user_id_and_title_and_digest_body_markdown"
ON "articles"
USING btree ("user_id", "title", digest("body_markdown", 'sha512'::text));
SQL
)
end
def down
remove_index :articles, name: :index_articles_on_user_id_and_title_and_digest_body_markdown, algorithm: :concurrently
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_09_10_205316) do
ActiveRecord::Schema.define(version: 2020_09_11_140318) do
# These are extensions that must be enabled in order to support this database
enable_extension "citext"
@ -153,6 +153,7 @@ ActiveRecord::Schema.define(version: 2020_09_10_205316) do
t.string "video_source_url"
t.string "video_state"
t.string "video_thumbnail_url"
t.index "user_id, title, digest(body_markdown, 'sha512'::text)", name: "index_articles_on_user_id_and_title_and_digest_body_markdown", unique: true
t.index ["boost_states"], name: "index_articles_on_boost_states", using: :gin
t.index ["canonical_url"], name: "index_articles_on_canonical_url", unique: true
t.index ["comment_score"], name: "index_articles_on_comment_score"

View file

@ -43,6 +43,15 @@ RSpec.describe Article, type: :model do
it { is_expected.not_to allow_value("foo").for(:main_image_background_hex_color) }
describe "#body_markdown" do
it "is unique scoped for user_id and title" do
art2 = build(:article, body_markdown: article.body_markdown, user: article.user, title: article.title)
expect(art2).not_to be_valid
expect(art2.errors.full_messages.to_sentence).to match("markdown has already been taken")
end
end
describe "#after_commit" do
it "on update enqueues job to index article to elasticsearch" do
article.save