This is an automated email from the ASF dual-hosted git repository.
cstamas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git
The following commit(s) were added to refs/heads/master by this push:
new 9e07b1b7e Maven Resolver lockrepro (#1665)
9e07b1b7e is described below
commit 9e07b1b7e2bac253c0f4b7a2675ca395a85b22a3
Author: Tamas Cservenak <[email protected]>
AuthorDate: Tue Nov 18 20:15:21 2025 +0100
Maven Resolver lockrepro (#1665)
Reproducer for Resolver 2.x. Does not run as part of test/build, is meant
to be ad-hoc be run from IDE.
---
.../ResolveTransitiveDependenciesParallel.java | 238 +++++++++++++++++++++
1 file changed, 238 insertions(+)
diff --git
a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ResolveTransitiveDependenciesParallel.java
b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ResolveTransitiveDependenciesParallel.java
new file mode 100644
index 000000000..cb2ebc81f
--- /dev/null
+++
b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ResolveTransitiveDependenciesParallel.java
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.resolver.examples;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.maven.resolver.examples.util.Booter;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.RepositorySystemSession.CloseableSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.collection.CollectRequest;
+import org.eclipse.aether.graph.Dependency;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactResult;
+import org.eclipse.aether.resolution.DependencyRequest;
+import org.eclipse.aether.util.artifact.JavaScopes;
+
+/**
+ * Resolves the transitive (compile) LARGE dependencies of an imaginary
artifact in parallel.
+ * This is the reproducer for locking issues: <a
href="https://github.com/apache/maven-resolver/issues/1644>GH-1644</a>
+ * This code does NOT run as part of build/tests, it is meant to be ad-hoc run
from IDE or alike. */
+public class ResolveTransitiveDependenciesParallel {
+
+ /**
+ * Main.
+ * @param args
+ * @throws Exception
+ */
+ public static void main(String[] args) throws Exception {
+
System.out.println("------------------------------------------------------------");
+
System.out.println(ResolveTransitiveDependenciesParallel.class.getSimpleName());
+
+ // note: these numbers below are not "universal", they stand for one
given WS with one given network,
+ // and they may need change with time if anything changes in relation
(even remote like Central!)
+ //
+ // cstamas (DK HW+net) 17. 11. 2025 (w/ empty local repo)
+ // One job is done in 20 sec
+ // BUT other must wait as they are serialized (time adds up)
+ // reproducer to succeed: 210 sec
+ // my run:
+ // DONE (21 sec): org.example:test:1: resolved 11; failed 0
+ // DONE (37 sec): org.example:test:6: resolved 11; failed 0
+ // DONE (60 sec): org.example:test:4: resolved 11; failed 0
+ // DONE (60 sec): org.example:test:5: resolved 11; failed 0
+ // DONE (170 sec): org.example:test:8: resolved 11; failed 0
+ // DONE (181 sec): org.example:test:3: resolved 11; failed 0
+ // DONE (181 sec): org.example:test:7: resolved 11; failed 0
+ // DONE (181 sec): org.example:test:2: resolved 11; failed 0
+ // =====
+ // TOTAL success=8; fail=0
+ //
+ // Pattern: as said above, one job does in 20 sec, BUT subsequent one
will do it in cca 40 sec (waiting 20 sec)
+ // and so on, the times are adding up. Each subsequent job with start
by waiting for previous jobs to finish
+ // due (intentional) artifact overlaps.
+
+ try (RepositorySystem system =
Booter.newRepositorySystem(Booter.selectFactory(args));
+ CloseableSession session =
Booter.newRepositorySystemSession(system, Booter.selectFs(args))
+ .setTransferListener(null)
+ .setRepositoryListener(null)
+ .setConfigProperty("aether.syncContext.named.time",
"210")
+ .build()) {
+ Artifact bigArtifact1 = new
DefaultArtifact("org.bytedeco:llvm:jar:linux-arm64:16.0.4-1.5.9");
+ Artifact bigArtifact2 = new
DefaultArtifact("org.bytedeco:llvm:jar:linux-armhf:16.0.4-1.5.9");
+ Artifact bigArtifact3 = new
DefaultArtifact("org.bytedeco:llvm:jar:linux-ppc64le:16.0.4-1.5.9");
+ Artifact bigArtifact4 = new
DefaultArtifact("org.bytedeco:llvm:jar:linux-x86:16.0.4-1.5.9");
+ Artifact bigArtifact5 = new
DefaultArtifact("org.bytedeco:llvm:jar:linux-x86_64:16.0.4-1.5.9");
+ Artifact bigArtifact6 = new
DefaultArtifact("org.bytedeco:llvm:jar:macosx-arm64:16.0.4-1.5.9");
+ Artifact bigArtifact7 = new
DefaultArtifact("org.bytedeco:llvm:jar:macosx-x86_64:16.0.4-1.5.9");
+ Artifact bigArtifact8 = new
DefaultArtifact("org.bytedeco:llvm:jar:windows-x86:16.0.4-1.5.9");
+ Artifact bigArtifact9 = new
DefaultArtifact("org.bytedeco:llvm:jar:windows-x86_64:16.0.4-1.5.9");
+
+ CountDownLatch latch = new CountDownLatch(8);
+ AtomicInteger success = new AtomicInteger(0);
+ AtomicInteger fail = new AtomicInteger(0);
+
+ Thread thread1 = new Thread(resolveWithDependencies(
+ latch,
+ success,
+ fail,
+ system,
+ session,
+ Booter.newRepositories(system, session),
+ "org.example:test:1",
+ bigArtifact1,
+ bigArtifact2));
+ Thread thread2 = new Thread(resolveWithDependencies(
+ latch,
+ success,
+ fail,
+ system,
+ session,
+ Booter.newRepositories(system, session),
+ "org.example:test:2",
+ bigArtifact2,
+ bigArtifact3));
+ Thread thread3 = new Thread(resolveWithDependencies(
+ latch,
+ success,
+ fail,
+ system,
+ session,
+ Booter.newRepositories(system, session),
+ "org.example:test:3",
+ bigArtifact3,
+ bigArtifact4));
+ Thread thread4 = new Thread(resolveWithDependencies(
+ latch,
+ success,
+ fail,
+ system,
+ session,
+ Booter.newRepositories(system, session),
+ "org.example:test:4",
+ bigArtifact4,
+ bigArtifact5));
+ Thread thread5 = new Thread(resolveWithDependencies(
+ latch,
+ success,
+ fail,
+ system,
+ session,
+ Booter.newRepositories(system, session),
+ "org.example:test:5",
+ bigArtifact5,
+ bigArtifact6));
+ Thread thread6 = new Thread(resolveWithDependencies(
+ latch,
+ success,
+ fail,
+ system,
+ session,
+ Booter.newRepositories(system, session),
+ "org.example:test:6",
+ bigArtifact6,
+ bigArtifact7));
+ Thread thread7 = new Thread(resolveWithDependencies(
+ latch,
+ success,
+ fail,
+ system,
+ session,
+ Booter.newRepositories(system, session),
+ "org.example:test:7",
+ bigArtifact7,
+ bigArtifact8));
+ Thread thread8 = new Thread(resolveWithDependencies(
+ latch,
+ success,
+ fail,
+ system,
+ session,
+ Booter.newRepositories(system, session),
+ "org.example:test:8",
+ bigArtifact8,
+ bigArtifact9));
+
+ thread1.start();
+ thread2.start();
+ thread3.start();
+ thread4.start();
+ thread5.start();
+ thread6.start();
+ thread7.start();
+ thread8.start();
+
+ latch.await();
+
+ System.out.println("=====");
+ System.out.println("TOTAL success=" + success.get() + "; fail=" +
fail.get());
+ }
+ }
+
+ @SuppressWarnings("checkstyle:parameternumber")
+ private static Runnable resolveWithDependencies(
+ CountDownLatch latch,
+ AtomicInteger success,
+ AtomicInteger fail,
+ RepositorySystem system,
+ RepositorySystemSession session,
+ List<RemoteRepository> repositories,
+ String gav,
+ Artifact... deps) {
+ return () -> {
+ try {
+ Instant now = Instant.now();
+ CollectRequest collectRequest = new CollectRequest();
+ collectRequest.setRootArtifact(new DefaultArtifact(gav));
+ for (Artifact dep : deps) {
+ collectRequest.addDependency(new Dependency(dep,
JavaScopes.COMPILE));
+ }
+ collectRequest.setRepositories(repositories);
+ DependencyRequest dependencyRequest = new
DependencyRequest(collectRequest, null);
+ List<ArtifactResult> artifactResults =
+ system.resolveDependencies(session,
dependencyRequest).getArtifactResults();
+ int resolved = 0;
+ int fails = 0;
+ for (ArtifactResult artifactResult : artifactResults) {
+ if (artifactResult.isResolved()) {
+ resolved++;
+ } else {
+ fails++;
+ }
+ }
+ String dur = Duration.between(now,
Instant.now()).get(ChronoUnit.SECONDS) + " sec";
+ System.out.println("DONE (" + dur + "): " + gav + ": resolved
" + resolved + "; failed " + fails);
+ success.getAndIncrement();
+ } catch (Exception e) {
+ System.out.println("FAILED " + gav + ": " + e.getMessage());
+ fail.getAndIncrement();
+ } finally {
+ latch.countDown();
+ }
+ };
+ }
+}