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
...
@@ -21,82 +21,34 @@ module ContainerExpirationPolicies
cleanup_tags_service_deleted_size
cleanup_tags_service_deleted_size
]
.
freeze
]
.
freeze
def
perform_work
delegate
:perform_work
,
:remaining_work_count
,
to: :inner_instance
return
unless
throttling_enabled?
return
unless
container_repository
def
inner_instance
strong_memoize
(
:inner_instance
)
do
log_extra_metadata_on_done
(
:container_repository_id
,
container_repository
.
id
)
if
loopless_enabled?
log_extra_metadata_on_done
(
:project_id
,
project
.
id
)
Loopless
.
new
(
self
)
else
unless
allowed_to_run?
(
container_repository
)
Looping
.
new
(
self
)
container_repository
.
cleanup_unscheduled!
end
log_extra_metadata_on_done
(
:cleanup_status
,
:skipped
)
return
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
end
def
max_running_jobs
def
max_running_jobs
return
0
unless
throttling_enabled?
return
0
unless
throttling_enabled?
::
Gitlab
::
CurrentSettings
.
current_application_settings
.
container_registry_expiration_policies_worker_capacity
::
Gitlab
::
CurrentSettings
.
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
end
end
def
throttling_enabled?
def
throttling_enabled?
Feature
.
enabled?
(
:container_registry_expiration_policies_throttling
)
Feature
.
enabled?
(
:container_registry_expiration_policies_throttling
)
end
end
def
max_cleanup_execution_time
def
loopless_enabled?
::
Gitlab
::
CurrentSettings
.
current_application_settings
.
container_registry_delete_tags_service_timeout
Feature
.
enabled?
(
:container_registry_expiration_policies_loopless
)
end
def
policy
project
.
container_expiration_policy
end
end
def
project
def
max_cleanup_execution_time
container_repository
.
project
::
Gitlab
::
CurrentSettings
.
container_registry_delete_tags_service_timeout
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
def
log_info
(
extra_structure
)
def
log_info
(
extra_structure
)
...
@@ -120,5 +72,100 @@ module ContainerExpirationPolicies
...
@@ -120,5 +72,100 @@ module ContainerExpirationPolicies
log_extra_metadata_on_done
(
:cleanup_tags_service_truncated
,
!!
truncated
)
log_extra_metadata_on_done
(
:cleanup_tags_service_truncated
,
!!
truncated
)
log_extra_metadata_on_done
(
:running_jobs_count
,
running_jobs_count
)
log_extra_metadata_on_done
(
:running_jobs_count
,
running_jobs_count
)
end
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
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
...
@@ -106,96 +106,102 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
end
end
end
end
context
'with repository in cleanup scheduled state'
do
context
'with loopless disabled'
do
it_behaves_like
'handling all repository conditions'
end
context
'with repository in cleanup unfinished state'
do
before
do
before
do
repository
.
cleanup_unfinished!
stub_feature_flags
(
container_registry_expiration_policies_loopless:
false
)
end
end
it_behaves_like
'handling all repository conditions'
context
'with repository in cleanup scheduled state'
do
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
)
}
it
'process the cleanup scheduled repository first'
do
context
'with repository in cleanup unfinished state'
do
service_response
=
cleanup_service_response
(
repository:
repository
)
before
do
expect
(
ContainerExpirationPolicies
::
CleanupService
)
repository
.
cleanup_unfinished!
.
to
receive
(
:new
).
with
(
repository
).
and_return
(
double
(
execute:
service_response
))
end
expect_log_extra_metadata
(
service_response:
service_response
)
subject
it_behaves_like
'handling all repository conditions'
end
end
end
context
'with multiple repositories in cleanup unfinished state'
do
context
'with another repository in cleanup unfinished state'
do
let_it_be
(
:repository2
)
{
create
(
:container_repository
,
:cleanup_unfinished
,
expiration_policy_started_at:
20
.
minutes
.
ago
)
}
let_it_be
(
:another_repository
)
{
create
(
:container_repository
,
:cleanup_unfinished
)
}
let_it_be
(
:repository3
)
{
create
(
:container_repository
,
:cleanup_unfinished
,
expiration_policy_started_at:
10
.
minutes
.
ago
)
}
before
do
it
'process the cleanup scheduled repository first'
do
repository
.
update!
(
expiration_policy_cleanup_status: :cleanup_unfinished
,
expiration_policy_started_at:
30
.
minutes
.
ago
)
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
end
it
'process the repository with the oldest expiration_policy_started_at'
do
context
'with multiple repositories in cleanup unfinished state'
do
service_response
=
cleanup_service_response
(
repository:
repository
)
let_it_be
(
:repository2
)
{
create
(
:container_repository
,
:cleanup_unfinished
,
expiration_policy_started_at:
20
.
minutes
.
ago
)
}
expect
(
ContainerExpirationPolicies
::
CleanupService
)
let_it_be
(
:repository3
)
{
create
(
:container_repository
,
:cleanup_unfinished
,
expiration_policy_started_at:
10
.
minutes
.
ago
)
}
.
to
receive
(
:new
).
with
(
repository
).
and_return
(
double
(
execute:
service_response
))
expect_log_extra_metadata
(
service_response:
service_response
)
subject
before
do
end
repository
.
update!
(
expiration_policy_cleanup_status: :cleanup_unfinished
,
expiration_policy_started_at:
30
.
minutes
.
ago
)
end
end
context
'with repository in cleanup ongoing state'
do
it
'process the repository with the oldest expiration_policy_started_at'
do
before
do
service_response
=
cleanup_service_response
(
repository:
repository
)
repository
.
cleanup_ongoing!
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
end
it
'does not process it'
do
context
'with repository in cleanup ongoing state'
do
expect
(
Projects
::
ContainerRepository
::
CleanupTagsService
).
not_to
receive
(
:new
)
before
do
repository
.
cleanup_ongoing!
end
expect
{
subject
}.
not_to
change
{
ContainerRepository
.
waiting_for_cleanup
.
count
}
it
'does not process it'
do
expect
(
repository
.
cleanup_ongoing?
).
to
be_truthy
expect
(
Projects
::
ContainerRepository
::
CleanupTagsService
).
not_to
receive
(
:new
)
end
end
context
'with no repository in any cleanup state'
do
expect
{
subject
}.
not_to
change
{
ContainerRepository
.
waiting_for_cleanup
.
count
}
before
do
expect
(
repository
.
cleanup_ongoing?
).
to
be_truthy
repository
.
cleanup_unscheduled!
end
end
end
it
'does not process it'
do
context
'with no repository in any cleanup state'
do
expect
(
Projects
::
ContainerRepository
::
CleanupTagsService
).
not_to
receive
(
:new
)
before
do
repository
.
cleanup_unscheduled!
end
expect
{
subject
}.
not_to
change
{
ContainerRepository
.
waiting_for_cleanup
.
count
}
it
'does not process it'
do
expect
(
repository
.
cleanup_unscheduled?
).
to
be_truthy
expect
(
Projects
::
ContainerRepository
::
CleanupTagsService
).
not_to
receive
(
:new
)
end
end
context
'with no container repository waiting'
do
expect
{
subject
}.
not_to
change
{
ContainerRepository
.
waiting_for_cleanup
.
count
}
before
do
expect
(
repository
.
cleanup_unscheduled?
).
to
be_truthy
repository
.
destroy!
end
end
end
it
'does not execute the cleanup tags service'
do
context
'with no container repository waiting'
do
expect
(
Projects
::
ContainerRepository
::
CleanupTagsService
).
not_to
receive
(
:new
)
before
do
repository
.
destroy!
end
expect
{
subject
}.
not_to
change
{
ContainerRepository
.
waiting_for_cleanup
.
count
}
it
'does not execute the cleanup tags service'
do
end
expect
(
Projects
::
ContainerRepository
::
CleanupTagsService
).
not_to
receive
(
:new
)
end
context
'with feature flag disabled'
do
expect
{
subject
}.
not_to
change
{
ContainerRepository
.
waiting_for_cleanup
.
count
}
before
do
end
stub_feature_flags
(
container_registry_expiration_policies_throttling:
false
)
end
end
it
'is a no-op'
do
context
'with feature flag disabled'
do
expect
(
Projects
::
ContainerRepository
::
CleanupTagsService
).
not_to
receive
(
:new
)
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
end
end
...
@@ -230,37 +236,42 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
...
@@ -230,37 +236,42 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
describe
'#remaining_work_count'
do
describe
'#remaining_work_count'
do
subject
{
worker
.
remaining_work_count
}
subject
{
worker
.
remaining_work_count
}
context
'with container repositoires waiting for cleanup'
do
context
'with loopless disabled'
do
let_it_be
(
:unfinished_repositories
)
{
create_list
(
:container_repository
,
2
,
:cleanup_unfinished
)
}
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
it
'logs the work count'
do
expect_log_info
(
expect_log_info
(
cleanup_scheduled_count:
1
,
cleanup_scheduled_count:
1
,
cleanup_unfinished_count:
2
,
cleanup_unfinished_count:
2
,
cleanup_total_count:
3
cleanup_total_count:
3
)
)
subject
subject
end
end
end
end
context
'with no container repositories waiting for cleanup'
do
context
'with no container repositories waiting for cleanup'
do
before
do
before
do
repository
.
cleanup_ongoing!
repository
.
cleanup_ongoing!
end
end
it
{
is_expected
.
to
eq
(
0
)
}
it
{
is_expected
.
to
eq
(
0
)
}
it
'logs 0 work count'
do
it
'logs 0 work count'
do
expect_log_info
(
expect_log_info
(
cleanup_scheduled_count:
0
,
cleanup_scheduled_count:
0
,
cleanup_unfinished_count:
0
,
cleanup_unfinished_count:
0
,
cleanup_total_count:
0
cleanup_total_count:
0
)
)
subject
subject
end
end
end
end
end
end
end
...
@@ -274,14 +285,20 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
...
@@ -274,14 +285,20 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
stub_application_setting
(
container_registry_expiration_policies_worker_capacity:
capacity
)
stub_application_setting
(
container_registry_expiration_policies_worker_capacity:
capacity
)
end
end
it
{
is_expected
.
to
eq
(
capacity
)
}
context
'with loopless disabled'
do
context
'with feature flag disabled'
do
before
do
before
do
stub_feature_flags
(
container_registry_expiration_policies_
throttling
:
false
)
stub_feature_flags
(
container_registry_expiration_policies_
loopless
:
false
)
end
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
end
end
...
...
spec/workers/container_expiration_policy_worker_spec.rb
View file @
2c3e5971
...
@@ -35,10 +35,16 @@ RSpec.describe ContainerExpirationPolicyWorker do
...
@@ -35,10 +35,16 @@ RSpec.describe ContainerExpirationPolicyWorker do
end
end
context
'With no container expiration policies'
do
context
'With no container expiration policies'
do
it
'does not execute any policies'
do
context
'with loopless disabled'
do
expect
(
ContainerRepository
).
not_to
receive
(
:for_project_id
)
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
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