docbrown/spec/factories/articles.rb
Jamie Gaskins 68025ae472
Optimize Article.published scope to use an index (#13071)
* Optimize Article.published scope

The index on `articles.published` is not being invoked due to lack of
diversity in the values in that column (it only has 2 possible values
because it's a boolean).

The index on `articles.published_at` is extremely diverse and will be
invoked any time it can reduce the scope of a query by an order of
magnitude or more.

On DEV, this does not change the row count for the scope:

    irb(main):001:0> Article.where(published: true).count == Article.where(published: true).where(Arel.sql("published_at < now()")).count
    => true

The query plan for the `Articles::Feeds::LargeForemExperimental` service
invoked in the `Stories::FeedController#show` endpoint goes from this:

    Planning Time: 0.271 ms
    Execution Time: 329.258 ms

to this:

    Planning Time: 0.330 ms
    Execution Time: 0.468 ms

This is a reduction in query time of 99.7%

* Set published_at in articles factory

An article that is published should always have a `published_at`
timestamp

* Use Time.current for Zonebie

* Add clarifying comment to Article.published scope

* Generate timestamp in Ruby instead of SQL

I think using transactions for specs was interfering with comparing
timestamps generated in SQL, so this commit generates the timestamp in
Ruby.

* Move published_at outside of the transient block

This was causing articles to be marked as `published_at` right now even
if `published_at` was specified in the `FactoryBot.create` call.

* published_at: nil is no longer the factory default

* published_at is no longer nil by default

* published_at is no longer nil by default

* We didn't actually want to clear this published_at

This was intentionally left out to show that published_at does not get
cleared when we flip published true->false.
2021-03-23 13:07:27 -04:00

64 lines
2.1 KiB
Ruby

FactoryBot.define do
sequence(:title) { |n| "#{Faker::Book.title}#{n}" }
factory :article do
published_at { Time.current }
transient do
title { generate :title }
published { true }
date { "01/01/2015" }
tags { "javascript, html, css" }
canonical_url { Faker::Internet.url }
with_canonical_url { false }
with_main_image { true }
with_date { false }
with_tags { true }
with_hr_issue { false }
with_tweet_tag { false }
with_user_subscription_tag { false }
with_title { true }
with_collection { nil }
end
co_author_ids { [] }
association :user, factory: :user, strategy: :create
description { Faker::Hipster.paragraph(sentence_count: 1)[0..100] }
main_image { with_main_image ? Faker::Avatar.image : nil }
experience_level_rating { rand(4..6) }
body_markdown do
<<~HEREDOC
---
title: #{title if with_title}
published: #{published}
tags: #{tags if with_tags}
date: #{date if with_date}
series: #{with_collection&.slug if with_collection}
canonical_url: #{canonical_url if with_canonical_url}
---
#{Faker::Hipster.paragraph(sentence_count: 2)}
#{'{% tweet 1018911886862057472 %}' if with_tweet_tag}
#{'{% user_subscription CTA text %}' if with_user_subscription_tag}
#{Faker::Hipster.paragraph(sentence_count: 1)}
#{"\n\n---\n\n something \n\n---\n funky in the code? \n---\n That's nice" if with_hr_issue}
HEREDOC
end
end
trait :video do
after(:build) do |article|
article.video = "https://s3.amazonaws.com/dev-to-input-v0/video-upload__2d7dc29e39a40c7059572bca75bb646b"
article.save
end
end
trait :with_notification_subscription do
after(:create) do |article|
create(:notification_subscription, user_id: article.user_id, notifiable: article)
end
end
trait :with_user_subscription_tag_role_user do
after(:build) { |article| article.user.add_role(:restricted_liquid_tag, LiquidTags::UserSubscriptionTag) }
end
end