jenkinsci / jenkinsci/gitlab-branch-source-plugin

Owner folder is saved once per branch head during indexing since PR #768

Open
#770 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
134
Forks
113
PR merge metrics
No merged PRs in 30d

Description

### Jenkins and plugins versions report

The upgrade in question:

```
gitlab-branch-source 740.v04f287f9194d -> 743.ve0c8154a_8da_b_
Jenkins core 2.568.1 -> 2.568.2
```

Full plugin list below.

Environment


```text
Jenkins: 2.568.2
OS: Windows Server 2019 - 10.0
Java: 21.0.9 - Eclipse Adoptium (OpenJDK 64-Bit Server VM)
---
active-directory:2.967.vf4e254546a_b_3
ant:520.vd082ecfb_16a_9
antisamy-markup-formatter:173.v680e3a_b_69ff3
apache-httpcomponents-client-4-api:4.5.14-269.vfa_2321039a_83
apache-httpcomponents-client-5-api:5.6.4-204.vfa_29df89ffcf
artifact-manager-s3:986.v7c9a_d15576b_b_
asm-api:9.10.1-216.va_9256d3b_844b_
audit-log:1.3
audit-trail:456.v39d2fd1ed556
authentication-tokens:1.144.v5ff4a_5ec5c33
aws-credentials:265.v8422a_f384cd9
aws-global-configuration:165.v712c4a_e4a_078
aws-java-sdk:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-api-gateway:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-autoscaling:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-cloudformation:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-cloudfront:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-cloudwatch:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-codebuild:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-codedeploy:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-ec2:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-ecr:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-ecs:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-efs:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-elasticbeanstalk:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-elasticloadbalancingv2:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-iam:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-kinesis:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-lambda:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-logs:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-minimal:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-organizations:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-secretsmanager:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-sns:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-sqs:1.12.780-480.v4a_0819121a_9e
aws-java-sdk-ssm:1.12.780-480.v4a_0819121a_9e
aws-java-sdk2-core:2.42.33-70.vd69c0763fa_60
aws-java-sdk2-ec2:2.42.33-70.vd69c0763fa_60
aws-java-sdk2-s3:2.42.33-70.vd69c0763fa_60
aws-lambda:0.5.10
blueocean:1.27.26
blueocean-autofavorite:1.2.5
blueocean-bitbucket-pipeline:1.27.26
blueocean-commons:1.27.26
blueocean-config:1.27.26
blueocean-core-js:1.27.26
blueocean-dashboard:1.27.26
blueocean-display-url:2.4.4
blueocean-events:1.27.26
blueocean-git-pipeline:1.27.26
blueocean-github-pipeline:1.27.26
blueocean-i18n:1.27.26
blueocean-jwt:1.27.26
blueocean-personalization:1.27.26
blueocean-pipeline-api-impl:1.27.26
blueocean-pipeline-editor:1.27.26
blueocean-pipeline-scm-api:1.27.26
blueocean-rest:1.27.26
blueocean-rest-impl:1.27.26
blueocean-web:1.27.26
bootstrap5-api:5.3.8-1048.va_c299057e35c
bouncycastle-api:2.30.1.84-291.v9f17b_21896e2
branch-api:2.1280.v0d4e5b_b_460ef
build-timeout:1.41
build-user-vars-plugin:214.va_eed2ed849ca_
caffeine-api:3.2.4-208.v7e2da_a_7db_82b_
checks-api:415.vf022234a_931d
cloud-stats:423.v63f017a_f582c
cloudbees-bitbucket-branch-source:937.3.8
cloudbees-folder:6.1106.v3a_d9a_6d2465e
codesonar:3.6.0
command-launcher:134.v025a_5fcf9dea_
commons-collections4-api:4.5.0-8.va_d5448ef9011
commons-compress-api:1.28.0-87.v48a_8104cb_b_25
commons-lang3-api:3.20.0-109.ve43756e2d2b_4
commons-text-api:1.15.0-218.va_61573470393
conditional-buildstep:1.5.0
copyartifact:795.ve8e151429b_27
credentials:1511.v2e3cb_0008ef0
credentials-binding:728.v902a_273b_8947
data-tables-api:2.3.8-1570.v1cb_1cd2a_0fb_c
dependency-check-jenkins-plugin:5.6.4
dependency-track:7.1.0
disk-usage:573.v5269a_8d16a_db_
display-url-api:2.217.va_6b_de84cc74b_
docker-commons:477.v289085a_b_6896
docker-java-api:3.7.1-136.v5d70f77a_c3d6
docker-plugin:1327.v9524f1ee134e
docker-workflow:653.v2f2c08eff0ec
durable-task:686.v80ff80875b_82
echarts-api:6.1.0-1306.vcee1648c16a_4
eddsa-api:0.3.0.1-29.v67e9a_1c969b_b_
email-ext:2038.v7b_8817a_499d9
favorite:2.267.vb_90d08408081
file-parameters:425.v3fa_801681b_5e
filesystem-list-parameter-plugin:0.0.18
font-awesome-api:7.3.1-1013.v0835a_879ec6d
git:5.10.1
git-client:6.6.1
git-server:137.ve0060b_432302
github:1.47.0
github-api:1.330-492.v3941a_032db_2a_
github-branch-source:1983.vfa_27ed961853
gitlab-api:6.3.0-126.vd066b_d896159
gitlab-branch-source:743.ve0c8154a_8da_b_
gitlab-merge-request-jenkins:2.0.0
gitlab-oauth:1.30
gitlab-plugin:1.9.16
global-slack-notifier:1.5
gradle:2.19.1252.v15196b_5a_6e10
gson-api:2.14.0-201.v8eefe5515533
handy-uri-templates-2-api:2.1.8-38.vcea_5d521d5f3
htmlpublisher:429
instance-identity:203.v15e81a_1b_7a_38
ionicons-api:94.vcc3065403257
jackson-annotations2-api:2.22-19.v10a_a_582ea_26e
jackson2-api:2.22.1-443.vc91f592333c4
jackson3-api:3.2.1-92.vd25c2e23c180
jakarta-activation-api:2.1.4-1
jakarta-mail-api:2.1.5-1
jakarta-xml-bind-api:4.0.9-19.v2b_a_5b_44d9a_1c
javadoc:354.vee1a_660b_4990
javax-activation-api:1.2.0-8
javax-mail-api:1.6.2-11
jaxb:2.3.9-143.v5979df3304e6
jdk-tool:83.v417146707a_3d
jenkins-design-language:1.27.26
jersey2-api:2.48-180.ve47b_264f849b_
jersey3-api:3.1.11-4.v77818819c2e1
jjwt-api:0.13.0-141.vd58b_a_9592b_6c
jnr-posix-api:3.2.1-213.v7401d99f6220
joda-time-api:2.14.3-200.v65623733c99f
jquery3-api:3.7.1-687.v68d468e40b_30
jsch:0.2.16-95.v3eecb_55fa_b_78
json-api:20260719-223.va_81f828cdb_58
json-path-api:3.0.0-218.vcd4dd1355de2
jsoup:1.23.1-103.v4fde9422cc6f
junit:1418.v67a_81935603c
ldap:807.809.vd3a_4e5e4ec98
lockable-resources:1539.v4db_b_fc1cc115
mailer:534.v1b_36f5864073
mapdb-api:1.0.9-44.va_1e1310c9118
matrix-auth:3.3
matrix-project:905.vcc6831e8760a_
maven-plugin:3.27
metrics:4.2.37-494.v06f9a_939d33a_
mina-sshd-api-common:2.19.0-192.v2b_a_7b_2c1dc71
mina-sshd-api-core:2.19.0-192.v2b_a_7b_2c1dc71
mstest:1.0.5
multibranch-build-strategy-extension:92.v6a_3f6939331d
nodelabelparameter:851.vd94e5048d321
nuget:1.1
okhttp-api:5.3.2-200.vedb_720a_cf1f8
oss-symbols-api:465.v25d9fdc88c26
pam-auth:1.12
pipeline-aws:1.45
pipeline-build-step:599.v4b_67ea_11b_152
pipeline-github-lib:65.v203688e7727e
pipeline-graph-analysis:254.v0f63a_a_447dca_
pipeline-groovy-lib:798.v5cc688825312
pipeline-input-step:560.v56198a_642157
pipeline-milestone-step:152.v6e22b_8cfc66c
pipeline-model-api:2.2293.v6e7193cec599
pipeline-model-definition:2.2293.v6e7193cec599
pipeline-model-extensions:2.2293.v6e7193cec599
pipeline-rest-api:2.41
pipeline-stage-step:345.va_96187909426
pipeline-stage-tags-metadata:2.2293.v6e7193cec599
pipeline-stage-view:2.41
plain-credentials:199.v9f8e1f741799
plugin-usage-plugin:418.v308c3c863d25
plugin-util-api:7.1341.v039f146993d9
powershell:185.v7a_026da_c54ee
prism-api:1.30.0-741.v034eb_0b_0a_a_fa_
pubsub-light:1.19
rebuild:338.va_0a_b_50e29397
resource-disposer:0.25
run-condition:276.v97298f3a_cd51
scm-api:728.vc30dcf7a_0df5
script-security:1412.v7737b_3405f86
slack:795.v4b_9705b_e6d47
snakeyaml-api:2.5-149.v72471e9c6371
snakeyaml-engine-api:3.1.1-12.v4320c7d6f89c
sonar:2.18.3
sse-gateway:1.29
ssh-credentials:372.va_250881b_08cd
ssh-slaves:3.1097.v868116049892
sshd:3.384.vc89b_5e138cf9
structs:362.va_b_695ef4fdf9
subversion:1303.vcfd9679fb_c12
support-core:1840.v08d78d1b_4db_c
thinBackup:2.1.4
timestamper:1.30
token-macro:477.vd4f0dc3cb_cf1
trilead-api:2.284.v1974ea_324382
variant:70.va_d9f17f859e0
versioncolumn:400.v3c5c3004f31d
woodstox-core-api:7.2.2-10.vcb_629759b_2c2
workflow-aggregator:608.v67378e9d3db_1
workflow-api:1413.v2ff1a_5e720fa_
workflow-basic-steps:1098.v808b_fd7f8cf4
workflow-cps:4362.v9b_991749898e
workflow-durable-task-step:1479.v56e587f413a_7
workflow-job:1590.v49101d088542
workflow-multibranch:841.vec5b_9e1806ec
workflow-scm-step:466.va_d69e602552b_
workflow-step-api:724.v538c2362b_dfb_
workflow-support:1015.v785e5a_b_b_8b_22
ws-cleanup:0.49

```

