Keep pipelines with artifacts_locked
-
Please check this box if this contribution uses AI-generated content (including content generated by GitLab Duo features) as outlined in the GitLab DCO & CLA. As a benefit of being a GitLab Community Contributor, you receive complimentary access to GitLab Duo.
What does this MR do and why?
Currently the retention of pipelines does not take into account if it is :artifacts_locked
. This can be quite surprising for users, as this will delete artifacts regardless of the configuration.
Before this MR, :artifacts_locked
only had an effect on the artifact retention for the cleanup job that uses expire_in
on artifacts, but not on the cleanup job for pipelines. This MR applies the setting also to pipeline retention.
This MR introduces a feature flag named :ci_skip_locked_pipelines
References
How to set up and validate locally
If you have an existing project in your GDK with older pipelines, you can start there. Gitlab-org/gitlab-shell
is a good example case that has artifacts and pipelines. Otherwise, you can also create a new project and pipelines like this:
- Enable feature flag via
rails c
:
Setup project / pipelines
- Create a new project
- Configure a pipeline that creates an artifact:
Build > Pipeline editor
:
stages:
- build
build-job:
stage: build
script:
- echo 'Hello World' >> artifact.txt
artifacts:
paths:
- artifact.txt
- Configure automatic pipeline cleanup
Settings > CI/CD > General pipelines > Automatic pipeline cleanup
to1 day
. - Make sure artifacts are supposed to be kept:
Settings > CI/CD > Artifacts > Keep artifacts from most recent successful job
. - Now run this pipeline at least twice. Make sure that one pipeline is
:artifacts_locked
and the other one is:unlocked
withCi::Pipeline.find(<id>).locked)
Test
Feature.enable(:labels_archive)
If you've created new pipelines, you'll need to change the date on them. Otherwise they won't be caught by the removal cron, unless you wait a day:
Ci::Pipeline.find(<id>).update(created_at: DateTime.now - 2.days)
You can wait for the scheduler to run at some point, but you can also trigger it like this:
Ci::ScheduleOldPipelinesRemovalCronWorker.new.perform
Ci::DestroyOldPipelinesWorker.new.perform_work
You might have to call Ci::DestroyOldPipelinesWorker.new.perform_work
a few times depending if you have the expiration configured on other projects as well. It might take a few runs until it reaches your test project.
Database Query
Query Plan for `.explain` called on Ci::DestroyOldPipelinesWorker.new.perform_work > pipelines
EXPLAIN SELECT "p_ci_pipelines".* FROM "p_ci_pipelines" WHERE "p_ci_pipelines"."project_id" = 19 AND "p_ci_pipelines"."created_at" < '2025-07-09 17:14:13.706268' AND "p_ci_pipelines"."locked" = 0 LIMIT 250
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=0.15..1.63 rows=3 width=366)
-> Append (cost=0.15..1.63 rows=3 width=366)
-> Index Scan using ci_pipelines_100_project_id_status_config_source_idx on ci_pipelines_100 p_ci_pipelines_1 (cost=0.15..1.62 rows=1 width=339)
Index Cond: (project_id = 19)
Filter: ((created_at < '2025-07-09 17:14:13.706268'::timestamp without time zone) AND (locked = 0))
-> Seq Scan on ci_pipelines_101 p_ci_pipelines_2 (cost=0.00..0.00 rows=1 width=380)
Filter: ((created_at < '2025-07-09 17:14:13.706268'::timestamp without time zone) AND (project_id = 19) AND (locked = 0))
-> Seq Scan on ci_pipelines_102 p_ci_pipelines_3 (cost=0.00..0.00 rows=1 width=380)
Filter: ((created_at < '2025-07-09 17:14:13.706268'::timestamp without time zone) AND (project_id = 19) AND (locked = 0))
(9 rows)
MR acceptance checklist
MR Checklist ( @nwittstruck)
-
Changelog entry added, if necessary -
Documentation created/updated via this MR -
Documentation reviewed by technical writer or follow-up review issue created -
Tests added for this feature/bug -
Tested in all supported browsers -
Conforms to the code review guidelines -
Conforms to the merge request performance guidelines -
Conforms to the style guides -
Conforms to the javascript style guides -
Conforms to the database guides