LCOV - code coverage report
Current view: top level - server/account/externalids - ExternalIdCacheImpl.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 26 31 83.9 %
Date: 2022-11-19 15:00:39 Functions: 7 9 77.8 %

          Line data    Source code
       1             : // Copyright (C) 2016 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.server.account.externalids;
      16             : 
      17             : import com.google.common.cache.Cache;
      18             : import com.google.common.collect.ImmutableSet;
      19             : import com.google.common.collect.ImmutableSetMultimap;
      20             : import com.google.gerrit.entities.Account;
      21             : import com.google.inject.Inject;
      22             : import com.google.inject.Singleton;
      23             : import com.google.inject.name.Named;
      24             : import java.io.IOException;
      25             : import java.util.Optional;
      26             : import java.util.concurrent.locks.Lock;
      27             : import java.util.concurrent.locks.ReentrantLock;
      28             : import org.eclipse.jgit.errors.ConfigInvalidException;
      29             : import org.eclipse.jgit.lib.ObjectId;
      30             : 
      31             : /** Caches external IDs of all accounts. The external IDs are always loaded from NoteDb. */
      32             : @Singleton
      33             : class ExternalIdCacheImpl implements ExternalIdCache {
      34             :   public static final String CACHE_NAME = "external_ids_map";
      35             : 
      36             :   private final Cache<ObjectId, AllExternalIds> extIdsByAccount;
      37             :   private final ExternalIdReader externalIdReader;
      38             :   private final ExternalIdCacheLoader externalIdCacheLoader;
      39             :   private final Lock lock;
      40             : 
      41             :   @Inject
      42             :   ExternalIdCacheImpl(
      43             :       @Named(CACHE_NAME) Cache<ObjectId, AllExternalIds> extIdsByAccount,
      44             :       ExternalIdReader externalIdReader,
      45         152 :       ExternalIdCacheLoader externalIdCacheLoader) {
      46         152 :     this.extIdsByAccount = extIdsByAccount;
      47         152 :     this.externalIdReader = externalIdReader;
      48         152 :     this.externalIdCacheLoader = externalIdCacheLoader;
      49         152 :     this.lock = new ReentrantLock(true /* fair */);
      50         152 :   }
      51             : 
      52             :   @Override
      53             :   public Optional<ExternalId> byKey(ExternalId.Key key) throws IOException {
      54          81 :     return Optional.ofNullable(get().byKey().get(key));
      55             :   }
      56             : 
      57             :   @Override
      58             :   public ImmutableSet<ExternalId> byAccount(Account.Id accountId) throws IOException {
      59         151 :     return get().byAccount().get(accountId);
      60             :   }
      61             : 
      62             :   @Override
      63             :   public ImmutableSet<ExternalId> byAccount(Account.Id accountId, ObjectId rev) throws IOException {
      64         151 :     return get(rev).byAccount().get(accountId);
      65             :   }
      66             : 
      67             :   @Override
      68             :   public ImmutableSetMultimap<Account.Id, ExternalId> allByAccount() throws IOException {
      69           0 :     return get().byAccount();
      70             :   }
      71             : 
      72             :   @Override
      73             :   public ImmutableSetMultimap<String, ExternalId> byEmails(String... emails) throws IOException {
      74         108 :     AllExternalIds allExternalIds = get();
      75         108 :     ImmutableSetMultimap.Builder<String, ExternalId> byEmails = ImmutableSetMultimap.builder();
      76         108 :     for (String email : emails) {
      77         108 :       byEmails.putAll(email, allExternalIds.byEmail().get(email));
      78             :     }
      79         108 :     return byEmails.build();
      80             :   }
      81             : 
      82             :   @Override
      83             :   public ImmutableSetMultimap<String, ExternalId> allByEmail() throws IOException {
      84           0 :     return get().byEmail();
      85             :   }
      86             : 
      87             :   private AllExternalIds get() throws IOException {
      88         151 :     return get(externalIdReader.readRevision());
      89             :   }
      90             : 
      91             :   /**
      92             :    * Returns the cached value or a freshly loaded value that will be cached with this call in case
      93             :    * the value was absent from the cache.
      94             :    *
      95             :    * <p>This method will load the value using {@link ExternalIdCacheLoader} in case it is not
      96             :    * already cached. {@link ExternalIdCacheLoader} requires loading older versions of the cached
      97             :    * value and Caffeine does not support recursive calls to the cache from loaders. Hence, we use a
      98             :    * Cache instead of a LoadingCache and perform the loading ourselves here similar to what a
      99             :    * loading cache would do.
     100             :    */
     101             :   private AllExternalIds get(ObjectId rev) throws IOException {
     102         151 :     AllExternalIds cachedValue = extIdsByAccount.getIfPresent(rev);
     103         151 :     if (cachedValue != null) {
     104         151 :       return cachedValue;
     105             :     }
     106             : 
     107             :     // Load the value and put it in the cache.
     108         151 :     lock.lock();
     109             :     try {
     110             :       // Check if value was already loaded while waiting for the lock.
     111         151 :       cachedValue = extIdsByAccount.getIfPresent(rev);
     112         151 :       if (cachedValue != null) {
     113           0 :         return cachedValue;
     114             :       }
     115             : 
     116             :       AllExternalIds newlyLoadedValue;
     117             :       try {
     118         151 :         newlyLoadedValue = externalIdCacheLoader.load(rev);
     119           0 :       } catch (ConfigInvalidException e) {
     120           0 :         throw new IOException("Cannot load external ids", e);
     121         151 :       }
     122         151 :       extIdsByAccount.put(rev, newlyLoadedValue);
     123         151 :       return newlyLoadedValue;
     124             :     } finally {
     125         151 :       lock.unlock();
     126             :     }
     127             :   }
     128             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750