### What Operating System are you using (both controller, and any agents involved in the problem)?

Controller: Windows Server 2019 (10.0), JDK 21.0.9.

Branch indexing runs on the built-in node. The threads are named "Executor #-1 for Built-In Node : executing BranchIndexing[\]" and the write is to the controller's own config.xml, so no agents are involved in this behavior.

### Reproduction steps

1. Install gitlab-branch-source `743.ve0c8154a_8da_b_`.
2. Configure a GitLab multibranch pipeline folder with several branches.
3. Let periodic branch indexing run, or trigger "Scan GitLab Project Now".
4. With the Audit Trail plugin installed, count the `updateItem` entries logged for that folder during a single scan. They carry the folder's own `itemName` and `itemUri` rather than a branch's.

In our logs, on `740.v04f287f9194d` there is one such entry per folder per scan. On `743.ve0c8154a_8da_b_` the count per scan matches the folder's branch count. Our folders hold roughly 5 to 400 branches each, and the entry counts track that range.

I have not tested this on a clean install. This is from our own instance before and after the upgrade.

### Expected Results

One audit `updateItem` entry for the folder per indexing scan, which is what our logs show on `740.v04f287f9194d`.

### Actual Results

One `updateItem` entry per branch, per folder, per scan. A folder with 47 branches now logs 47 entries where it logged one before, and the per scan count matches each folder's branch count.

