LCOV - code coverage report
Current view: top level - server/account - AccountCacheImpl.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 64 73 87.7 %
Date: 2022-11-19 15:00:39 Functions: 14 16 87.5 %

          Line data    Source code
       1             : // Copyright (C) 2009 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;
      16             : 
      17             : import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
      18             : 
      19             : import com.google.common.cache.CacheLoader;
      20             : import com.google.common.cache.LoadingCache;
      21             : import com.google.common.collect.ImmutableMap;
      22             : import com.google.common.collect.Sets;
      23             : import com.google.common.flogger.FluentLogger;
      24             : import com.google.gerrit.entities.Account;
      25             : import com.google.gerrit.entities.RefNames;
      26             : import com.google.gerrit.exceptions.StorageException;
      27             : import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
      28             : import com.google.gerrit.server.account.externalids.ExternalIds;
      29             : import com.google.gerrit.server.cache.CacheModule;
      30             : import com.google.gerrit.server.config.AllUsersName;
      31             : import com.google.gerrit.server.config.CachedPreferences;
      32             : import com.google.gerrit.server.config.DefaultPreferencesCache;
      33             : import com.google.gerrit.server.git.GitRepositoryManager;
      34             : import com.google.gerrit.server.logging.Metadata;
      35             : import com.google.gerrit.server.logging.TraceContext;
      36             : import com.google.gerrit.server.logging.TraceContext.TraceTimer;
      37             : import com.google.gerrit.server.util.time.TimeUtil;
      38             : import com.google.inject.Inject;
      39             : import com.google.inject.Module;
      40             : import com.google.inject.Singleton;
      41             : import com.google.inject.name.Named;
      42             : import java.io.IOException;
      43             : import java.util.Collections;
      44             : import java.util.Map;
      45             : import java.util.Optional;
      46             : import java.util.Set;
      47             : import java.util.concurrent.ExecutionException;
      48             : import org.eclipse.jgit.lib.ObjectId;
      49             : import org.eclipse.jgit.lib.Ref;
      50             : import org.eclipse.jgit.lib.Repository;
      51             : 
      52             : /** Caches important (but small) account state to avoid database hits. */
      53             : @Singleton
      54             : public class AccountCacheImpl implements AccountCache {
      55         152 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      56             : 
      57             :   private static final String BYID_AND_REV_NAME = "accounts";
      58             : 
      59             :   public static Module module() {
      60         152 :     return new CacheModule() {
      61             :       @Override
      62             :       protected void configure() {
      63         152 :         persist(BYID_AND_REV_NAME, CachedAccountDetails.Key.class, CachedAccountDetails.class)
      64         152 :             .version(1)
      65         152 :             .keySerializer(CachedAccountDetails.Key.Serializer.INSTANCE)
      66         152 :             .valueSerializer(CachedAccountDetails.Serializer.INSTANCE)
      67         152 :             .loader(Loader.class);
      68             : 
      69         152 :         bind(AccountCacheImpl.class);
      70         152 :         bind(AccountCache.class).to(AccountCacheImpl.class);
      71         152 :       }
      72             :     };
      73             :   }
      74             : 
      75             :   private final ExternalIds externalIds;
      76             :   private final LoadingCache<CachedAccountDetails.Key, CachedAccountDetails> accountDetailsCache;
      77             :   private final GitRepositoryManager repoManager;
      78             :   private final AllUsersName allUsersName;
      79             :   private final DefaultPreferencesCache defaultPreferenceCache;
      80             :   private final ExternalIdKeyFactory externalIdKeyFactory;
      81             : 
      82             :   @Inject
      83             :   AccountCacheImpl(
      84             :       ExternalIds externalIds,
      85             :       @Named(BYID_AND_REV_NAME)
      86             :           LoadingCache<CachedAccountDetails.Key, CachedAccountDetails> accountDetailsCache,
      87             :       GitRepositoryManager repoManager,
      88             :       AllUsersName allUsersName,
      89             :       DefaultPreferencesCache defaultPreferenceCache,
      90         152 :       ExternalIdKeyFactory externalIdKeyFactory) {
      91         152 :     this.externalIds = externalIds;
      92         152 :     this.accountDetailsCache = accountDetailsCache;
      93         152 :     this.repoManager = repoManager;
      94         152 :     this.allUsersName = allUsersName;
      95         152 :     this.defaultPreferenceCache = defaultPreferenceCache;
      96         152 :     this.externalIdKeyFactory = externalIdKeyFactory;
      97         152 :   }
      98             : 
      99             :   @Override
     100             :   public AccountState getEvenIfMissing(Account.Id accountId) {
     101         151 :     return get(accountId).orElse(missing(accountId));
     102             :   }
     103             : 
     104             :   @Override
     105             :   public Optional<AccountState> get(Account.Id accountId) {
     106         151 :     return Optional.ofNullable(get(Collections.singleton(accountId)).getOrDefault(accountId, null));
     107             :   }
     108             : 
     109             :   @Override
     110             :   public AccountState getFromMetaId(Account.Id id, ObjectId metaId) {
     111             :     try {
     112           1 :       CachedAccountDetails.Key key = CachedAccountDetails.Key.create(id, metaId);
     113             : 
     114           1 :       CachedAccountDetails accountDetails = accountDetailsCache.get(key);
     115           1 :       return AccountState.forCachedAccount(accountDetails, CachedPreferences.EMPTY, externalIds);
     116           0 :     } catch (IOException | ExecutionException e) {
     117           0 :       throw new StorageException(e);
     118             :     }
     119             :   }
     120             : 
     121             :   @Override
     122             :   public Map<Account.Id, AccountState> get(Set<Account.Id> accountIds) {
     123             :     try {
     124         151 :       try (Repository allUsers = repoManager.openRepository(allUsersName)) {
     125             :         // Get the default preferences for this Gerrit host
     126         151 :         Ref ref = allUsers.exactRef(RefNames.REFS_USERS_DEFAULT);
     127             :         CachedPreferences defaultPreferences =
     128         151 :             ref != null
     129           2 :                 ? defaultPreferenceCache.get(ref.getObjectId())
     130         151 :                 : DefaultPreferencesCache.EMPTY;
     131             : 
     132         151 :         Set<CachedAccountDetails.Key> keys =
     133         151 :             Sets.newLinkedHashSetWithExpectedSize(accountIds.size());
     134         151 :         for (Account.Id id : accountIds) {
     135         151 :           Ref userRef = allUsers.exactRef(RefNames.refsUsers(id));
     136         151 :           if (userRef == null) {
     137          43 :             continue;
     138             :           }
     139         151 :           keys.add(CachedAccountDetails.Key.create(id, userRef.getObjectId()));
     140         151 :         }
     141         151 :         ImmutableMap.Builder<Account.Id, AccountState> result = ImmutableMap.builder();
     142             :         for (Map.Entry<CachedAccountDetails.Key, CachedAccountDetails> account :
     143         151 :             accountDetailsCache.getAll(keys).entrySet()) {
     144         151 :           result.put(
     145         151 :               account.getKey().accountId(),
     146         151 :               AccountState.forCachedAccount(account.getValue(), defaultPreferences, externalIds));
     147         151 :         }
     148         151 :         return result.build();
     149             :       }
     150           0 :     } catch (IOException | ExecutionException e) {
     151           0 :       throw new StorageException(e);
     152             :     }
     153             :   }
     154             : 
     155             :   @Override
     156             :   public Optional<AccountState> getByUsername(String username) {
     157             :     try {
     158          72 :       return externalIds
     159          72 :           .get(externalIdKeyFactory.create(SCHEME_USERNAME, username))
     160          72 :           .map(e -> get(e.accountId()))
     161          72 :           .orElseGet(Optional::empty);
     162           0 :     } catch (IOException e) {
     163           0 :       logger.atWarning().withCause(e).log("Cannot load AccountState for username %s", username);
     164           0 :       return Optional.empty();
     165             :     }
     166             :   }
     167             : 
     168             :   private AccountState missing(Account.Id accountId) {
     169         151 :     Account.Builder account = Account.builder(accountId, TimeUtil.now());
     170         151 :     account.setActive(false);
     171         151 :     return AccountState.forAccount(account.build());
     172             :   }
     173             : 
     174             :   @Singleton
     175             :   static class Loader extends CacheLoader<CachedAccountDetails.Key, CachedAccountDetails> {
     176             :     private final GitRepositoryManager repoManager;
     177             :     private final AllUsersName allUsersName;
     178             : 
     179             :     @Inject
     180         152 :     Loader(GitRepositoryManager repoManager, AllUsersName allUsersName) {
     181         152 :       this.repoManager = repoManager;
     182         152 :       this.allUsersName = allUsersName;
     183         152 :     }
     184             : 
     185             :     @Override
     186             :     public CachedAccountDetails load(CachedAccountDetails.Key key) throws Exception {
     187         151 :       try (TraceTimer ignored =
     188         151 :               TraceContext.newTimer(
     189         151 :                   "Loading account", Metadata.builder().accountId(key.accountId().get()).build());
     190         151 :           Repository repo = repoManager.openRepository(allUsersName)) {
     191         151 :         AccountConfig cfg = new AccountConfig(key.accountId(), allUsersName, repo).load(key.id());
     192         151 :         Account account =
     193         151 :             cfg.getLoadedAccount()
     194         151 :                 .orElseThrow(() -> new AccountNotFoundException(key.accountId() + " not found"));
     195         151 :         return CachedAccountDetails.create(
     196         151 :             account, cfg.getProjectWatches(), cfg.asCachedPreferences());
     197             :       }
     198             :     }
     199             :   }
     200             : 
     201             :   /** Signals that the account was not found in the primary storage. */
     202             :   private static class AccountNotFoundException extends Exception {
     203             :     private static final long serialVersionUID = 1L;
     204             : 
     205             :     public AccountNotFoundException(String message) {
     206           0 :       super(message);
     207           0 :     }
     208             :   }
     209             : }

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