Dependency Proxy TTL workers
⛰ Context
The Dependency Proxy allows users to cache images pulled from DockerHub in their GitLab storage. Images are made up of blobs
and manifests
. There are many reasons users may want to clear their cache or delete specific images. Examples include:
- Users pull an image like
alpine:latest
, where the image changes over time, meaning we end up with a bunch of staleblobs
andmanifests
taking up space unnecessarily. - The image that was cached is simply no longer used anywhere.
To help relieve this problem, we are implementing Time-To-Live (TTL) policies where the user can set how long they want any given blob/manifest to be retained before being removed.
🚮 What does this MR do?
In previous MRs we created a model to store the TTL policies and added a "status" to the blob and manifest models. This MR implements the workers that are responsible for:
- Updating blobs/manifests to "expired" based on their associated policies
- Deleting the expired records/files
(1.) is handled by DependencyProxy::ImageTtlGroupPolicyWorker
and (2.) is handled by DependencyProxy::CleanupBlobWorker
and DependencyProxy::CleanupManifestWorker
.
(1.) is a worker that runs once daily, iterating over all enabled TTL policies and then updating any qualified blobs/manifests to "expired". It kicks off the second set of workers that delete the expired records.
The (2.) workers use the LimitedCapacity::Worker
to ensure the queue for deleting files does not get backed up by only allowing a certain number of jobs to run at any given time. This number of jobs is controlled by the new ApplicationSetting
: dependency_proxy_ttl_group_policy_worker_capacity
. We want to control this because some blobs may be very large and take a long time to be deleted.
We also update the dependency proxy services to ensure no expired blob or manifest is ever served when users pull an image.
💾 Database
🐘 Migration output
Up migration
== 20210910014741 AddDependencyProxyTtlGroupPolicyWorkerCapacityToApplicationSettings: migrating
-- add_column(:application_settings, :dependency_proxy_ttl_group_policy_worker_capacity, :smallint, {:default=>2, :null=>false})
-> 0.0055s
== 20210910014741 AddDependencyProxyTtlGroupPolicyWorkerCapacityToApplicationSettings: migrated (0.0056s)
== 20210910015047 AddAppSettingsDepProxyTtlWorkerCapacityCheckConstraint: migrating
-- transaction_open?()
-> 0.0000s
-- current_schema()
-> 0.0003s
-- execute("ALTER TABLE application_settings\nADD CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive\nCHECK ( dependency_proxy_ttl_group_policy_worker_capacity >= 0 )\nNOT VALID;\n")
-> 0.0045s
-- current_schema()
-> 0.0002s
-- execute("SET statement_timeout TO 0")
-> 0.0011s
-- execute("ALTER TABLE application_settings VALIDATE CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive;")
-> 0.0023s
-- execute("RESET statement_timeout")
-> 0.0012s
== 20210910015047 AddAppSettingsDepProxyTtlWorkerCapacityCheckConstraint: migrated (0.0453s)
== 20210913224558 UpdateDependencyProxyManifestsUniquenessConstraint: migrating
-- transaction_open?()
-> 0.0000s
-- index_exists?(:dependency_proxy_manifests, [:group_id, :file_name, :status], {:unique=>true, :name=>"index_dep_prox_manifests_on_group_id_file_name_and_status", :algorithm=>:concurrently})
-> 0.0044s
-- add_index(:dependency_proxy_manifests, [:group_id, :file_name, :status], {:unique=>true, :name=>"index_dep_prox_manifests_on_group_id_file_name_and_status", :algorithm=>:concurrently})
-> 0.0043s
-- transaction_open?()
-> 0.0000s
-- indexes(:dependency_proxy_manifests)
-> 0.0018s
-- remove_index(:dependency_proxy_manifests, {:algorithm=>:concurrently, :name=>"index_dependency_proxy_manifests_on_group_id_and_file_name"})
-> 0.0030s
== 20210913224558 UpdateDependencyProxyManifestsUniquenessConstraint: migrated (0.0182s)
== 20210914172202 AddStatusIndexToDependencyProxyTables: migrating ============
-- transaction_open?()
-> 0.0000s
-- index_exists?(:dependency_proxy_manifests, :status, {:name=>"index_dependency_proxy_manifests_on_status", :algorithm=>:concurrently})
-> 0.0015s
-- add_index(:dependency_proxy_manifests, :status, {:name=>"index_dependency_proxy_manifests_on_status", :algorithm=>:concurrently})
-> 0.0032s
-- transaction_open?()
-> 0.0000s
-- index_exists?(:dependency_proxy_blobs, :status, {:name=>"index_dependency_proxy_blobs_on_status", :algorithm=>:concurrently})
-> 0.0014s
-- add_index(:dependency_proxy_blobs, :status, {:name=>"index_dependency_proxy_blobs_on_status", :algorithm=>:concurrently})
-> 0.0030s
== 20210914172202 AddStatusIndexToDependencyProxyTables: migrated (0.0148s) ===
== 20210928171122 AddGroupIdStatusIdIndexToDependencyProxyTables: migrating ===
-- transaction_open?()
-> 0.0000s
-- index_exists?(:dependency_proxy_manifests, [:group_id, :status, :id], {:name=>"index_dependency_proxy_manifests_on_group_id_status_and_id", :algorithm=>:concurrently})
-> 0.0037s
-- execute("SET statement_timeout TO 0")
-> 0.0007s
-- add_index(:dependency_proxy_manifests, [:group_id, :status, :id], {:name=>"index_dependency_proxy_manifests_on_group_id_status_and_id", :algorithm=>:concurrently})
-> 0.0077s
-- execute("RESET statement_timeout")
-> 0.0008s
-- transaction_open?()
-> 0.0000s
-- index_exists?(:dependency_proxy_blobs, [:group_id, :status, :id], {:name=>"index_dependency_proxy_blobs_on_group_id_status_and_id", :algorithm=>:concurrently})
-> 0.0017s
-- add_index(:dependency_proxy_blobs, [:group_id, :status, :id], {:name=>"index_dependency_proxy_blobs_on_group_id_status_and_id", :algorithm=>:concurrently})
-> 0.0039s
== 20210928171122 AddGroupIdStatusIdIndexToDependencyProxyTables: migrated (0.0230s)
Down migration
== 20210928171122 AddGroupIdStatusIdIndexToDependencyProxyTables: reverting ===
-- transaction_open?()
-> 0.0000s
-- indexes(:dependency_proxy_manifests)
-> 0.0038s
-- execute("SET statement_timeout TO 0")
-> 0.0006s
-- remove_index(:dependency_proxy_manifests, {:algorithm=>:concurrently, :name=>"index_dependency_proxy_manifests_on_group_id_status_and_id"})
-> 0.0039s
-- execute("RESET statement_timeout")
-> 0.0006s
-- transaction_open?()
-> 0.0000s
-- indexes(:dependency_proxy_blobs)
-> 0.0022s
-- remove_index(:dependency_proxy_blobs, {:algorithm=>:concurrently, :name=>"index_dependency_proxy_blobs_on_group_id_status_and_id"})
-> 0.0020s
== 20210928171122 AddGroupIdStatusIdIndexToDependencyProxyTables: reverted (0.0166s)
== 20210914172202 AddStatusIndexToDependencyProxyTables: reverting ============
-- transaction_open?()
-> 0.0000s
-- indexes(:dependency_proxy_manifests)
-> 0.0059s
-- execute("SET statement_timeout TO 0")
-> 0.0007s
-- remove_index(:dependency_proxy_manifests, {:algorithm=>:concurrently, :name=>"index_dependency_proxy_manifests_on_status"})
-> 0.0169s
-- execute("RESET statement_timeout")
-> 0.0006s
-- transaction_open?()
-> 0.0000s
-- indexes(:dependency_proxy_blobs)
-> 0.0019s
-- remove_index(:dependency_proxy_blobs, {:algorithm=>:concurrently, :name=>"index_dependency_proxy_blobs_on_status"})
-> 0.0030s
== 20210914172202 AddStatusIndexToDependencyProxyTables: reverted (0.0357s) ===
== 20210913224558 UpdateDependencyProxyManifestsUniquenessConstraint: reverting
-- transaction_open?()
-> 0.0000s
-- index_exists?(:dependency_proxy_manifests, [:group_id, :file_name], {:unique=>true, :name=>"index_dependency_proxy_manifests_on_group_id_and_file_name", :algorithm=>:concurrently})
-> 0.0034s
-- execute("SET statement_timeout TO 0")
-> 0.0006s
-- add_index(:dependency_proxy_manifests, [:group_id, :file_name], {:unique=>true, :name=>"index_dependency_proxy_manifests_on_group_id_and_file_name", :algorithm=>:concurrently})
-> 0.0103s
-- execute("RESET statement_timeout")
-> 0.0006s
-- transaction_open?()
-> 0.0000s
-- indexes(:dependency_proxy_manifests)
-> 0.0017s
-- remove_index(:dependency_proxy_manifests, {:algorithm=>:concurrently, :name=>"index_dep_prox_manifests_on_group_id_file_name_and_status"})
-> 0.0042s
== 20210913224558 UpdateDependencyProxyManifestsUniquenessConstraint: reverted (0.0245s)
== 20210910015047 AddAppSettingsDepProxyTtlWorkerCapacityCheckConstraint: reverting
-- transaction_open?()
-> 0.0000s
-- execute("ALTER TABLE application_settings\nDROP CONSTRAINT IF EXISTS app_settings_dep_proxy_ttl_policies_worker_capacity_positive\n")
-> 0.0035s
== 20210910015047 AddAppSettingsDepProxyTtlWorkerCapacityCheckConstraint: reverted (0.0285s)
== 20210910014741 AddDependencyProxyTtlGroupPolicyWorkerCapacityToApplicationSettings: reverting
-- remove_column(:application_settings, :dependency_proxy_ttl_group_policy_worker_capacity, :smallint, {:default=>2, :null=>false})
-> 0.0107s
== 20210910014741 AddDependencyProxyTtlGroupPolicyWorkerCapacityToApplicationSettings: reverted (0.0141s)
🔎 Query Analysis
There are a number of queries introduced in these workers. To reduce clutter in this description, I've commented them where each query occurs:
- !70029 (comment 679903596)
- !70029 (comment 679903603)
- !70029 (comment 679903612)
- !70029 (comment 679903615)
- !70029 (comment 679903618)
Screenshots or screen recordings
These are strongly recommended to assist reviewers and reduce the time to merge your change.
💻 How to set up and validate locally
Numbered steps to set up and validate the change are strongly suggested.
📏 MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
-
I have evaluated the MR acceptance checklist for this MR.
Related to #294187 (closed)