What I noticed first was log volume. Our daily Audit Trail log was stable for 226 consecutive days, from 2025-12-27 (the oldest file we retain) through 2026-08-09:

```
mean 19.06 MiB/day, min 16.85, max 30.40
only 7 of those 226 days exceeded 22 MiB
```

Image

During a maintenance window on the night of 2026-08-10, I updated Jenkins core from 2.568.1 to 2.568.2 along with a batch of plugins. That batch included gitlab-branch-source, which went from `740.v04f287f9194d` to `743.ve0c8154a_8da_b_`. Since then:

```
2026-08-10 52.75 MiB (change began late that evening)
2026-08-11 607.92 MiB
2026-08-12 613.41 MiB
2026-08-13 619.47 MiB
2026-08-14 599.00 MiB
2026-08-15 621.63 MiB
2026-08-16 625.62 MiB
```

That is a 32x jump in the daily mean. The quietest day since the update is still 19.7x larger than the single busiest day in the preceding 226.

Image

Nothing else about our setup changed. No new projects, no configuration changes, and our build activity actually went down over the same period.

As for severity, Jenkins is still working normally and nothing is broken on our end. The cost is storage, plus disk writes if the analysis below is right. We're seeing roughly 600 MiB per day of extra log volume, or about 18 GiB a month. It also makes our log processing more cumbersome, though that part is specific to our workflow.

I'm reporting it because the analysis in the "Anything else" section below points to an unintended side effect rather than intended behavior, and suggests the underlying change is to how often config.xml gets written rather than just to log volume. I can't evaluate that part myself, so whether it matters in practice is a better judgment call for you than for me.

### Anything else?

## Everything in this section is AI analysis

The rest of this report was produced by Claude Code. It analyzed our audit logs, gave me the scripts I ran on the controller, and drew the conclusions below. The stack trace is real output from our instance, and the measurements come from our audit logs. Identifying the root cause and suggesting the fix are its work, not mine, so please evaluate them on their own merits.

---

### What happens

