LCOV - code coverage report
Current view: top level - server/change - MergeabilityCacheImpl.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 65 76 85.5 %
Date: 2022-11-19 15:00:39 Functions: 21 22 95.5 %

          Line data    Source code
       1             : // Copyright (C) 2014 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.change;
      16             : 
      17             : import static com.google.common.base.Preconditions.checkArgument;
      18             : import static java.util.Objects.requireNonNull;
      19             : 
      20             : import com.google.common.base.Converter;
      21             : import com.google.common.base.Enums;
      22             : import com.google.common.base.MoreObjects;
      23             : import com.google.common.cache.Cache;
      24             : import com.google.common.cache.Weigher;
      25             : import com.google.common.flogger.FluentLogger;
      26             : import com.google.common.util.concurrent.UncheckedExecutionException;
      27             : import com.google.gerrit.entities.BranchNameKey;
      28             : import com.google.gerrit.extensions.client.SubmitType;
      29             : import com.google.gerrit.proto.Protos;
      30             : import com.google.gerrit.server.cache.CacheModule;
      31             : import com.google.gerrit.server.cache.proto.Cache.MergeabilityKeyProto;
      32             : import com.google.gerrit.server.cache.serialize.BooleanCacheSerializer;
      33             : import com.google.gerrit.server.cache.serialize.CacheSerializer;
      34             : import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
      35             : import com.google.gerrit.server.git.CodeReviewCommit;
      36             : import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
      37             : import com.google.gerrit.server.submit.SubmitDryRun;
      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.util.Arrays;
      43             : import java.util.Objects;
      44             : import java.util.Set;
      45             : import java.util.concurrent.ExecutionException;
      46             : import org.eclipse.jgit.lib.ObjectId;
      47             : import org.eclipse.jgit.lib.Ref;
      48             : import org.eclipse.jgit.lib.Repository;
      49             : import org.eclipse.jgit.revwalk.RevCommit;
      50             : 
      51             : @Singleton
      52             : public class MergeabilityCacheImpl implements MergeabilityCache {
      53         152 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      54             : 
      55             :   private static final String CACHE_NAME = "mergeability";
      56             : 
      57             :   public static Module module() {
      58         152 :     return new CacheModule() {
      59             :       @Override
      60             :       protected void configure() {
      61         152 :         persist(CACHE_NAME, EntryKey.class, Boolean.class)
      62         152 :             .maximumWeight(1 << 20)
      63         152 :             .weigher(MergeabilityWeigher.class)
      64         152 :             .version(1)
      65         152 :             .keySerializer(EntryKey.Serializer.INSTANCE)
      66         152 :             .valueSerializer(BooleanCacheSerializer.INSTANCE);
      67         152 :         bind(MergeabilityCache.class).to(MergeabilityCacheImpl.class);
      68         152 :       }
      69             :     };
      70             :   }
      71             : 
      72             :   public static ObjectId toId(Ref ref) {
      73           2 :     return ref != null && ref.getObjectId() != null ? ref.getObjectId() : ObjectId.zeroId();
      74             :   }
      75             : 
      76             :   public static class EntryKey {
      77             :     private ObjectId commit;
      78             :     private ObjectId into;
      79             :     private SubmitType submitType;
      80             :     private String mergeStrategy;
      81             : 
      82          22 :     public EntryKey(ObjectId commit, ObjectId into, SubmitType submitType, String mergeStrategy) {
      83          22 :       checkArgument(
      84             :           submitType != SubmitType.INHERIT,
      85             :           "Cannot cache %s.%s",
      86          22 :           SubmitType.class.getSimpleName(),
      87             :           submitType);
      88          22 :       this.commit = requireNonNull(commit, "commit");
      89          22 :       this.into = requireNonNull(into, "into");
      90          22 :       this.submitType = requireNonNull(submitType, "submitType");
      91          22 :       this.mergeStrategy = requireNonNull(mergeStrategy, "mergeStrategy");
      92          22 :     }
      93             : 
      94             :     public ObjectId getCommit() {
      95           1 :       return commit;
      96             :     }
      97             : 
      98             :     public ObjectId getInto() {
      99           1 :       return into;
     100             :     }
     101             : 
     102             :     public SubmitType getSubmitType() {
     103           1 :       return submitType;
     104             :     }
     105             : 
     106             :     public String getMergeStrategy() {
     107           1 :       return mergeStrategy;
     108             :     }
     109             : 
     110             :     @Override
     111             :     public boolean equals(Object o) {
     112          21 :       if (o instanceof EntryKey) {
     113          21 :         EntryKey k = (EntryKey) o;
     114          21 :         return commit.equals(k.commit)
     115          21 :             && into.equals(k.into)
     116             :             && submitType == k.submitType
     117          21 :             && mergeStrategy.equals(k.mergeStrategy);
     118             :       }
     119           0 :       return false;
     120             :     }
     121             : 
     122             :     @Override
     123             :     public int hashCode() {
     124          21 :       return Objects.hash(commit, into, submitType, mergeStrategy);
     125             :     }
     126             : 
     127             :     @Override
     128             :     public String toString() {
     129           0 :       return MoreObjects.toStringHelper(this)
     130           0 :           .add("commit", commit.name())
     131           0 :           .add("into", into.name())
     132           0 :           .addValue(submitType)
     133           0 :           .addValue(mergeStrategy)
     134           0 :           .toString();
     135             :     }
     136             : 
     137         152 :     enum Serializer implements CacheSerializer<EntryKey> {
     138         152 :       INSTANCE;
     139             : 
     140         152 :       private static final Converter<String, SubmitType> SUBMIT_TYPE_CONVERTER =
     141         152 :           Enums.stringConverter(SubmitType.class);
     142             : 
     143             :       @Override
     144             :       public byte[] serialize(EntryKey object) {
     145           1 :         ObjectIdConverter idConverter = ObjectIdConverter.create();
     146           1 :         return Protos.toByteArray(
     147           1 :             MergeabilityKeyProto.newBuilder()
     148           1 :                 .setCommit(idConverter.toByteString(object.getCommit()))
     149           1 :                 .setInto(idConverter.toByteString(object.getInto()))
     150           1 :                 .setSubmitType(SUBMIT_TYPE_CONVERTER.reverse().convert(object.getSubmitType()))
     151           1 :                 .setMergeStrategy(object.getMergeStrategy())
     152           1 :                 .build());
     153             :       }
     154             : 
     155             :       @Override
     156             :       public EntryKey deserialize(byte[] in) {
     157           1 :         MergeabilityKeyProto proto = Protos.parseUnchecked(MergeabilityKeyProto.parser(), in);
     158           1 :         ObjectIdConverter idConverter = ObjectIdConverter.create();
     159           1 :         return new EntryKey(
     160           1 :             idConverter.fromByteString(proto.getCommit()),
     161           1 :             idConverter.fromByteString(proto.getInto()),
     162           1 :             SUBMIT_TYPE_CONVERTER.convert(proto.getSubmitType()),
     163           1 :             proto.getMergeStrategy());
     164             :       }
     165             :     }
     166             :   }
     167             : 
     168         152 :   public static class MergeabilityWeigher implements Weigher<EntryKey, Boolean> {
     169             :     @Override
     170             :     public int weigh(EntryKey k, Boolean v) {
     171          21 :       return 16
     172             :           + 2 * (16 + 20)
     173             :           + 3 * 8 // Size of EntryKey, 64-bit JVM.
     174             :           + 8; // Size of Boolean.
     175             :     }
     176             :   }
     177             : 
     178             :   private final SubmitDryRun submitDryRun;
     179             :   private final Cache<EntryKey, Boolean> cache;
     180             : 
     181             :   @Inject
     182             :   MergeabilityCacheImpl(
     183         146 :       SubmitDryRun submitDryRun, @Named(CACHE_NAME) Cache<EntryKey, Boolean> cache) {
     184         146 :     this.submitDryRun = submitDryRun;
     185         146 :     this.cache = cache;
     186         146 :   }
     187             : 
     188             :   @Override
     189             :   public boolean get(
     190             :       ObjectId commit,
     191             :       Ref intoRef,
     192             :       SubmitType submitType,
     193             :       String mergeStrategy,
     194             :       BranchNameKey dest,
     195             :       Repository repo) {
     196          21 :     ObjectId into = intoRef != null ? intoRef.getObjectId() : ObjectId.zeroId();
     197          21 :     EntryKey key = new EntryKey(commit, into, submitType, mergeStrategy);
     198             :     try {
     199          21 :       return cache.get(
     200             :           key,
     201             :           () -> {
     202          21 :             if (key.into.equals(ObjectId.zeroId())) {
     203          10 :               return true; // Assume yes on new branch.
     204             :             }
     205          21 :             try (CodeReviewRevWalk rw = CodeReviewCommit.newRevWalk(repo)) {
     206          21 :               Set<RevCommit> accepted = SubmitDryRun.getAlreadyAccepted(repo, rw);
     207          21 :               accepted.add(rw.parseCommit(key.into));
     208          21 :               accepted.addAll(Arrays.asList(rw.parseCommit(key.commit).getParents()));
     209          21 :               return submitDryRun.run(
     210             :                   null, key.submitType, repo, rw, dest, key.into, key.commit, accepted);
     211             :             }
     212             :           });
     213           0 :     } catch (ExecutionException | UncheckedExecutionException e) {
     214           0 :       logger.atSevere().withCause(e.getCause()).log(
     215             :           "Error checking mergeability of %s into %s (%s)",
     216           0 :           key.commit.name(), key.into.name(), key.submitType.name());
     217           0 :       return false;
     218             :     }
     219             :   }
     220             : 
     221             :   @Override
     222             :   public Boolean getIfPresent(
     223             :       ObjectId commit, Ref intoRef, SubmitType submitType, String mergeStrategy) {
     224           2 :     return cache.getIfPresent(new EntryKey(commit, toId(intoRef), submitType, mergeStrategy));
     225             :   }
     226             : }

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