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.index.account; 16 : 17 : import com.google.common.collect.ImmutableSet; 18 : import com.google.common.flogger.FluentLogger; 19 : import com.google.gerrit.common.Nullable; 20 : import com.google.gerrit.entities.Account; 21 : import com.google.gerrit.exceptions.StorageException; 22 : import com.google.gerrit.extensions.events.AccountIndexedListener; 23 : import com.google.gerrit.index.Index; 24 : import com.google.gerrit.server.account.AccountCache; 25 : import com.google.gerrit.server.account.AccountState; 26 : import com.google.gerrit.server.index.StalenessCheckResult; 27 : import com.google.gerrit.server.logging.Metadata; 28 : import com.google.gerrit.server.logging.TraceContext; 29 : import com.google.gerrit.server.logging.TraceContext.TraceTimer; 30 : import com.google.gerrit.server.plugincontext.PluginSetContext; 31 : import com.google.inject.assistedinject.Assisted; 32 : import com.google.inject.assistedinject.AssistedInject; 33 : import java.io.IOException; 34 : import java.util.Collection; 35 : import java.util.Collections; 36 : import java.util.Optional; 37 : 38 : /** 39 : * Implementation for indexing a Gerrit account. The account will be loaded from {@link 40 : * AccountCache}. 41 : */ 42 : public class AccountIndexerImpl implements AccountIndexer { 43 151 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 44 : 45 : /** Factory for creating an instance. */ 46 : public interface Factory { 47 : AccountIndexerImpl create(AccountIndexCollection indexes); 48 : 49 : AccountIndexerImpl create(@Nullable AccountIndex index); 50 : } 51 : 52 : private final AccountCache byIdCache; 53 : private final PluginSetContext<AccountIndexedListener> indexedListener; 54 : private final StalenessChecker stalenessChecker; 55 : @Nullable private final AccountIndexCollection indexes; 56 : @Nullable private final AccountIndex index; 57 : 58 : @AssistedInject 59 : AccountIndexerImpl( 60 : AccountCache byIdCache, 61 : PluginSetContext<AccountIndexedListener> indexedListener, 62 : StalenessChecker stalenessChecker, 63 151 : @Assisted AccountIndexCollection indexes) { 64 151 : this.byIdCache = byIdCache; 65 151 : this.indexedListener = indexedListener; 66 151 : this.stalenessChecker = stalenessChecker; 67 151 : this.indexes = indexes; 68 151 : this.index = null; 69 151 : } 70 : 71 : @AssistedInject 72 : AccountIndexerImpl( 73 : AccountCache byIdCache, 74 : PluginSetContext<AccountIndexedListener> indexedListener, 75 : StalenessChecker stalenessChecker, 76 0 : @Assisted @Nullable AccountIndex index) { 77 0 : this.byIdCache = byIdCache; 78 0 : this.indexedListener = indexedListener; 79 0 : this.stalenessChecker = stalenessChecker; 80 0 : this.indexes = null; 81 0 : this.index = index; 82 0 : } 83 : 84 : @Override 85 : public void index(Account.Id id) { 86 151 : Optional<AccountState> accountState = byIdCache.get(id); 87 : 88 151 : if (accountState.isPresent()) { 89 151 : logger.atFine().log("Replace account %d in index", id.get()); 90 : } else { 91 39 : logger.atFine().log("Delete account %d from index", id.get()); 92 : } 93 : 94 151 : for (Index<Account.Id, AccountState> i : getWriteIndexes()) { 95 : // Evict the cache to get an up-to-date value for sure. 96 151 : if (accountState.isPresent()) { 97 151 : try (TraceTimer traceTimer = 98 151 : TraceContext.newTimer( 99 : "Replacing account in index", 100 151 : Metadata.builder() 101 151 : .accountId(id.get()) 102 151 : .indexVersion(i.getSchema().getVersion()) 103 151 : .build())) { 104 151 : i.replace(accountState.get()); 105 0 : } catch (RuntimeException e) { 106 0 : throw new StorageException( 107 0 : String.format( 108 : "Failed to replace account %d in index version %d", 109 0 : id.get(), i.getSchema().getVersion()), 110 : e); 111 151 : } 112 : } else { 113 39 : try (TraceTimer traceTimer = 114 39 : TraceContext.newTimer( 115 : "Deleting account in index", 116 39 : Metadata.builder() 117 39 : .accountId(id.get()) 118 39 : .indexVersion(i.getSchema().getVersion()) 119 39 : .build())) { 120 39 : i.delete(id); 121 0 : } catch (RuntimeException e) { 122 0 : throw new StorageException( 123 0 : String.format( 124 : "Failed to delete account %d from index version %d", 125 0 : id.get(), i.getSchema().getVersion()), 126 : e); 127 39 : } 128 : } 129 151 : } 130 151 : fireAccountIndexedEvent(id.get()); 131 151 : } 132 : 133 : @Override 134 : public boolean reindexIfStale(Account.Id id) { 135 : try { 136 1 : StalenessCheckResult stalenessCheckResult = stalenessChecker.check(id); 137 1 : if (stalenessCheckResult.isStale()) { 138 1 : logger.atInfo().log("Reindexing stale document %s", stalenessCheckResult); 139 1 : index(id); 140 1 : return true; 141 : } 142 0 : } catch (IOException e) { 143 0 : throw new StorageException(e); 144 1 : } 145 1 : return false; 146 : } 147 : 148 : private void fireAccountIndexedEvent(int id) { 149 151 : indexedListener.runEach(l -> l.onAccountIndexed(id)); 150 151 : } 151 : 152 : private Collection<AccountIndex> getWriteIndexes() { 153 151 : if (indexes != null) { 154 151 : return indexes.getWriteIndexes(); 155 : } 156 : 157 0 : return index != null ? Collections.singleton(index) : ImmutableSet.of(); 158 : } 159 : }