LuciferYang opened a new pull request, #12423:
URL: https://github.com/apache/gravitino/pull/12423

   ### What changes were proposed in this pull request?
   
   `CredentialFactory.create` called `ServiceLoader.load(Credential.class)` on 
every invocation and iterated/instantiated every registered `Credential` 
implementation just to resolve one type -> class mapping. This caches the scan 
once into an immutable `type -> class` map (initialization-on-demand holder 
idiom) and turns `lookupCredential` into a single map get.
   
   Lookup stays case-insensitive and still throws `No credential found for: 
<type>` (preserving the caller's original casing) on a miss; duplicate-type 
detection moves to cache-build time.
   
   ### Why are the changes needed?
   
   `Credential` is a fixed, built-in SPI (custom credentials are added via 
`CredentialProvider`, not by registering new `Credential` services), so the 
`ServiceLoader` result is stable for the JVM lifetime and does not need to be 
rescanned — nor all providers re-instantiated — on every lookup.
   
   A JMH micro-benchmark shows the type lookup dropping from **~380 µs/op to 
~64 ns/op**:
   
   ```
   Benchmark                                  Mode  Cnt       Score       Error 
 Units
   CredentialLookupBenchmark.newCachedLookup  avgt    3      64.064 ±     8.449 
 ns/op
   CredentialLookupBenchmark.oldPerCallScan   avgt    3  380491.264 ± 35292.735 
 ns/op
   ```
   
   <details>
   <summary>Benchmark used (not part of this PR — drop under 
<code>core/src/jmh/java/org/apache/gravitino/credential/</code> and run 
<code>./gradlew :core:jmh</code>)</summary>
   
   `oldPerCallScan` replays the previous `ServiceLoader.load` + stream-filter 
per call; `newCachedLookup` does a single `get` on the pre-built map. Only the 
type-lookup step is measured; the shared `newInstance` of the resolved 
credential is excluded, since it is unchanged by this PR.
   
   ```java
   package org.apache.gravitino.credential;
   
   import java.util.HashMap;
   import java.util.List;
   import java.util.Locale;
   import java.util.Map;
   import java.util.ServiceLoader;
   import java.util.concurrent.TimeUnit;
   import java.util.stream.Collectors;
   import java.util.stream.StreamSupport;
   import org.openjdk.jmh.annotations.Benchmark;
   import org.openjdk.jmh.annotations.BenchmarkMode;
   import org.openjdk.jmh.annotations.Mode;
   import org.openjdk.jmh.annotations.OutputTimeUnit;
   import org.openjdk.jmh.annotations.Scope;
   import org.openjdk.jmh.annotations.Setup;
   import org.openjdk.jmh.annotations.State;
   import org.openjdk.jmh.infra.Blackhole;
   
   @State(Scope.Benchmark)
   @BenchmarkMode(Mode.AverageTime)
   @OutputTimeUnit(TimeUnit.NANOSECONDS)
   public class CredentialLookupBenchmark {
   
     private Map<String, Class<? extends Credential>> cachedClasses;
     private String[] types;
   
     @Setup
     public void setup() {
       cachedClasses = new HashMap<>();
       for (Credential credential : ServiceLoader.load(Credential.class)) {
         cachedClasses.put(
             credential.credentialType().toLowerCase(Locale.ROOT), 
credential.getClass());
       }
       types = cachedClasses.keySet().toArray(new String[0]);
     }
   
     @Benchmark
     public void oldPerCallScan(Blackhole bh) {
       for (String type : types) {
         bh.consume(oldLookup(type));
       }
     }
   
     @Benchmark
     public void newCachedLookup(Blackhole bh) {
       for (String type : types) {
         bh.consume(cachedClasses.get(type.toLowerCase(Locale.ROOT)));
       }
     }
   
     private static Class<? extends Credential> oldLookup(String 
credentialType) {
       ServiceLoader<Credential> serviceLoader = 
ServiceLoader.load(Credential.class);
       List<Class<? extends Credential>> credentials =
           StreamSupport.stream(serviceLoader.spliterator(), false)
               .filter(credential -> 
credentialType.equalsIgnoreCase(credential.credentialType()))
               .map(Credential::getClass)
               .collect(Collectors.toList());
       return credentials.get(0);
     }
   }
   ```
   </details>
   
   ### Does this PR introduce _any_ user-facing change?
   
   No. Internal factory optimization; lookup semantics (case-insensitive 
resolution, `No credential found for: <type>` on miss) are unchanged.
   
   ### How was this patch tested?
   
   Existing `TestCredentialFactory` cases plus new ones for unknown-type 
rejection (including that the error message preserves the original casing) and 
case-insensitive lookup. `./gradlew :common:test --tests 
"org.apache.gravitino.credential.TestCredentialFactory"` and spotless pass.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to