Line data Source code
1 : // Copyright (C) 2017 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.group; 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.AccountGroup; 21 : import com.google.gerrit.entities.InternalGroup; 22 : import com.google.gerrit.exceptions.StorageException; 23 : import com.google.gerrit.extensions.events.GroupIndexedListener; 24 : import com.google.gerrit.index.Index; 25 : import com.google.gerrit.server.account.GroupCache; 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 an internal Gerrit group. The group will be loaded from {@link 40 : * GroupCache}. 41 : */ 42 : public class GroupIndexerImpl implements GroupIndexer { 43 151 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 44 : 45 : public interface Factory { 46 : GroupIndexerImpl create(GroupIndexCollection indexes); 47 : 48 : GroupIndexerImpl create(@Nullable GroupIndex index); 49 : } 50 : 51 : private final GroupCache groupCache; 52 : private final PluginSetContext<GroupIndexedListener> indexedListener; 53 : private final StalenessChecker stalenessChecker; 54 : @Nullable private final GroupIndexCollection indexes; 55 : @Nullable private final GroupIndex index; 56 : 57 : @AssistedInject 58 : GroupIndexerImpl( 59 : GroupCache groupCache, 60 : PluginSetContext<GroupIndexedListener> indexedListener, 61 : StalenessChecker stalenessChecker, 62 151 : @Assisted GroupIndexCollection indexes) { 63 151 : this.groupCache = groupCache; 64 151 : this.indexedListener = indexedListener; 65 151 : this.stalenessChecker = stalenessChecker; 66 151 : this.indexes = indexes; 67 151 : this.index = null; 68 151 : } 69 : 70 : @AssistedInject 71 : GroupIndexerImpl( 72 : GroupCache groupCache, 73 : PluginSetContext<GroupIndexedListener> indexedListener, 74 : StalenessChecker stalenessChecker, 75 0 : @Assisted @Nullable GroupIndex index) { 76 0 : this.groupCache = groupCache; 77 0 : this.indexedListener = indexedListener; 78 0 : this.stalenessChecker = stalenessChecker; 79 0 : this.indexes = null; 80 0 : this.index = index; 81 0 : } 82 : 83 : @Override 84 : public void index(AccountGroup.UUID uuid) { 85 : // Evict the cache to get an up-to-date value for sure. 86 151 : groupCache.evict(uuid); 87 151 : Optional<InternalGroup> internalGroup = groupCache.get(uuid); 88 : 89 151 : if (internalGroup.isPresent()) { 90 151 : logger.atFine().log("Replace group %s in index", uuid.get()); 91 : } else { 92 22 : logger.atFine().log("Delete group %s from index", uuid.get()); 93 : } 94 : 95 151 : for (Index<AccountGroup.UUID, InternalGroup> i : getWriteIndexes()) { 96 151 : if (internalGroup.isPresent()) { 97 151 : try (TraceTimer traceTimer = 98 151 : TraceContext.newTimer( 99 : "Replacing group", 100 151 : Metadata.builder() 101 151 : .groupUuid(uuid.get()) 102 151 : .indexVersion(i.getSchema().getVersion()) 103 151 : .build())) { 104 151 : i.replace(internalGroup.get()); 105 0 : } catch (RuntimeException e) { 106 0 : throw new StorageException( 107 0 : String.format( 108 : "Failed to replace group %s in index version %d", 109 0 : uuid.get(), i.getSchema().getVersion()), 110 : e); 111 151 : } 112 : } else { 113 22 : try (TraceTimer traceTimer = 114 22 : TraceContext.newTimer( 115 : "Deleting group", 116 22 : Metadata.builder() 117 22 : .groupUuid(uuid.get()) 118 22 : .indexVersion(i.getSchema().getVersion()) 119 22 : .build())) { 120 22 : i.delete(uuid); 121 0 : } catch (RuntimeException e) { 122 0 : throw new StorageException( 123 0 : String.format( 124 : "Failed to delete group %s from index version %d", 125 0 : uuid.get(), i.getSchema().getVersion()), 126 : e); 127 22 : } 128 : } 129 151 : } 130 151 : fireGroupIndexedEvent(uuid.get()); 131 151 : } 132 : 133 : @Override 134 : public boolean reindexIfStale(AccountGroup.UUID uuid) { 135 : try { 136 4 : StalenessCheckResult stalenessCheckResult = stalenessChecker.check(uuid); 137 4 : if (stalenessCheckResult.isStale()) { 138 4 : logger.atInfo().log("Reindexing stale document %s", stalenessCheckResult); 139 4 : index(uuid); 140 4 : 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 fireGroupIndexedEvent(String uuid) { 149 151 : indexedListener.runEach(l -> l.onGroupIndexed(uuid)); 150 151 : } 151 : 152 : private Collection<GroupIndex> 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 : }