001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.maven.resolver.examples; 020 021import java.time.Duration; 022import java.time.Instant; 023import java.time.temporal.ChronoUnit; 024import java.util.List; 025import java.util.concurrent.CountDownLatch; 026import java.util.concurrent.atomic.AtomicInteger; 027 028import org.apache.maven.resolver.examples.util.Booter; 029import org.eclipse.aether.DefaultRepositorySystemSession; 030import org.eclipse.aether.RepositorySystem; 031import org.eclipse.aether.RepositorySystemSession; 032import org.eclipse.aether.artifact.Artifact; 033import org.eclipse.aether.artifact.DefaultArtifact; 034import org.eclipse.aether.collection.CollectRequest; 035import org.eclipse.aether.graph.Dependency; 036import org.eclipse.aether.repository.RemoteRepository; 037import org.eclipse.aether.resolution.ArtifactResult; 038import org.eclipse.aether.resolution.DependencyRequest; 039import org.eclipse.aether.util.artifact.JavaScopes; 040 041/** 042 * Resolves the transitive (compile) LARGE dependencies of an imaginary artifact in parallel. 043 * This is the reproducer for locking issues: <a href="https://github.com/apache/maven-resolver/issues/1644">GH-1644</a> 044 * This code does NOT run as part of build/tests, it is meant to be ad-hoc run from IDE or alike. 045 */ 046public class ResolveTransitiveDependenciesParallel { 047 048 /** 049 * Main. 050 * @param args 051 * @throws Exception 052 */ 053 public static void main(String[] args) throws Exception { 054 System.out.println("------------------------------------------------------------"); 055 System.out.println(ResolveTransitiveDependencies.class.getSimpleName()); 056 057 RepositorySystem system = Booter.newRepositorySystem(Booter.selectFactory(args)); 058 059 // note: these numbers below are not "universal", they stand for one given WS with one given network, 060 // and they may need change with time if anything changes in relation (even remote like Central!) 061 // 062 // cstamas (DK HW+net) 17. 11. 2025 (w/ empty local repo) 063 // One job is done in 20 sec 064 // BUT other must wait as they are serialized (time adds up) 065 // reproducer to succeed: 210 sec 066 // my run: 067 // DONE (21 sec): org.example:test:1: resolved 11; failed 0 068 // DONE (37 sec): org.example:test:6: resolved 11; failed 0 069 // DONE (60 sec): org.example:test:4: resolved 11; failed 0 070 // DONE (60 sec): org.example:test:5: resolved 11; failed 0 071 // DONE (170 sec): org.example:test:8: resolved 11; failed 0 072 // DONE (181 sec): org.example:test:3: resolved 11; failed 0 073 // DONE (181 sec): org.example:test:7: resolved 11; failed 0 074 // DONE (181 sec): org.example:test:2: resolved 11; failed 0 075 // ===== 076 // TOTAL success=8; fail=0 077 // 078 // Pattern: as said above, one job does in 20 sec, BUT subsequent one will do it in cca 40 sec (waiting 20 sec) 079 // and so on, the times are adding up. Each subsequent job with start by waiting for previous jobs to finish 080 // due (intentional) artifact overlaps. 081 082 DefaultRepositorySystemSession session = Booter.newRepositorySystemSession(system); 083 session.setConfigProperty("aether.syncContext.named.time", "210"); 084 session.setTransferListener(null); 085 session.setRepositoryListener(null); 086 087 Artifact bigArtifact1 = new DefaultArtifact("org.bytedeco:llvm:jar:linux-arm64:16.0.4-1.5.9"); 088 Artifact bigArtifact2 = new DefaultArtifact("org.bytedeco:llvm:jar:linux-armhf:16.0.4-1.5.9"); 089 Artifact bigArtifact3 = new DefaultArtifact("org.bytedeco:llvm:jar:linux-ppc64le:16.0.4-1.5.9"); 090 Artifact bigArtifact4 = new DefaultArtifact("org.bytedeco:llvm:jar:linux-x86:16.0.4-1.5.9"); 091 Artifact bigArtifact5 = new DefaultArtifact("org.bytedeco:llvm:jar:linux-x86_64:16.0.4-1.5.9"); 092 Artifact bigArtifact6 = new DefaultArtifact("org.bytedeco:llvm:jar:macosx-arm64:16.0.4-1.5.9"); 093 Artifact bigArtifact7 = new DefaultArtifact("org.bytedeco:llvm:jar:macosx-x86_64:16.0.4-1.5.9"); 094 Artifact bigArtifact8 = new DefaultArtifact("org.bytedeco:llvm:jar:windows-x86:16.0.4-1.5.9"); 095 Artifact bigArtifact9 = new DefaultArtifact("org.bytedeco:llvm:jar:windows-x86_64:16.0.4-1.5.9"); 096 097 CountDownLatch latch = new CountDownLatch(8); 098 AtomicInteger success = new AtomicInteger(0); 099 AtomicInteger fail = new AtomicInteger(0); 100 101 Thread thread1 = new Thread(resolveWithDependencies( 102 latch, 103 success, 104 fail, 105 system, 106 session, 107 Booter.newRepositories(system, session), 108 "org.example:test:1", 109 bigArtifact1, 110 bigArtifact2)); 111 Thread thread2 = new Thread(resolveWithDependencies( 112 latch, 113 success, 114 fail, 115 system, 116 session, 117 Booter.newRepositories(system, session), 118 "org.example:test:2", 119 bigArtifact2, 120 bigArtifact3)); 121 Thread thread3 = new Thread(resolveWithDependencies( 122 latch, 123 success, 124 fail, 125 system, 126 session, 127 Booter.newRepositories(system, session), 128 "org.example:test:3", 129 bigArtifact3, 130 bigArtifact4)); 131 Thread thread4 = new Thread(resolveWithDependencies( 132 latch, 133 success, 134 fail, 135 system, 136 session, 137 Booter.newRepositories(system, session), 138 "org.example:test:4", 139 bigArtifact4, 140 bigArtifact5)); 141 Thread thread5 = new Thread(resolveWithDependencies( 142 latch, 143 success, 144 fail, 145 system, 146 session, 147 Booter.newRepositories(system, session), 148 "org.example:test:5", 149 bigArtifact5, 150 bigArtifact6)); 151 Thread thread6 = new Thread(resolveWithDependencies( 152 latch, 153 success, 154 fail, 155 system, 156 session, 157 Booter.newRepositories(system, session), 158 "org.example:test:6", 159 bigArtifact6, 160 bigArtifact7)); 161 Thread thread7 = new Thread(resolveWithDependencies( 162 latch, 163 success, 164 fail, 165 system, 166 session, 167 Booter.newRepositories(system, session), 168 "org.example:test:7", 169 bigArtifact7, 170 bigArtifact8)); 171 Thread thread8 = new Thread(resolveWithDependencies( 172 latch, 173 success, 174 fail, 175 system, 176 session, 177 Booter.newRepositories(system, session), 178 "org.example:test:8", 179 bigArtifact8, 180 bigArtifact9)); 181 182 thread1.start(); 183 thread2.start(); 184 thread3.start(); 185 thread4.start(); 186 thread5.start(); 187 thread6.start(); 188 thread7.start(); 189 thread8.start(); 190 191 latch.await(); 192 193 System.out.println("====="); 194 System.out.println("TOTAL success=" + success.get() + "; fail=" + fail.get()); 195 } 196 197 @SuppressWarnings("checkstyle:parameternumber") 198 private static Runnable resolveWithDependencies( 199 CountDownLatch latch, 200 AtomicInteger success, 201 AtomicInteger fail, 202 RepositorySystem system, 203 RepositorySystemSession session, 204 List<RemoteRepository> repositories, 205 String gav, 206 Artifact... deps) { 207 return () -> { 208 try { 209 Instant now = Instant.now(); 210 CollectRequest collectRequest = new CollectRequest(); 211 collectRequest.setRootArtifact(new DefaultArtifact(gav)); 212 for (Artifact dep : deps) { 213 collectRequest.addDependency(new Dependency(dep, JavaScopes.COMPILE)); 214 } 215 collectRequest.setRepositories(repositories); 216 DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, null); 217 List<ArtifactResult> artifactResults = 218 system.resolveDependencies(session, dependencyRequest).getArtifactResults(); 219 int resolved = 0; 220 int fails = 0; 221 for (ArtifactResult artifactResult : artifactResults) { 222 if (artifactResult.isResolved()) { 223 resolved++; 224 } else { 225 fails++; 226 } 227 } 228 String dur = Duration.between(now, Instant.now()).get(ChronoUnit.SECONDS) + " sec"; 229 System.out.println("DONE (" + dur + "): " + gav + ": resolved " + resolved + "; failed " + fails); 230 success.getAndIncrement(); 231 } catch (Exception e) { 232 System.out.println("FAILED " + gav + ": " + e.getMessage()); 233 fail.getAndIncrement(); 234 } finally { 235 latch.countDown(); 236 } 237 }; 238 } 239}