All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gerrit.server.git.SearchingChangeCacheImpl Maven / Gradle / Ivy

There is a newer version: 3.11.0-rc3
Show newest version
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.gerrit.server.git;

import com.google.auto.value.AutoValue;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.UsedAt;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
import com.google.inject.util.Providers;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.stream.Stream;

/**
 * Cache based on an index query of the most recent changes. The number of cached items depends on
 * the index implementation and configuration.
 *
 * 

This cache is intended to be used when filtering references. By design it returns only a * fraction of all changes. These are the changes that were modified last. */ @Singleton public class SearchingChangeCacheImpl implements GitReferenceUpdatedListener { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); static final String ID_CACHE = "changes"; public static class SearchingChangeCacheImplModule extends CacheModule { private final boolean slave; public SearchingChangeCacheImplModule() { this(false); } public SearchingChangeCacheImplModule(boolean slave) { this.slave = slave; } @Override protected void configure() { if (slave) { bind(SearchingChangeCacheImpl.class).toProvider(Providers.of(null)); } else { cache(ID_CACHE, Project.NameKey.class, new TypeLiteral>() {}) .maximumWeight(0) .loader(Loader.class); bind(SearchingChangeCacheImpl.class); DynamicSet.bind(binder(), GitReferenceUpdatedListener.class) .to(SearchingChangeCacheImpl.class); } } } @AutoValue @UsedAt(UsedAt.Project.GOOGLE) public abstract static class CachedChange { // Subset of fields in ChangeData, specifically fields needed to serve // VisibleRefFilter without touching the database. More can be added as // necessary. abstract Change change(); @Nullable abstract ReviewerSet reviewers(); } private final LoadingCache> cache; private final ChangeData.Factory changeDataFactory; @Inject SearchingChangeCacheImpl( @Named(ID_CACHE) LoadingCache> cache, ChangeData.Factory changeDataFactory) { this.cache = cache; this.changeDataFactory = changeDataFactory; } /** * Read changes for the project from the secondary index. * *

Returned changes only include the {@code Change} object (with id, branch) and the reviewers. * Additional stored fields are not loaded from the index. * * @param project project to read. * @return stream of known changes; empty if no changes. */ public Stream getChangeData(Project.NameKey project) { List cached; try { cached = cache.get(project); } catch (ExecutionException e) { logger.atWarning().withCause(e).log("Cannot fetch changes for %s", project); return Stream.empty(); } return cached.stream() .map( cc -> { ChangeData cd = changeDataFactory.create(cc.change()); cd.setReviewers(cc.reviewers()); return cd; }); } @Override public void onGitReferenceUpdated(GitReferenceUpdatedListener.Event event) { if (event.getRefName().startsWith(RefNames.REFS_CHANGES)) { cache.invalidate(Project.nameKey(event.getProjectName())); } } static class Loader extends CacheLoader> { private final OneOffRequestContext requestContext; private final Provider queryProvider; @Inject Loader(OneOffRequestContext requestContext, Provider queryProvider) { this.requestContext = requestContext; this.queryProvider = queryProvider; } @Override public List load(Project.NameKey key) throws Exception { try (TraceTimer timer = TraceContext.newTimer( "Loading changes of project", Metadata.builder().projectName(key.get()).build()); ManualRequestContext ctx = requestContext.open()) { List cds = queryProvider .get() .setRequestedFields(ChangeField.CHANGE_SPEC, ChangeField.REVIEWER_SPEC) .byProject(key); Map result = new HashMap<>(cds.size()); for (ChangeData cd : cds) { if (result.containsKey(cd.getId())) { logger.atWarning().log( "Duplicate changes returned from change query by project %s: %s, %s", key, cd.change(), result.get(cd.getId()).change()); } result.put( cd.getId(), new AutoValue_SearchingChangeCacheImpl_CachedChange(cd.change(), cd.reviewers())); } return List.copyOf(result.values()); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy