From 17a97ef2fa3ee3aba31b401001a1f0f9744fbf12 Mon Sep 17 00:00:00 2001 From: Frank Groeneveld Date: Fri, 28 Oct 2016 19:39:20 +0200 Subject: [PATCH] Remove unreferenced LFS objects from DB and fs --- CHANGELOG.md | 1 + app/models/lfs_object.rb | 6 ++ .../remove_unreferenced_lfs_objects_worker.rb | 8 +++ config/initializers/1_settings.rb | 3 + ...ve_unreferenced_lfs_objects_worker_spec.rb | 55 +++++++++++++++++++ 5 files changed, 73 insertions(+) create mode 100644 app/workers/remove_unreferenced_lfs_objects_worker.rb create mode 100644 spec/workers/remove_unreferenced_lfs_objects_worker_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 99991fb805f..87e372be9be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Please view this file on the master branch, on stable branches it's out of date. - New issue board list dropdown stays open after adding a new list - Fix: Backup restore doesn't clear cache - API: Fix project deploy keys 400 and 500 errors when adding an existing key. !6784 (Joshua Welsh) + - Add job for removal of unreferenced LFS objects from both the database and the filesystem (Frank Groeneveld) - Replace jquery.cookie plugin with js.cookie !7085 - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method - Fix Sign in page 'Forgot your password?' link overlaps on medium-large screens diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index 18657c3e1c8..7712d5783e0 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -17,4 +17,10 @@ class LfsObject < ActiveRecord::Base def project_allowed_access?(project) projects.exists?(storage_project(project).id) end + + def self.destroy_unreferenced + joins("LEFT JOIN lfs_objects_projects ON lfs_objects_projects.lfs_object_id = #{table_name}.id") + .where(lfs_objects_projects: { id: nil }) + .destroy_all + end end diff --git a/app/workers/remove_unreferenced_lfs_objects_worker.rb b/app/workers/remove_unreferenced_lfs_objects_worker.rb new file mode 100644 index 00000000000..b80f131d5f7 --- /dev/null +++ b/app/workers/remove_unreferenced_lfs_objects_worker.rb @@ -0,0 +1,8 @@ +class RemoveUnreferencedLfsObjectsWorker + include Sidekiq::Worker + include CronjobQueue + + def perform + LfsObject.destroy_unreferenced + end +end diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index efe0ac9c965..9fec2ad6bf7 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -307,6 +307,9 @@ Settings.cron_jobs['prune_old_events_worker']['job_class'] = 'PruneOldEventsWork Settings.cron_jobs['trending_projects_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['trending_projects_worker']['cron'] = '0 1 * * *' Settings.cron_jobs['trending_projects_worker']['job_class'] = 'TrendingProjectsWorker' +Settings.cron_jobs['remove_unreferenced_lfs_objects_worker'] ||= Settingslogic.new({}) +Settings.cron_jobs['remove_unreferenced_lfs_objects_worker']['cron'] ||= '20 0 * * *' +Settings.cron_jobs['remove_unreferenced_lfs_objects_worker']['job_class'] = 'RemoveUnreferencedLfsObjectsWorker' # # GitLab Shell diff --git a/spec/workers/remove_unreferenced_lfs_objects_worker_spec.rb b/spec/workers/remove_unreferenced_lfs_objects_worker_spec.rb new file mode 100644 index 00000000000..6d42946de38 --- /dev/null +++ b/spec/workers/remove_unreferenced_lfs_objects_worker_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe RemoveUnreferencedLfsObjectsWorker do + let(:worker) { RemoveUnreferencedLfsObjectsWorker.new } + + describe '#perform' do + let!(:unreferenced_lfs_object1) { create(:lfs_object, oid: '1') } + let!(:unreferenced_lfs_object2) { create(:lfs_object, oid: '2') } + let!(:project1) { create(:empty_project, lfs_enabled: true) } + let!(:project2) { create(:empty_project, lfs_enabled: true) } + let!(:referenced_lfs_object1) { create(:lfs_object, oid: '3') } + let!(:referenced_lfs_object2) { create(:lfs_object, oid: '4') } + let!(:lfs_objects_project1_1) do + create(:lfs_objects_project, + project: project1, + lfs_object: referenced_lfs_object1 + ) + end + let!(:lfs_objects_project2_1) do + create(:lfs_objects_project, + project: project2, + lfs_object: referenced_lfs_object1 + ) + end + let!(:lfs_objects_project1_2) do + create(:lfs_objects_project, + project: project1, + lfs_object: referenced_lfs_object2 + ) + end + + it 'removes unreferenced lfs objects' do + worker.perform + + expect(LfsObject.where(id: unreferenced_lfs_object1.id)).to be_empty + expect(LfsObject.where(id: unreferenced_lfs_object2.id)).to be_empty + end + + it 'leaves referenced lfs objects' do + worker.perform + + expect(referenced_lfs_object1.reload).to be_present + expect(referenced_lfs_object2.reload).to be_present + end + + it 'removes unreferenced lfs objects after project removal' do + project1.destroy + + worker.perform + + expect(referenced_lfs_object1.reload).to be_present + expect(LfsObject.where(id: referenced_lfs_object2.id)).to be_empty + end + end +end