diff options
Diffstat (limited to 'app/services/repositories/housekeeping_service.rb')
-rw-r--r-- | app/services/repositories/housekeeping_service.rb | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/app/services/repositories/housekeeping_service.rb b/app/services/repositories/housekeeping_service.rb new file mode 100644 index 00000000000..6a2fa95d25f --- /dev/null +++ b/app/services/repositories/housekeeping_service.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +# Used for git housekeeping +# +# Ex. +# Repositories::HousekeepingService.new(project).execute +# Repositories::HousekeepingService.new(project.wiki).execute +# +module Repositories + class HousekeepingService < BaseService + # Timeout set to 24h + LEASE_TIMEOUT = 86400 + PACK_REFS_PERIOD = 6 + + class LeaseTaken < StandardError + def to_s + "Somebody already triggered housekeeping for this resource in the past #{LEASE_TIMEOUT / 60} minutes" + end + end + + def initialize(resource, task = nil) + @resource = resource + @task = task + end + + def execute + lease_uuid = try_obtain_lease + raise LeaseTaken unless lease_uuid.present? + + yield if block_given? + + execute_gitlab_shell_gc(lease_uuid) + end + + def needed? + pushes_since_gc > 0 && period_match? && housekeeping_enabled? + end + + def increment! + Gitlab::Metrics.measure(:increment_pushes_since_gc) do + @resource.increment_pushes_since_gc + end + end + + private + + def execute_gitlab_shell_gc(lease_uuid) + GitGarbageCollectWorker.perform_async(@resource.id, task, lease_key, lease_uuid) + ensure + if pushes_since_gc >= gc_period + Gitlab::Metrics.measure(:reset_pushes_since_gc) do + @resource.reset_pushes_since_gc + end + end + end + + def try_obtain_lease + Gitlab::Metrics.measure(:obtain_housekeeping_lease) do + lease = ::Gitlab::ExclusiveLease.new(lease_key, timeout: LEASE_TIMEOUT) + lease.try_obtain + end + end + + def lease_key + "#{@resource.class.name.underscore.pluralize}_housekeeping:#{@resource.id}" + end + + def pushes_since_gc + @resource.pushes_since_gc + end + + def task + return @task if @task + + if pushes_since_gc % gc_period == 0 + :gc + elsif pushes_since_gc % full_repack_period == 0 + :full_repack + elsif pushes_since_gc % repack_period == 0 + :incremental_repack + else + :pack_refs + end + end + + def period_match? + [gc_period, full_repack_period, repack_period, PACK_REFS_PERIOD].any? { |period| pushes_since_gc % period == 0 } + end + + def housekeeping_enabled? + Gitlab::CurrentSettings.housekeeping_enabled + end + + def gc_period + Gitlab::CurrentSettings.housekeeping_gc_period + end + + def full_repack_period + Gitlab::CurrentSettings.housekeeping_full_repack_period + end + + def repack_period + Gitlab::CurrentSettings.housekeeping_incremental_repack_period + end + end +end |