Skip to content

perf: parallelize user directory scanning and XML unmarshaling at sta…#27063

Open
rajat315315 wants to merge 3 commits into
jenkinsci:masterfrom
rajat315315:perf/parallel-user-scanning
Open

perf: parallelize user directory scanning and XML unmarshaling at sta…#27063
rajat315315 wants to merge 3 commits into
jenkinsci:masterfrom
rajat315315:perf/parallel-user-scanning

Conversation

@rajat315315

@rajat315315 rajat315315 commented Jul 9, 2026

Copy link
Copy Markdown

Description

This PR parallelizes user index initialization during startup inside hudson.model.User$AllUsers#scanAll.

Benefits

  1. Accelerates Startup: Concurrently unmarshals XML configs and populates the registry cache, leveraging multi-core processors.
  2. Thread-Safe Concurrent Cache: Utilizes the thread-safe ConcurrentHashMap (byName) for concurrent index registrations.
  3. Resilient Failure Handling: Errors in individual user profiles are handled in-thread, logging issues without interrupting the overall indexing task.

Micro-benchmark Results (3,000 users, 10 iterations)

  • Sequential Scan (Legacy): ~793.00 ms average per iteration.
  • Parallel Scan (Optimized): ~265.10 ms average per iteration.
  • Speedup: ~2.99x faster using Parallel Streams.

Fixes #27059

Testing done

I have added an automated test parallelScanAllWithMalformedConfig() that
that verifies the behavior of User.AllUsers.scanAll().

  • Generates 10 valid user profiles on disk and saves them.
  • Sets up two separate malformed config.xml files inside distinct directories:
    • One with incomplete tag nesting (syntax-invalid).
    • One mapping to a different class like Descriptor instead of User (semantics-invalid).
  • Clears the registry memory cache.
  • Invokes User.AllUsers.scanAll().
  • Asserts that all 10 valid user registries are successfully loaded.
  • Asserts that none of the malformed users abort the scan or are registered.
  • Ensures thorough cleanup of directories on disk in a finally block.

Screenshots (UI changes only)

Before

After

Proposed changelog entries

parallelize user directory scanning and XML unmarshaling at startup

Proposed changelog category

/label major-rfe

Proposed upgrade guidelines

N/A

Submitter checklist

  • The issue, if it exists, is well-described.
  • The changelog entries and upgrade guidelines are appropriate for the audience affected by the change (users or developers, depending on the change) and are in the imperative mood (see examples). Fill in the Proposed upgrade guidelines section only if there are breaking changes or changes that may require extra steps from users during upgrade.
  • There is automated testing or an explanation as to why this change has no tests.
  • New public classes, fields, and methods are annotated with @Restricted or have @since TODO Javadocs, as appropriate.
  • New deprecations are annotated with @Deprecated(since = "TODO") or @Deprecated(forRemoval = true, since = "TODO"), if applicable.
  • UI changes do not introduce regressions when enforcing the current default rules of Content Security Policy Plugin. In particular, new or substantially changed JavaScript is not defined inline and does not call eval to ease future introduction of Content Security Policy (CSP) directives (see documentation).
  • For dependency updates, there are links to external changelogs and, if possible, full differentials.
  • For new APIs and extension points, there is a link to at least one consumer.

Desired reviewers

@mention

Before the changes are marked as ready-for-merge:

Maintainer checklist

  • There are at least two (2) approvals for the pull request and no outstanding requests for change.
  • Conversations in the pull request are over, or it is explicit that a reviewer is not blocking the change.
  • Changelog entries in the pull request title and/or Proposed changelog entries are accurate, human-readable, and in the imperative mood.
  • Proper changelog labels are set so that the changelog can be generated automatically.
  • If the change needs additional upgrade steps from users, the upgrade-guide-needed label is set and there is a Proposed upgrade guidelines section in the pull request title (see example).
  • If it would make sense to backport the change to LTS, be a Bug or Improvement, and either the issue or pull request must be labeled as lts-candidate to be considered.

