Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
2c3e5971
Commit
2c3e5971
authored
Apr 21, 2021
by
David Fernandez
Committed by
Bob Van Landuyt
Apr 21, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add an inner class in the cleanup container repository worker [RUN ALL RSPEC] [RUN AS-IF-FOSS]
parent
20a537c8
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
226 additions
and
156 deletions
+226
-156
app/workers/container_expiration_policies/cleanup_container_repository_worker.rb
...xpiration_policies/cleanup_container_repository_worker.rb
+109
-62
spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb
...tion_policies/cleanup_container_repository_worker_spec.rb
+108
-91
spec/workers/container_expiration_policy_worker_spec.rb
spec/workers/container_expiration_policy_worker_spec.rb
+9
-3
No files found.
app/workers/container_expiration_policies/cleanup_container_repository_worker.rb
View file @
2c3e5971
...
...
@@ -21,82 +21,34 @@ module ContainerExpirationPolicies
cleanup_tags_service_deleted_size
]
.
freeze
def
perform_work
return
unless
throttling_enabled?
return
unless
container_repository
log_extra_metadata_on_done
(
:container_repository_id
,
container_repository
.
id
)
log_extra_metadata_on_done
(
:project_id
,
project
.
id
)
unless
allowed_to_run?
(
container_repository
)
container_repository
.
cleanup_unscheduled!
log_extra_metadata_on_done
(
:cleanup_status
,
:skipped
)
return
delegate
:perform_work
,
:remaining_work_count
,
to: :inner_instance
def
inner_instance
strong_memoize
(
:inner_instance
)
do
if
loopless_enabled?
Loopless
.
new
(
self
)
else
Looping
.
new
(
self
)
end
end
result
=
ContainerExpirationPolicies
::
CleanupService
.
new
(
container_repository
)
.
execute
log_on_done
(
result
)
end
def
remaining_work_count
cleanup_scheduled_count
=
ContainerRepository
.
cleanup_scheduled
.
count
cleanup_unfinished_count
=
ContainerRepository
.
cleanup_unfinished
.
count
total_count
=
cleanup_scheduled_count
+
cleanup_unfinished_count
log_info
(
cleanup_scheduled_count:
cleanup_scheduled_count
,
cleanup_unfinished_count:
cleanup_unfinished_count
,
cleanup_total_count:
total_count
)
total_count
end
def
max_running_jobs
return
0
unless
throttling_enabled?
::
Gitlab
::
CurrentSettings
.
current_application_settings
.
container_registry_expiration_policies_worker_capacity
end
private
def
allowed_to_run?
(
container_repository
)
return
false
unless
policy
&
.
enabled
&&
policy
&
.
next_run_at
Time
.
zone
.
now
+
max_cleanup_execution_time
.
seconds
<
policy
.
next_run_at
::
Gitlab
::
CurrentSettings
.
container_registry_expiration_policies_worker_capacity
end
def
throttling_enabled?
Feature
.
enabled?
(
:container_registry_expiration_policies_throttling
)
end
def
max_cleanup_execution_time
::
Gitlab
::
CurrentSettings
.
current_application_settings
.
container_registry_delete_tags_service_timeout
end
def
policy
project
.
container_expiration_policy
def
loopless_enabled?
Feature
.
enabled?
(
:container_registry_expiration_policies_loopless
)
end
def
project
container_repository
.
project
end
def
container_repository
strong_memoize
(
:container_repository
)
do
ContainerRepository
.
transaction
do
# rubocop: disable CodeReuse/ActiveRecord
# We need a lock to prevent two workers from picking up the same row
container_repository
=
ContainerRepository
.
waiting_for_cleanup
.
order
(
:expiration_policy_cleanup_status
,
:expiration_policy_started_at
)
.
limit
(
1
)
.
lock
(
'FOR UPDATE SKIP LOCKED'
)
.
first
# rubocop: enable CodeReuse/ActiveRecord
container_repository
&
.
tap
(
&
:cleanup_ongoing!
)
end
end
def
max_cleanup_execution_time
::
Gitlab
::
CurrentSettings
.
container_registry_delete_tags_service_timeout
end
def
log_info
(
extra_structure
)
...
...
@@ -120,5 +72,100 @@ module ContainerExpirationPolicies
log_extra_metadata_on_done
(
:cleanup_tags_service_truncated
,
!!
truncated
)
log_extra_metadata_on_done
(
:running_jobs_count
,
running_jobs_count
)
end
# rubocop: disable Scalability/IdempotentWorker
# TODO: move the logic from this class to the parent one when container_registry_expiration_policies_loopless is removed
# Tracking issue: https://gitlab.com/gitlab-org/gitlab/-/issues/325273
class
Loopless
# TODO fill the logic here with the approach documented in
# https://gitlab.com/gitlab-org/gitlab/-/issues/267546#limited-worker
def
initialize
(
parent
)
@parent
=
parent
end
end
# rubocop: enable Scalability/IdempotentWorker
# rubocop: disable Scalability/IdempotentWorker
# TODO remove this class when `container_registry_expiration_policies_loopless` is removed
# Tracking issue: https://gitlab.com/gitlab-org/gitlab/-/issues/325273
class
Looping
include
Gitlab
::
Utils
::
StrongMemoize
delegate
:throttling_enabled?
,
:log_extra_metadata_on_done
,
:log_info
,
:log_on_done
,
:max_cleanup_execution_time
,
to: :@parent
def
initialize
(
parent
)
@parent
=
parent
end
def
perform_work
return
unless
throttling_enabled?
return
unless
container_repository
log_extra_metadata_on_done
(
:container_repository_id
,
container_repository
.
id
)
log_extra_metadata_on_done
(
:project_id
,
project
.
id
)
unless
allowed_to_run?
(
container_repository
)
container_repository
.
cleanup_unscheduled!
log_extra_metadata_on_done
(
:cleanup_status
,
:skipped
)
return
end
result
=
ContainerExpirationPolicies
::
CleanupService
.
new
(
container_repository
)
.
execute
log_on_done
(
result
)
end
def
remaining_work_count
cleanup_scheduled_count
=
ContainerRepository
.
cleanup_scheduled
.
count
cleanup_unfinished_count
=
ContainerRepository
.
cleanup_unfinished
.
count
total_count
=
cleanup_scheduled_count
+
cleanup_unfinished_count
log_info
(
cleanup_scheduled_count:
cleanup_scheduled_count
,
cleanup_unfinished_count:
cleanup_unfinished_count
,
cleanup_total_count:
total_count
)
total_count
end
private
def
allowed_to_run?
(
container_repository
)
return
false
unless
policy
&
.
enabled
&&
policy
&
.
next_run_at
Time
.
zone
.
now
+
max_cleanup_execution_time
.
seconds
<
policy
.
next_run_at
end
def
policy
project
.
container_expiration_policy
end
def
project
container_repository
.
project
end
def
container_repository
strong_memoize
(
:container_repository
)
do
ContainerRepository
.
transaction
do
# rubocop: disable CodeReuse/ActiveRecord
# We need a lock to prevent two workers from picking up the same row
container_repository
=
ContainerRepository
.
waiting_for_cleanup
.
order
(
:expiration_policy_cleanup_status
,
:expiration_policy_started_at
)
.
limit
(
1
)
.
lock
(
'FOR UPDATE SKIP LOCKED'
)
.
first
# rubocop: enable CodeReuse/ActiveRecord
container_repository
&
.
tap
(
&
:cleanup_ongoing!
)
end
end
end
end
# rubocop: enable Scalability/IdempotentWorker
end
end
spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb
View file @
2c3e5971
...
...
@@ -106,96 +106,102 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
end
end
context
'with repository in cleanup scheduled state'
do
it_behaves_like
'handling all repository conditions'
end
context
'with repository in cleanup unfinished state'
do
context
'with loopless disabled'
do
before
do
repository
.
cleanup_unfinished!
stub_feature_flags
(
container_registry_expiration_policies_loopless:
false
)
end
it_behaves_like
'handling all repository conditions'
end
context
'with another repository in cleanup unfinished state'
do
let_it_be
(
:another_repository
)
{
create
(
:container_repository
,
:cleanup_unfinished
)
}
context
'with repository in cleanup scheduled state'
do
it_behaves_like
'handling all repository conditions'
end
it
'process the cleanup scheduled repository first'
do
service_response
=
cleanup_service_response
(
repository:
repository
)
expect
(
ContainerExpirationPolicies
::
CleanupService
)
.
to
receive
(
:new
).
with
(
repository
).
and_return
(
double
(
execute:
service_response
))
expect_log_extra_metadata
(
service_response:
service_response
)
context
'with repository in cleanup unfinished state'
do
before
do
repository
.
cleanup_unfinished!
end
subject
it_behaves_like
'handling all repository conditions'
end
end
context
'with multiple repositories in cleanup unfinished state'
do
let_it_be
(
:repository2
)
{
create
(
:container_repository
,
:cleanup_unfinished
,
expiration_policy_started_at:
20
.
minutes
.
ago
)
}
let_it_be
(
:repository3
)
{
create
(
:container_repository
,
:cleanup_unfinished
,
expiration_policy_started_at:
10
.
minutes
.
ago
)
}
context
'with another repository in cleanup unfinished state'
do
let_it_be
(
:another_repository
)
{
create
(
:container_repository
,
:cleanup_unfinished
)
}
before
do
repository
.
update!
(
expiration_policy_cleanup_status: :cleanup_unfinished
,
expiration_policy_started_at:
30
.
minutes
.
ago
)
it
'process the cleanup scheduled repository first'
do
service_response
=
cleanup_service_response
(
repository:
repository
)
expect
(
ContainerExpirationPolicies
::
CleanupService
)
.
to
receive
(
:new
).
with
(
repository
).
and_return
(
double
(
execute:
service_response
))
expect_log_extra_metadata
(
service_response:
service_response
)
subject
end
end
it
'process the repository with the oldest expiration_policy_started_at'
do
service_response
=
cleanup_service_response
(
repository:
repository
)
expect
(
ContainerExpirationPolicies
::
CleanupService
)
.
to
receive
(
:new
).
with
(
repository
).
and_return
(
double
(
execute:
service_response
))
expect_log_extra_metadata
(
service_response:
service_response
)
context
'with multiple repositories in cleanup unfinished state'
do
let_it_be
(
:repository2
)
{
create
(
:container_repository
,
:cleanup_unfinished
,
expiration_policy_started_at:
20
.
minutes
.
ago
)
}
let_it_be
(
:repository3
)
{
create
(
:container_repository
,
:cleanup_unfinished
,
expiration_policy_started_at:
10
.
minutes
.
ago
)
}
subject
end
end
before
do
repository
.
update!
(
expiration_policy_cleanup_status: :cleanup_unfinished
,
expiration_policy_started_at:
30
.
minutes
.
ago
)
end
context
'with repository in cleanup ongoing state'
do
before
do
repository
.
cleanup_ongoing!
it
'process the repository with the oldest expiration_policy_started_at'
do
service_response
=
cleanup_service_response
(
repository:
repository
)
expect
(
ContainerExpirationPolicies
::
CleanupService
)
.
to
receive
(
:new
).
with
(
repository
).
and_return
(
double
(
execute:
service_response
))
expect_log_extra_metadata
(
service_response:
service_response
)
subject
end
end
it
'does not process it'
do
expect
(
Projects
::
ContainerRepository
::
CleanupTagsService
).
not_to
receive
(
:new
)
context
'with repository in cleanup ongoing state'
do
before
do
repository
.
cleanup_ongoing!
end
expect
{
subject
}.
not_to
change
{
ContainerRepository
.
waiting_for_cleanup
.
count
}
expect
(
repository
.
cleanup_ongoing?
).
to
be_truthy
end
end
it
'does not process it'
do
expect
(
Projects
::
ContainerRepository
::
CleanupTagsService
).
not_to
receive
(
:new
)
context
'with no repository in any cleanup state'
do
before
do
repository
.
cleanup_unscheduled!
expect
{
subject
}.
not_to
change
{
ContainerRepository
.
waiting_for_cleanup
.
count
}
expect
(
repository
.
cleanup_ongoing?
).
to
be_truthy
end
end
it
'does not process it'
do
expect
(
Projects
::
ContainerRepository
::
CleanupTagsService
).
not_to
receive
(
:new
)
context
'with no repository in any cleanup state'
do
before
do
repository
.
cleanup_unscheduled!
end
expect
{
subject
}.
not_to
change
{
ContainerRepository
.
waiting_for_cleanup
.
count
}
expect
(
repository
.
cleanup_unscheduled?
).
to
be_truthy
end
end
it
'does not process it'
do
expect
(
Projects
::
ContainerRepository
::
CleanupTagsService
).
not_to
receive
(
:new
)
context
'with no container repository waiting'
do
before
do
repository
.
destroy!
expect
{
subject
}.
not_to
change
{
ContainerRepository
.
waiting_for_cleanup
.
count
}
expect
(
repository
.
cleanup_unscheduled?
).
to
be_truthy
end
end
it
'does not execute the cleanup tags service'
do
expect
(
Projects
::
ContainerRepository
::
CleanupTagsService
).
not_to
receive
(
:new
)
context
'with no container repository waiting'
do
before
do
repository
.
destroy!
end
expect
{
subject
}.
not_to
change
{
ContainerRepository
.
waiting_for_cleanup
.
count
}
end
end
it
'does not execute the cleanup tags service'
do
expect
(
Projects
::
ContainerRepository
::
CleanupTagsService
).
not_to
receive
(
:new
)
context
'with feature flag disabled'
do
before
do
stub_feature_flags
(
container_registry_expiration_policies_throttling:
false
)
expect
{
subject
}.
not_to
change
{
ContainerRepository
.
waiting_for_cleanup
.
count
}
end
end
it
'is a no-op'
do
expect
(
Projects
::
ContainerRepository
::
CleanupTagsService
).
not_to
receive
(
:new
)
context
'with feature flag disabled'
do
before
do
stub_feature_flags
(
container_registry_expiration_policies_throttling:
false
)
end
it
'is a no-op'
do
expect
(
Projects
::
ContainerRepository
::
CleanupTagsService
).
not_to
receive
(
:new
)
expect
{
subject
}.
not_to
change
{
ContainerRepository
.
waiting_for_cleanup
.
count
}
expect
{
subject
}.
not_to
change
{
ContainerRepository
.
waiting_for_cleanup
.
count
}
end
end
end
...
...
@@ -230,37 +236,42 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
describe
'#remaining_work_count'
do
subject
{
worker
.
remaining_work_count
}
context
'with container repositoires waiting for cleanup'
do
let_it_be
(
:unfinished_repositories
)
{
create_list
(
:container_repository
,
2
,
:cleanup_unfinished
)
}
context
'with loopless disabled'
do
before
do
stub_feature_flags
(
container_registry_expiration_policies_loopless:
false
)
end
context
'with container repositoires waiting for cleanup'
do
let_it_be
(
:unfinished_repositories
)
{
create_list
(
:container_repository
,
2
,
:cleanup_unfinished
)
}
it
{
is_expected
.
to
eq
(
3
)
}
it
{
is_expected
.
to
eq
(
3
)
}
it
'logs the work count'
do
expect_log_info
(
cleanup_scheduled_count:
1
,
cleanup_unfinished_count:
2
,
cleanup_total_count:
3
)
it
'logs the work count'
do
expect_log_info
(
cleanup_scheduled_count:
1
,
cleanup_unfinished_count:
2
,
cleanup_total_count:
3
)
subject
subject
end
end
end
context
'with no container repositories waiting for cleanup'
do
before
do
repository
.
cleanup_ongoing!
end
context
'with no container repositories waiting for cleanup'
do
before
do
repository
.
cleanup_ongoing!
end
it
{
is_expected
.
to
eq
(
0
)
}
it
{
is_expected
.
to
eq
(
0
)
}
it
'logs 0 work count'
do
expect_log_info
(
cleanup_scheduled_count:
0
,
cleanup_unfinished_count:
0
,
cleanup_total_count:
0
)
it
'logs 0 work count'
do
expect_log_info
(
cleanup_scheduled_count:
0
,
cleanup_unfinished_count:
0
,
cleanup_total_count:
0
)
subject
subject
end
end
end
end
...
...
@@ -274,14 +285,20 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
stub_application_setting
(
container_registry_expiration_policies_worker_capacity:
capacity
)
end
it
{
is_expected
.
to
eq
(
capacity
)
}
context
'with feature flag disabled'
do
context
'with loopless disabled'
do
before
do
stub_feature_flags
(
container_registry_expiration_policies_
throttling
:
false
)
stub_feature_flags
(
container_registry_expiration_policies_
loopless
:
false
)
end
it
{
is_expected
.
to
eq
(
0
)
}
it
{
is_expected
.
to
eq
(
capacity
)
}
context
'with feature flag disabled'
do
before
do
stub_feature_flags
(
container_registry_expiration_policies_throttling:
false
)
end
it
{
is_expected
.
to
eq
(
0
)
}
end
end
end
...
...
spec/workers/container_expiration_policy_worker_spec.rb
View file @
2c3e5971
...
...
@@ -35,10 +35,16 @@ RSpec.describe ContainerExpirationPolicyWorker do
end
context
'With no container expiration policies'
do
it
'does not execute any policies'
do
expect
(
ContainerRepository
).
not_to
receive
(
:for_project_id
)
context
'with loopless disabled'
do
before
do
stub_feature_flags
(
container_registry_expiration_policies_loopless:
false
)
end
expect
{
subject
}.
not_to
change
{
ContainerRepository
.
cleanup_scheduled
.
count
}
it
'does not execute any policies'
do
expect
(
ContainerRepository
).
not_to
receive
(
:for_project_id
)
expect
{
subject
}.
not_to
change
{
ContainerRepository
.
cleanup_scheduled
.
count
}
end
end
end
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment