Skip to content

perf: batch DigestStore serialization during Maven/Gradle project import - #3839

Open
chagong wants to merge 4 commits into
eclipse-jdtls:mainfrom
chagong:perf/batch-digest-store
Open

perf: batch DigestStore serialization during Maven/Gradle project import#3839
chagong wants to merge 4 commits into
eclipse-jdtls:mainfrom
chagong:perf/batch-digest-store

Conversation

@chagong

@chagong chagong commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

During project import, DigestStore.updateDigest(Path) was called once per build file, and each call that detected a change serialized the entire digest map to disk via ObjectOutputStream. For a workspace with N modules this produced O(N) full-map serializations during a single import (and O(N²) cumulative bytes written as the map grows), all on the import thread.

This change adds a batched API and uses it in the import paths so the digest map is serialized at most once per import, instead of once per changed build file.

Changes

  • DigestStore: add updateDigests(Collection<Path>), which computes all requested digests, applies changes under the existing synchronized block, and serializes the map only once if anything changed. updateDigest(Path) now delegates to it, so existing per-file callers keep identical behavior (including the boolean "changed" return).
  • MavenProjectImporter: collect the POM paths of new/re-imported projects during classification and flush them with a single updateDigests(...) call after the loop, instead of calling updateDigest(...) per project.
  • GradleProjectImporter: collect the build.gradle(.kts) / settings.gradle(.kts) paths across all imported Gradle projects and seed them with a single updateDigests(...) call.

The build-support / file-change paths (MavenBuildSupport, GradleBuildSupport, StandardProjectsManager) intentionally keep using the per-file updateDigest(Path) boolean gate — their behavior is unchanged.

Testing

  • Added MavenProjectImporterTest.testDigestStoreBatchUpdate covering batch update, no-op when unchanged, persistence across a new DigestStore instance, and change detection after a file edit.
  • ./mvnw verify for org.eclipse.jdt.ls.tests (MavenProjectImporterTest: 35 tests, 0 failures).
  • Manual end-to-end: built a local server, packaged the VS Code extension, and imported real Maven and Gradle projects — both imported and built with no errors, and .file-digests contained the expected build-file entries.

DigestStore.updateDigest(Path) serialized the entire digest map to disk on every changed build file, producing O(N) full-map serializations during a single import of an N-module workspace.

Add DigestStore.updateDigests(Collection<Path>) that applies all changes under the existing lock and serializes at most once. updateDigest(Path) now delegates to it, preserving existing per-file behavior. MavenProjectImporter and GradleProjectImporter collect build-file paths during import and flush them with a single batched call.

The per-file boolean gate in MavenBuildSupport, GradleBuildSupport and StandardProjectsManager is unchanged.

Signed-off-by: Changyong Gong <chagon@microsoft.com>
@chagong
chagong force-pushed the perf/batch-digest-store branch from 23fd8aa to 9937572 Compare July 7, 2026 04:48
…related to this change)

The Integration Tests / Jenkins Build failures on the prior CI run were
caused by MavenProjectMetadataFileTest#testMetadataFileSync and
#testDeleteClasspath, which are known to fail intermittently
independent of this change (see eclipse-jdtls#1443 and
eclipse-jdtls#1251). Reproduced the same failures on eclipse-jdtls/eclipse.jdt.ls
main branch CI (unrelated PR), confirming this is not caused by the
DigestStore batching change in this PR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant