Commit 650f4086 authored by Kamil Trzciński's avatar Kamil Trzciński

Forbid the use of `#reload` and prefer `#reset`

The `#reload` makes to load all objects into memory,
and the main purpose of `#reload` is to drop the association cache.

The `#reset` seems to solve exactly that case.
parent 7457c1e1
...@@ -204,3 +204,9 @@ Style/ReturnNil: ...@@ -204,3 +204,9 @@ Style/ReturnNil:
# nil values on the left hand side # nil values on the left hand side
Performance/RegexpMatch: Performance/RegexpMatch:
Enabled: false Enabled: false
ActiveRecordAssociationReload:
Enabled: true
Exclude:
- 'spec/**/*'
- 'ee/spec/**/*'
...@@ -40,7 +40,7 @@ class Admin::ProjectsController < Admin::ApplicationController ...@@ -40,7 +40,7 @@ class Admin::ProjectsController < Admin::ApplicationController
namespace = Namespace.find_by(id: params[:new_namespace_id]) namespace = Namespace.find_by(id: params[:new_namespace_id])
::Projects::TransferService.new(@project, current_user, params.dup).execute(namespace) ::Projects::TransferService.new(@project, current_user, params.dup).execute(namespace)
@project.reload @project.reset
redirect_to admin_project_path(@project) redirect_to admin_project_path(@project)
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
...@@ -55,7 +55,7 @@ class Profiles::PasswordsController < Profiles::ApplicationController ...@@ -55,7 +55,7 @@ class Profiles::PasswordsController < Profiles::ApplicationController
flash[:notice] = "Password was successfully updated. Please login with it" flash[:notice] = "Password was successfully updated. Please login with it"
redirect_to new_user_session_path redirect_to new_user_session_path
else else
@user.reload @user.reset
render 'edit' render 'edit'
end end
end end
......
...@@ -14,7 +14,7 @@ class Projects::ImportsController < Projects::ApplicationController ...@@ -14,7 +14,7 @@ class Projects::ImportsController < Projects::ApplicationController
def create def create
if @project.update(import_params) if @project.update(import_params)
@project.import_state.reload.schedule @project.import_state.reset.schedule
end end
redirect_to project_import_path(@project) redirect_to project_import_path(@project)
......
...@@ -237,7 +237,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -237,7 +237,7 @@ class ProjectsController < Projects::ApplicationController
def toggle_star def toggle_star
current_user.toggle_star(@project) current_user.toggle_star(@project)
@project.reload @project.reset
render json: { render json: {
star_count: @project.star_count star_count: @project.star_count
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
class ApplicationRecord < ActiveRecord::Base class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true self.abstract_class = true
alias_method :reset, :reload
def self.id_in(ids) def self.id_in(ids)
where(id: ids) where(id: ids)
end end
......
...@@ -387,7 +387,7 @@ class MergeRequestDiff < ApplicationRecord ...@@ -387,7 +387,7 @@ class MergeRequestDiff < ApplicationRecord
save! save!
end end
merge_request_diff_files.reload merge_request_diff_files.reset
end end
private private
...@@ -541,10 +541,10 @@ class MergeRequestDiff < ApplicationRecord ...@@ -541,10 +541,10 @@ class MergeRequestDiff < ApplicationRecord
def save_commits def save_commits
MergeRequestDiffCommit.create_bulk(self.id, compare.commits.reverse) MergeRequestDiffCommit.create_bulk(self.id, compare.commits.reverse)
# merge_request_diff_commits.reload is preferred way to reload associated # merge_request_diff_commits.reset is preferred way to reload associated
# objects but it returns cached result for some reason in this case # objects but it returns cached result for some reason in this case
# we can circumvent that by specifying that we need an uncached reload # we can circumvent that by specifying that we need an uncached reload
commits = self.class.uncached { merge_request_diff_commits.reload } commits = self.class.uncached { merge_request_diff_commits.reset }
self.commits_count = commits.size self.commits_count = commits.size
end end
......
...@@ -20,7 +20,7 @@ module Groups ...@@ -20,7 +20,7 @@ module Groups
end end
# reload the relation to prevent triggering destroy hooks on the projects again # reload the relation to prevent triggering destroy hooks on the projects again
group.projects.reload group.projects.reset
group.children.each do |group| group.children.each do |group|
# This needs to be synchronous since the namespace gets destroyed below # This needs to be synchronous since the namespace gets destroyed below
......
...@@ -22,7 +22,7 @@ module Notes ...@@ -22,7 +22,7 @@ module Notes
# We need to refresh the previous suggestions call cache # We need to refresh the previous suggestions call cache
# in order to get the new records. # in order to get the new records.
note.reload note.reset
end end
note note
......
...@@ -29,7 +29,7 @@ module Projects ...@@ -29,7 +29,7 @@ module Projects
set_detected_repository_languages set_detected_repository_languages
end end
project.repository_languages.reload project.repository_languages.reset
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
...@@ -26,7 +26,7 @@ module Projects ...@@ -26,7 +26,7 @@ module Projects
# Remove remaining project group links from source_project # Remove remaining project group links from source_project
def remove_remaining_project_group_links def remove_remaining_project_group_links
source_project.reload.project_group_links.destroy_all # rubocop: disable DestroyAll source_project.reset.project_group_links.destroy_all # rubocop: disable DestroyAll
end end
def group_links_in_target_project def group_links_in_target_project
......
...@@ -30,7 +30,7 @@ module Projects ...@@ -30,7 +30,7 @@ module Projects
true true
rescue Projects::TransferService::TransferError => ex rescue Projects::TransferService::TransferError => ex
project.reload project.reset
project.errors.add(:new_namespace, ex.message) project.errors.add(:new_namespace, ex.message)
false false
end end
...@@ -122,7 +122,7 @@ module Projects ...@@ -122,7 +122,7 @@ module Projects
def rollback_side_effects def rollback_side_effects
rollback_folder_move rollback_folder_move
project.reload project.reset
update_namespace_and_visibility(@old_namespace) update_namespace_and_visibility(@old_namespace)
update_repository_configuration(@old_path) update_repository_configuration(@old_path)
end end
......
...@@ -33,7 +33,7 @@ module Users ...@@ -33,7 +33,7 @@ module Users
end end
end end
user.reload user.reset
end end
private private
......
...@@ -25,7 +25,7 @@ module Users ...@@ -25,7 +25,7 @@ module Users
# We need an up to date User object that has access to all relations that # We need an up to date User object that has access to all relations that
# may have been created earlier. The only way to ensure this is to reload # may have been created earlier. The only way to ensure this is to reload
# the User object. # the User object.
user.reload user.reset
end end
def execute def execute
...@@ -84,7 +84,7 @@ module Users ...@@ -84,7 +84,7 @@ module Users
# Since we batch insert authorization rows, Rails' associations may get # Since we batch insert authorization rows, Rails' associations may get
# out of sync. As such we force a reload of the User object. # out of sync. As such we force a reload of the User object.
user.reload user.reset
end end
def fresh_access_levels_per_project def fresh_access_levels_per_project
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
- if @resource.try(:unconfirmed_email?) - if @resource.try(:unconfirmed_email?)
%p %p
We're contacting you to notify you that your email is being changed to #{@resource.reload.unconfirmed_email}. We're contacting you to notify you that your email is being changed to #{@resource.reset.unconfirmed_email}.
- else - else
%p %p
We're contacting you to notify you that your email has been changed to #{@resource.email}. We're contacting you to notify you that your email has been changed to #{@resource.email}.
......
Hello, <%= @resource.name %>! Hello, <%= @resource.name %>!
<% if @resource.try(:unconfirmed_email?) %> <% if @resource.try(:unconfirmed_email?) %>
We're contacting you to notify you that your email is being changed to <%= @resource.reload.unconfirmed_email %>. We're contacting you to notify you that your email is being changed to <%= @resource.reset.unconfirmed_email %>.
<% else %> <% else %>
We're contacting you to notify you that your email has been changed to <%= @resource.email %>. We're contacting you to notify you that your email has been changed to <%= @resource.email %>.
<% end %> <% end %>
......
...@@ -137,7 +137,7 @@ module API ...@@ -137,7 +137,7 @@ module API
pipeline.cancel_running pipeline.cancel_running
status 200 status 200
present pipeline.reload, with: Entities::Pipeline present pipeline.reset, with: Entities::Pipeline
end end
end end
......
...@@ -351,7 +351,7 @@ module API ...@@ -351,7 +351,7 @@ module API
not_modified! not_modified!
else else
current_user.toggle_star(user_project) current_user.toggle_star(user_project)
user_project.reload user_project.reset
present user_project, with: Entities::Project, current_user: current_user present user_project, with: Entities::Project, current_user: current_user
end end
...@@ -363,7 +363,7 @@ module API ...@@ -363,7 +363,7 @@ module API
post ':id/unstar' do post ':id/unstar' do
if current_user.starred?(user_project) if current_user.starred?(user_project)
current_user.toggle_star(user_project) current_user.toggle_star(user_project)
user_project.reload user_project.reset
present user_project, with: Entities::Project, current_user: current_user present user_project, with: Entities::Project, current_user: current_user
else else
...@@ -403,7 +403,7 @@ module API ...@@ -403,7 +403,7 @@ module API
result = ::Projects::ForkService.new(fork_from_project, current_user).execute(user_project) result = ::Projects::ForkService.new(fork_from_project, current_user).execute(user_project)
if result if result
present user_project.reload, with: Entities::Project, current_user: current_user present user_project.reset, with: Entities::Project, current_user: current_user
else else
render_api_error!("Project already forked", 409) if user_project.forked? render_api_error!("Project already forked", 409) if user_project.forked?
end end
......
...@@ -12,7 +12,7 @@ module Gitlab ...@@ -12,7 +12,7 @@ module Gitlab
retries -= 1 retries -= 1
raise unless retries >= 0 raise unless retries >= 0
subject.reload subject.reset
retry retry
end end
......
...@@ -50,7 +50,7 @@ namespace :gitlab do ...@@ -50,7 +50,7 @@ namespace :gitlab do
puts "Waiting for the import to finish" puts "Waiting for the import to finish"
sleep(5) sleep(5)
project.reload project.reset
end end
Projects::ImportExport::ExportService.new(project, admin).execute Projects::ImportExport::ExportService.new(project, admin).execute
......
# frozen_string_literal: true
module RuboCop
module Cop
# Cop that blacklists the use of `reload`.
class ActiveRecordAssociationReload < RuboCop::Cop::Cop
MSG = 'Use reset instead of reload. ' \
'For more details check the https://gitlab.com/gitlab-org/gitlab-ce/issues/60218.'
def_node_matcher :reload?, <<~PATTERN
(send _ :reload ...)
PATTERN
def on_send(node)
return unless reload?(node)
add_offense(node, location: :selector)
end
end
end
end
...@@ -6,6 +6,7 @@ require_relative 'cop/gitlab/finder_with_find_by' ...@@ -6,6 +6,7 @@ require_relative 'cop/gitlab/finder_with_find_by'
require_relative 'cop/gitlab/union' require_relative 'cop/gitlab/union'
require_relative 'cop/include_sidekiq_worker' require_relative 'cop/include_sidekiq_worker'
require_relative 'cop/safe_params' require_relative 'cop/safe_params'
require_relative 'cop/active_record_association_reload'
require_relative 'cop/avoid_return_from_blocks' require_relative 'cop/avoid_return_from_blocks'
require_relative 'cop/avoid_break_from_strong_memoize' require_relative 'cop/avoid_break_from_strong_memoize'
require_relative 'cop/avoid_route_redirect_leading_slash' require_relative 'cop/avoid_route_redirect_leading_slash'
......
...@@ -6,7 +6,7 @@ describe Gitlab::OptimisticLocking do ...@@ -6,7 +6,7 @@ describe Gitlab::OptimisticLocking do
describe '#retry_lock' do describe '#retry_lock' do
it 'does not reload object if state changes' do it 'does not reload object if state changes' do
expect(pipeline).not_to receive(:reload) expect(pipeline).not_to receive(:reset)
expect(pipeline).to receive(:succeed).and_call_original expect(pipeline).to receive(:succeed).and_call_original
described_class.retry_lock(pipeline) do |subject| described_class.retry_lock(pipeline) do |subject|
...@@ -17,7 +17,7 @@ describe Gitlab::OptimisticLocking do ...@@ -17,7 +17,7 @@ describe Gitlab::OptimisticLocking do
it 'retries action if exception is raised' do it 'retries action if exception is raised' do
pipeline.succeed pipeline.succeed
expect(pipeline2).to receive(:reload).and_call_original expect(pipeline2).to receive(:reset).and_call_original
expect(pipeline2).to receive(:drop).twice.and_call_original expect(pipeline2).to receive(:drop).twice.and_call_original
described_class.retry_lock(pipeline2) do |subject| described_class.retry_lock(pipeline2) do |subject|
......
# frozen_string_literal: true
require 'spec_helper'
require 'rubocop'
require_relative '../../../rubocop/cop/active_record_association_reload'
describe RuboCop::Cop::ActiveRecordAssociationReload do
include CopHelper
subject(:cop) { described_class.new }
context 'when using ActiveRecord::Base' do
it 'registers an offense on reload usage' do
expect_offense(<<~PATTERN.strip_indent)
users = User.all
users.reload
^^^^^^ Use reset instead of reload. For more details check the https://gitlab.com/gitlab-org/gitlab-ce/issues/60218.
PATTERN
end
it 'does not register an offense on reset usage' do
expect_no_offenses(<<~PATTERN.strip_indent)
users = User.all
users.reset
PATTERN
end
end
context 'when using ActiveRecord::Relation' do
it 'registers an offense on reload usage' do
expect_offense(<<~PATTERN.strip_indent)
user = User.new
user.reload
^^^^^^ Use reset instead of reload. For more details check the https://gitlab.com/gitlab-org/gitlab-ce/issues/60218.
PATTERN
end
it 'does not register an offense on reset usage' do
expect_no_offenses(<<~PATTERN.strip_indent)
user = User.new
user.reset
PATTERN
end
end
context 'when using on self' do
it 'registers an offense on reload usage' do
expect_offense(<<~PATTERN.strip_indent)
reload
^^^^^^ Use reset instead of reload. For more details check the https://gitlab.com/gitlab-org/gitlab-ce/issues/60218.
PATTERN
end
it 'does not register an offense on reset usage' do
expect_no_offenses(<<~PATTERN.strip_indent)
reset
PATTERN
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment