Limit `update_tracked_fields` to write to database once/hour

Every time a user logs in or out, the Trackable attributes are written to the
database. This is causing a lot of load on the database, for data that isn't
really critical.

So to avoid the database being hammered, add a Gitlab::ExclusiveLease before
writing trackable attributes to the database. This lease expires after an hour,
so only when the attributes were written more than an hour ago, they can be
written again. Otherwise they are ignored.
This commit is contained in:
Toon Claes 2017-05-02 22:52:14 +02:00
parent 8b9cd3c072
commit 6a915d6f2d
3 changed files with 33 additions and 0 deletions

View File

@ -40,6 +40,16 @@ class User < ActiveRecord::Base
devise :lockable, :recoverable, :rememberable, :trackable,
:validatable, :omniauthable, :confirmable, :registerable
# Limit trackable fields to update at most once every hour
alias_method :devise_update_tracked_fields!, :update_tracked_fields!
def update_tracked_fields!(request)
lease = Gitlab::ExclusiveLease.new("user_update_tracked_fields:#{id}", timeout: 1.hour.to_i)
return unless lease.try_obtain
devise_update_tracked_fields!(request)
end
attr_accessor :force_random_password
# Virtual attribute for authenticating by either username or email

View File

@ -0,0 +1,4 @@
---
title: "Limit User's trackable attributes, like `current_sign_in_at`, to update at most once/hour"
merge_request: 11053
author:

View File

@ -344,6 +344,25 @@ describe User, models: true do
end
end
describe '#update_tracked_fields!', :redis do
let(:request) { OpenStruct.new(remote_ip: "127.0.0.1") }
let(:user) { create(:user) }
it 'writes trackable attributes' do
expect do
user.update_tracked_fields!(request)
end.to change { user.reload.current_sign_in_at }
end
it 'does not write trackable attributes when called a second time within the hour' do
user.update_tracked_fields!(request)
expect do
user.update_tracked_fields!(request)
end.not_to change { user.current_sign_in_at }
end
end
shared_context 'user keys' do
let(:user) { create(:user) }
let!(:key) { create(:key, user: user) }