LCOV - code coverage report
Current view: top level - server/git/receive - ReceivePackRefCache.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 46 49 93.9 %
Date: 2022-11-19 15:00:39 Functions: 15 16 93.8 %

          Line data    Source code
       1             : // Copyright (C) 2019 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.git.receive;
      16             : 
      17             : import static com.google.common.collect.ImmutableList.toImmutableList;
      18             : 
      19             : import com.google.common.base.Supplier;
      20             : import com.google.common.collect.ImmutableList;
      21             : import com.google.common.collect.ListMultimap;
      22             : import com.google.common.collect.MultimapBuilder;
      23             : import com.google.gerrit.entities.Change;
      24             : import com.google.gerrit.entities.PatchSet;
      25             : import com.google.gerrit.entities.RefNames;
      26             : import java.io.IOException;
      27             : import java.util.Map;
      28             : import java.util.Objects;
      29             : import org.eclipse.jgit.annotations.Nullable;
      30             : import org.eclipse.jgit.lib.ObjectId;
      31             : import org.eclipse.jgit.lib.Ref;
      32             : import org.eclipse.jgit.lib.RefDatabase;
      33             : 
      34             : /**
      35             :  * Simple cache for accessing refs by name, prefix or {@link ObjectId}. Intended to be used when
      36             :  * processing a {@code git push}.
      37             :  *
      38             :  * <p>This class is not thread safe.
      39             :  */
      40             : public interface ReceivePackRefCache {
      41             : 
      42             :   /**
      43             :    * Returns an instance that delegates all calls to the provided {@link RefDatabase}. To be used in
      44             :    * tests or when the ref database is fast with forward (name to {@link ObjectId}) and inverse
      45             :    * ({@code ObjectId} to name) lookups.
      46             :    */
      47             :   static ReceivePackRefCache noCache(RefDatabase delegate) {
      48           2 :     return new NoCache(delegate);
      49             :   }
      50             : 
      51             :   /**
      52             :    * Returns an instance that answers calls based on refs previously advertised and captured in
      53             :    * {@link AllRefsWatcher}. Speeds up inverse lookups by building a {@code Map<ObjectId,
      54             :    * List<Ref>>} and a {@code Map<Change.Id, List<Ref>>}.
      55             :    *
      56             :    * <p>This implementation speeds up lookups when the ref database does not support inverse ({@code
      57             :    * ObjectId} to name) lookups.
      58             :    */
      59             :   static ReceivePackRefCache withAdvertisedRefs(Supplier<Map<String, Ref>> allRefsSupplier) {
      60          98 :     return new WithAdvertisedRefs(allRefsSupplier);
      61             :   }
      62             : 
      63             :   /** Returns a list of {@link com.google.gerrit.entities.PatchSet.Id}s that point to {@code id}. */
      64             :   ImmutableList<PatchSet.Id> patchSetIdsFromObjectId(ObjectId id) throws IOException;
      65             : 
      66             :   /** Returns all refs whose name starts with {@code prefix}. */
      67             :   ImmutableList<Ref> byPrefix(String prefix) throws IOException;
      68             : 
      69             :   /** Returns a ref whose name matches {@code ref} or {@code null} if such a ref does not exist. */
      70             :   @Nullable
      71             :   Ref exactRef(String ref) throws IOException;
      72             : 
      73             :   class NoCache implements ReceivePackRefCache {
      74             :     private final RefDatabase delegate;
      75             : 
      76           2 :     private NoCache(RefDatabase delegate) {
      77           2 :       this.delegate = delegate;
      78           2 :     }
      79             : 
      80             :     @Override
      81             :     public ImmutableList<PatchSet.Id> patchSetIdsFromObjectId(ObjectId id) throws IOException {
      82           2 :       return delegate.getTipsWithSha1(id).stream()
      83           2 :           .map(r -> PatchSet.Id.fromRef(r.getName()))
      84           2 :           .filter(Objects::nonNull)
      85           2 :           .collect(toImmutableList());
      86             :     }
      87             : 
      88             :     @Override
      89             :     public ImmutableList<Ref> byPrefix(String prefix) throws IOException {
      90           2 :       return delegate.getRefsByPrefix(prefix).stream().collect(toImmutableList());
      91             :     }
      92             : 
      93             :     @Override
      94             :     @Nullable
      95             :     public Ref exactRef(String name) throws IOException {
      96           2 :       return delegate.exactRef(name);
      97             :     }
      98             :   }
      99             : 
     100             :   class WithAdvertisedRefs implements ReceivePackRefCache {
     101             :     /** We estimate that a change has an average of 4 patch sets plus the meta ref. */
     102             :     private static final int ESTIMATED_NUMBER_OF_REFS_PER_CHANGE = 5;
     103             : 
     104             :     private final Supplier<Map<String, Ref>> allRefsSupplier;
     105             : 
     106             :     // Collections lazily populated during processing.
     107             :     private Map<String, Ref> allRefs;
     108             :     /** Contains only patch set refs. */
     109             :     private ListMultimap<Change.Id, Ref> refsByChange;
     110             :     /** Contains all refs. */
     111             :     private ListMultimap<ObjectId, Ref> refsByObjectId;
     112             : 
     113          98 :     private WithAdvertisedRefs(Supplier<Map<String, Ref>> allRefsSupplier) {
     114          98 :       this.allRefsSupplier = allRefsSupplier;
     115          98 :     }
     116             : 
     117             :     @Override
     118             :     public ImmutableList<PatchSet.Id> patchSetIdsFromObjectId(ObjectId id) {
     119          97 :       lazilyInitRefMaps();
     120          97 :       return refsByObjectId.get(id).stream()
     121          97 :           .map(r -> PatchSet.Id.fromRef(r.getName()))
     122          97 :           .filter(Objects::nonNull)
     123          97 :           .collect(toImmutableList());
     124             :     }
     125             : 
     126             :     @Override
     127             :     public ImmutableList<Ref> byPrefix(String prefix) {
     128          97 :       lazilyInitRefMaps();
     129          97 :       if (RefNames.isRefsChanges(prefix)) {
     130          39 :         Change.Id cId = Change.Id.fromRefPart(prefix);
     131          39 :         if (cId != null) {
     132           0 :           return refsByChange.get(cId).stream()
     133           0 :               .filter(r -> r.getName().startsWith(prefix))
     134           0 :               .collect(toImmutableList());
     135             :         }
     136             :       }
     137          97 :       return allRefs().values().stream()
     138          97 :           .filter(r -> r.getName().startsWith(prefix))
     139          97 :           .collect(toImmutableList());
     140             :     }
     141             : 
     142             :     @Override
     143             :     @Nullable
     144             :     public Ref exactRef(String name) {
     145          97 :       return allRefs().get(name);
     146             :     }
     147             : 
     148             :     private Map<String, Ref> allRefs() {
     149          97 :       if (allRefs == null) {
     150          97 :         allRefs = allRefsSupplier.get();
     151             :       }
     152          97 :       return allRefs;
     153             :     }
     154             : 
     155             :     private void lazilyInitRefMaps() {
     156          97 :       if (refsByChange != null) {
     157          96 :         return;
     158             :       }
     159             : 
     160          97 :       refsByObjectId = MultimapBuilder.hashKeys().arrayListValues().build();
     161          97 :       refsByChange =
     162          97 :           MultimapBuilder.hashKeys(allRefs().size() / ESTIMATED_NUMBER_OF_REFS_PER_CHANGE)
     163          97 :               .arrayListValues(ESTIMATED_NUMBER_OF_REFS_PER_CHANGE)
     164          97 :               .build();
     165          97 :       for (Ref ref : allRefs().values()) {
     166          97 :         ObjectId objectId = ref.getObjectId();
     167          97 :         if (objectId != null) {
     168          97 :           refsByObjectId.put(objectId, ref);
     169          97 :           Change.Id changeId = Change.Id.fromRef(ref.getName());
     170          97 :           if (changeId != null) {
     171          67 :             refsByChange.put(changeId, ref);
     172             :           }
     173             :         }
     174          97 :       }
     175          97 :     }
     176             :   }
     177             : }

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