@comment-ops-bot comment-ops-bot Bot added the major-rfe For changelog: Major enhancement. Will be highlighted on the top label Jul 9, 2026
@MarkEWaite MarkEWaite added needs-ath-build Needs to run through the full acceptance-test-harness suite needs-pct-build A run through of bom is needed labels Jul 9, 2026
@MarkEWaite MarkEWaite requested a review from Copilot July 9, 2026 20:19
@MarkEWaite

Copy link
Copy Markdown
Contributor

Please complete the "testing done" section of the pull request template. The comment says:

Provide a clear description of how this change was tested. At minimum this should include proof that a computer has executed the changed lines. Ideally this should include an automated test or an explanation as to why this change has no tests. Note that automated test coverage is less than complete, so a successful PR build does not necessarily imply that a computer has executed the changed lines. If automated test coverage does not exist for the lines you are changing, you must describe the scenario(s) in which you manually tested the change. For frontend changes, include screenshots of the relevant page(s) before and after the change. For refactoring and code cleanup changes, exercise the code before and after the change and verify the behavior remains the same.

Please explain why you checked the box for "has automated tests" but only changed production code, without adding an automated test.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request speeds up Jenkins startup user indexing by parallelizing the on-disk user directory scan and config.xml unmarshaling performed in hudson.model.User.AllUsers#scanAll.

Changes:

  • Switch user directory iteration from a sequential for loop to a parallel stream.
  • Preserve existing per-user failure handling behavior (skip invalid directories / missing or unreadable config.xml / invalid IDs) while allowing other users to load concurrently.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread core/src/main/java/hudson/model/User.java Outdated
Comment thread core/src/main/java/hudson/model/User.java Outdated

@MarkEWaite MarkEWaite left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the pull request.

Please complete the "testing done" section of the pull request description. Include a description of the interactive testing you performed to assure that the change is ready for large scale use.

Please provide one or more automated tests that address the concerns raised in the GitHub Copilot review of the pull request.

@rajat315315 rajat315315 requested a review from MarkEWaite July 10, 2026 12:36
@MarkEWaite

Copy link
Copy Markdown
Contributor

I created an installation with over 40000 users with the following script in the Groovy script console:

import hudson.security.HudsonPrivateSecurityRealm
import jenkins.model.Jenkins

def jenkins = Jenkins.getInstance()
def securityRealm = jenkins.getSecurityRealm()

if (securityRealm instanceof HudsonPrivateSecurityRealm) {
    (1..42317).each { i ->
        def userId = String.format("user_%05d", i)
        def password = "InsecurePassword2026_${i}"
        def fullName = "Test User ${i}"

        // Check if the user already exists to prevent throwing errors
        def existingUser = securityRealm.getUser(userId)

        if (existingUser == null) {
            try {
                // Create the account in the HudsonPrivateSecurityRealm
                def newUser = securityRealm.createAccount(userId, password)

                // Set metadata properties
                newUser.setFullName(fullName)

                if (i % 50 == 0) {
                    println "Successfully created user: ${userId}"
                }
            } catch (Exception e) {
                println "Failed to create user ${userId}: ${e.message}"
            }
        } else {
            println "User ${userId} already exists. Skipping."
        }
    }

    // Save configuration to disk
    jenkins.save()

}

I saw no issues starting with that many users. The "Manage Jenkins" / "Users" page is slow to render because it does not use pagination to limit the number of rows shown to the user, but that is a pre-existing condition that is not changed by this pull request.

@MarkEWaite MarkEWaite added the needs-security-review Awaiting review by a security team member label Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

major-rfe For changelog: Major enhancement. Will be highlighted on the top needs-ath-build Needs to run through the full acceptance-test-harness suite needs-pct-build A run through of bom is needed needs-security-review Awaiting review by a security team member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parallel User scanning at Jenkins Startup

3 participants