`saveTimestampInOwner()` is called unconditionally at the top of two `retrieveActions` overloads. One of them, `retrieveActions(SCMHead, SCMHeadEvent, TaskListener)`, is invoked by branch-api once per branch head on every indexing pass, via `SCMHeadObserverImpl.setBranchActions` → `SCMSource.fetchActions`. Each call performs a full `owner.save()`.

The effect is that a multibranch folder's `config.xml` is rewritten once per branch per scan instead of once per scan.

### Stack trace

Captured on this controller via a temporary `ItemListener`:

```
ItemListener.fireOnUpdated(ItemListener.java:217)
AbstractFolder.save(AbstractFolder.java:1108)
MultiBranchProject.save(MultiBranchProject.java:516)
GitLabSCMSource.saveOwner(GitLabSCMSource.java:332)
GitLabSCMSource.saveTimestampInOwner(GitLabSCMSource.java:353)
GitLabSCMSource.retrieveActions(GitLabSCMSource.java:735)
SCMSource.fetchActions(SCMSource.java:811)
MultiBranchProject$SCMHeadObserverImpl.setBranchActions(MultiBranchProject.java:2225)
MultiBranchProject$SCMHeadObserverImpl.observe(MultiBranchProject.java:2054)
SCMSourceRequest.process(SCMSourceRequest.java:357)
GitLabSCMSource.retrieve(GitLabSCMSource.java:491)
SCMSource.fetch(SCMSource.java:278)
MultiBranchProject.computeChildren(MultiBranchProject.java:693)
ComputedFolder.updateChildren(ComputedFolder.java:274)
MultiBranchProject$BranchIndexing.run(MultiBranchProject.java:1096)
```

### Why #768 and not something else in the batch

The plugin upgrade was `740.v04f287f9194d` → `743.ve0c8154a_8da_b_`. Per the commit history for `GitLabSCMSource.java`, the only two commits in that window are #495 (2026-07-27) and #768 (2026-08-03). The stack trace above names `saveTimestampInOwner()`, which #768 introduced.

Jenkins core was also updated in the same window, 2.568.1 → 2.568.2. Noting it for completeness, but the stack trace shows the save originating in `GitLabSCMSource`, and the 2.568.2 changelog contains no changes to item or configuration persistence, so core does not appear to be implicated.

### Measurements

Derived from this instance's audit logs. Roughly 85 multibranch folders, scanned every 2 minutes (most) or 5 minutes (the rest). Branch counts per folder range from about 5 to about 400.

```
Folder saves/day before: 36,615 (one per scan)
Folder saves/day after: 1,252,318 (one per branch per scan) - 34x
Scan count/day: 36,615 before, 35,534 after (unchanged)
```

Saves per scan equal each folder's branch count, and track branch churn in real time: one folder went from 10 to 11 saves per scan the moment a branch job was created, another went from 8 to 7 when a branch was removed.

The distribution is heavily skewed — the single largest folder accounts for about 21% of the total, and the top five for about 58%. An instance with only a few branches per repository would likely not notice this at all.

Onset was immediate: the controller restarted during the maintenance window, and the first indexing cycles after startup produced the first multi save scan at 22:33:14 US Central on the evening of 2026-08-10.

The log growth is the visible symptom; the underlying change is ~1.25M `config.xml` writes per day where there were ~36,000.

### Suggested fix

The stated motivation for #768 is job-dsl: `jobDsl()` invokes `retrieve()`, and after a GitLab communication failure the saved and expected configs are identical, so the failure goes undetected. The always-updating timestamp makes the configs differ so the work is re-attempted.

That scenario drives retrieval at the source level. It does not go through `retrieveActions(SCMHead, ...)`, which is the overload branch-api calls once per branch head during indexing. So dropping `saveTimestampInOwner()` from the head-based overload appears not to affect the case #768 was written to fix, while removing the per-branch amplification. The source-level overload, `retrieveActions(SCMSourceEvent, ...)`, already runs once per scan and would continue to provide the signal.

If the head-based path is needed for event-driven single-head retrievals where the source-level overload does not run, an alternative would be to make the save conditional — skip it when `lastRetrieveTimestamp` has already been updated in the current pass.

Which is correct is a judgement call for the maintainers; flagging the behaviour is the point.

### Related

- #768 — introduced the behaviour
- #495 — the other `GitLabSCMSource` change in this version delta
- #736 / [JENKINS-75902](https://issues.jenkins.io/browse/JENKINS-75902) — the issue both PRs were addressing

### Are you interested in contributing a fix?

No.

Contributor guide

Open the contributing guide

Research direction

Start by tracing the two retrieveActions overloads in the GitLab branch-source indexing flow, focusing on the unconditional saveTimestampInOwner() call mentioned in the report. Reproduce a scan with Audit Trail enabled and verify that the fix restores one folder updateItem entry per scan rather than one per branch.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
ci-cd
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.