Commit 2771c6ec authored by Andreas Brandl's avatar Andreas Brandl

Merge branch 'tc-index-uploads-file-store-ee' into 'master'

EE port: Enhance performance of counting local Uploads

Closes #6070

See merge request gitlab-org/gitlab-ee!8144
parents a939dd1c 4d1dac8f
...@@ -13,7 +13,7 @@ class Upload < ActiveRecord::Base ...@@ -13,7 +13,7 @@ class Upload < ActiveRecord::Base
validates :model, presence: true validates :model, presence: true
validates :uploader, presence: true validates :uploader, presence: true
scope :with_files_stored_locally, -> { where(store: [nil, ObjectStorage::Store::LOCAL]) } scope :with_files_stored_locally, -> { where(store: ObjectStorage::Store::LOCAL) }
scope :with_files_stored_remotely, -> { where(store: ObjectStorage::Store::REMOTE) } scope :with_files_stored_remotely, -> { where(store: ObjectStorage::Store::REMOTE) }
before_save :calculate_checksum!, if: :foreground_checksummable? before_save :calculate_checksum!, if: :foreground_checksummable?
...@@ -71,8 +71,6 @@ class Upload < ActiveRecord::Base ...@@ -71,8 +71,6 @@ class Upload < ActiveRecord::Base
end end
def local? def local?
return true if store.nil?
store == ObjectStorage::Store::LOCAL store == ObjectStorage::Store::LOCAL
end end
......
---
title: Enhance performance of counting local Uploads
merge_request: 22522
author:
type: performance
# frozen_string_literal: true
class AddIndexToUploadsStore < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :uploads, :store
end
def down
remove_concurrent_index :uploads, :store
end
end
# frozen_string_literal: true
class StealFillStoreUpload < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
BATCH_SIZE = 10_000
disable_ddl_transaction!
class Upload < ActiveRecord::Base
include EachBatch
self.table_name = 'uploads'
self.inheritance_column = :_type_disabled # Disable STI
end
def up
Gitlab::BackgroundMigration.steal('FillStoreUpload')
Upload.where(store: nil).each_batch(of: BATCH_SIZE) do |batch|
range = batch.pluck('MIN(id)', 'MAX(id)').first
Gitlab::BackgroundMigration::FillStoreUpload.new.perform(*range)
end
end
def down
# noop
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20181101144347) do ActiveRecord::Schema.define(version: 20181105201455) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -2870,6 +2870,7 @@ ActiveRecord::Schema.define(version: 20181101144347) do ...@@ -2870,6 +2870,7 @@ ActiveRecord::Schema.define(version: 20181101144347) do
add_index "uploads", ["checksum"], name: "index_uploads_on_checksum", using: :btree add_index "uploads", ["checksum"], name: "index_uploads_on_checksum", using: :btree
add_index "uploads", ["model_id", "model_type"], name: "index_uploads_on_model_id_and_model_type", using: :btree add_index "uploads", ["model_id", "model_type"], name: "index_uploads_on_model_id_and_model_type", using: :btree
add_index "uploads", ["store"], name: "index_uploads_on_store", using: :btree
add_index "uploads", ["uploader", "path"], name: "index_uploads_on_uploader_and_path", using: :btree add_index "uploads", ["uploader", "path"], name: "index_uploads_on_uploader_and_path", using: :btree
create_table "user_agent_details", force: :cascade do |t| create_table "user_agent_details", force: :cascade do |t|
......
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20181105201455_steal_fill_store_upload.rb')
describe StealFillStoreUpload, :migration do
let(:uploads) { table(:uploads) }
describe '#up' do
it 'steals the FillStoreUpload background migration' do
expect(Gitlab::BackgroundMigration).to receive(:steal).with('FillStoreUpload').and_call_original
migrate!
end
it 'does not run migration if not needed' do
uploads.create(size: 100.kilobytes,
uploader: 'AvatarUploader',
path: 'uploads/-/system/avatar.jpg',
store: 1)
expect_any_instance_of(Gitlab::BackgroundMigration::FillStoreUpload).not_to receive(:perform)
migrate!
end
it 'ensures all rows are migrated' do
uploads.create(size: 100.kilobytes,
uploader: 'AvatarUploader',
path: 'uploads/-/system/avatar.jpg',
store: nil)
expect_any_instance_of(Gitlab::BackgroundMigration::FillStoreUpload).to receive(:perform).and_call_original
expect do
migrate!
end.to change { uploads.where(store: nil).count }.from(1).to(0)
end
end
end
...@@ -21,7 +21,8 @@ describe Upload do ...@@ -21,7 +21,8 @@ describe Upload do
path: __FILE__, path: __FILE__,
size: described_class::CHECKSUM_THRESHOLD + 1.kilobyte, size: described_class::CHECKSUM_THRESHOLD + 1.kilobyte,
model: build_stubbed(:user), model: build_stubbed(:user),
uploader: double('ExampleUploader') uploader: double('ExampleUploader'),
store: ObjectStorage::Store::LOCAL
) )
expect(UploadChecksumWorker) expect(UploadChecksumWorker)
...@@ -35,7 +36,8 @@ describe Upload do ...@@ -35,7 +36,8 @@ describe Upload do
path: __FILE__, path: __FILE__,
size: described_class::CHECKSUM_THRESHOLD, size: described_class::CHECKSUM_THRESHOLD,
model: build_stubbed(:user), model: build_stubbed(:user),
uploader: double('ExampleUploader') uploader: double('ExampleUploader'),
store: ObjectStorage::Store::LOCAL
) )
expect { upload.save } expect { upload.save }
...@@ -60,7 +62,7 @@ describe Upload do ...@@ -60,7 +62,7 @@ describe Upload do
describe '#absolute_path' do describe '#absolute_path' do
it 'returns the path directly when already absolute' do it 'returns the path directly when already absolute' do
path = '/path/to/namespace/project/secret/file.jpg' path = '/path/to/namespace/project/secret/file.jpg'
upload = described_class.new(path: path) upload = described_class.new(path: path, store: ObjectStorage::Store::LOCAL)
expect(upload).not_to receive(:uploader_class) expect(upload).not_to receive(:uploader_class)
...@@ -69,7 +71,7 @@ describe Upload do ...@@ -69,7 +71,7 @@ describe Upload do
it "delegates to the uploader's absolute_path method" do it "delegates to the uploader's absolute_path method" do
uploader = spy('FakeUploader') uploader = spy('FakeUploader')
upload = described_class.new(path: 'secret/file.jpg') upload = described_class.new(path: 'secret/file.jpg', store: ObjectStorage::Store::LOCAL)
expect(upload).to receive(:uploader_class).and_return(uploader) expect(upload).to receive(:uploader_class).and_return(uploader)
upload.absolute_path upload.absolute_path
...@@ -81,7 +83,8 @@ describe Upload do ...@@ -81,7 +83,8 @@ describe Upload do
describe '#calculate_checksum!' do describe '#calculate_checksum!' do
let(:upload) do let(:upload) do
described_class.new(path: __FILE__, described_class.new(path: __FILE__,
size: described_class::CHECKSUM_THRESHOLD - 1.megabyte) size: described_class::CHECKSUM_THRESHOLD - 1.megabyte,
store: ObjectStorage::Store::LOCAL)
end end
it 'sets `checksum` to SHA256 sum of the file' do it 'sets `checksum` to SHA256 sum of the file' do
...@@ -104,14 +107,14 @@ describe Upload do ...@@ -104,14 +107,14 @@ describe Upload do
describe '#exist?' do describe '#exist?' do
it 'returns true when the file exists' do it 'returns true when the file exists' do
upload = described_class.new(path: __FILE__) upload = described_class.new(path: __FILE__, store: ObjectStorage::Store::LOCAL)
expect(upload).to exist expect(upload).to exist
end end
context 'when the file does not exist' do context 'when the file does not exist' do
it 'returns false' do it 'returns false' do
upload = described_class.new(path: "#{__FILE__}-nope") upload = described_class.new(path: "#{__FILE__}-nope", store: ObjectStorage::Store::LOCAL)
expect(upload).not_to exist expect(upload).not_to exist
end end
...@@ -139,7 +142,7 @@ describe Upload do ...@@ -139,7 +142,7 @@ describe Upload do
context 'when the record is not persisted' do context 'when the record is not persisted' do
it 'does not send a message to Sentry' do it 'does not send a message to Sentry' do
upload = described_class.new(path: "#{__FILE__}-nope") upload = described_class.new(path: "#{__FILE__}-nope", store: ObjectStorage::Store::LOCAL)
expect(Raven).not_to receive(:capture_message) expect(Raven).not_to receive(:capture_message)
...@@ -147,7 +150,7 @@ describe Upload do ...@@ -147,7 +150,7 @@ describe Upload do
end end
it 'does not increment a metric counter' do it 'does not increment a metric counter' do
upload = described_class.new(path: "#{__FILE__}-nope") upload = described_class.new(path: "#{__FILE__}-nope", store: ObjectStorage::Store::LOCAL)
expect(Gitlab::Metrics).not_to receive(:counter) expect(Gitlab::Metrics).not_to receive(:counter)
......
...@@ -38,14 +38,6 @@ describe 'gitlab:uploads:migrate rake tasks' do ...@@ -38,14 +38,6 @@ describe 'gitlab:uploads:migrate rake tasks' do
let!(:projects) { create_list(:project, 10, :with_avatar) } let!(:projects) { create_list(:project, 10, :with_avatar) }
it_behaves_like 'enqueue jobs in batch', batch: 4 it_behaves_like 'enqueue jobs in batch', batch: 4
context 'Upload has store = nil' do
before do
Upload.where(model: projects).update_all(store: nil)
end
it_behaves_like 'enqueue jobs in batch', batch: 4
end
end end
context "for Group" do context "for Group" do
......
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