DfsReftableDatabase.java

  1. /*
  2.  * Copyright (C) 2017, Google Inc. and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

  10. package org.eclipse.jgit.internal.storage.dfs;

  11. import java.io.IOException;
  12. import java.util.Arrays;
  13. import java.util.HashSet;
  14. import java.util.List;
  15. import java.util.Map;
  16. import java.util.Set;
  17. import java.util.TreeSet;
  18. import java.util.concurrent.locks.Lock;
  19. import java.util.concurrent.locks.ReentrantLock;

  20. import org.eclipse.jgit.annotations.Nullable;
  21. import org.eclipse.jgit.internal.storage.reftable.MergedReftable;
  22. import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
  23. import org.eclipse.jgit.internal.storage.reftable.ReftableDatabase;
  24. import org.eclipse.jgit.lib.BatchRefUpdate;
  25. import org.eclipse.jgit.lib.NullProgressMonitor;
  26. import org.eclipse.jgit.lib.ObjectId;
  27. import org.eclipse.jgit.lib.Ref;
  28. import org.eclipse.jgit.revwalk.RevWalk;
  29. import org.eclipse.jgit.transport.ReceiveCommand;
  30. import org.eclipse.jgit.util.RefList;
  31. import org.eclipse.jgit.util.RefMap;

  32. /**
  33.  * A {@link org.eclipse.jgit.internal.storage.dfs.DfsRefDatabase} that uses
  34.  * reftable for storage.
  35.  * <p>
  36.  * A {@code DfsRefDatabase} instance is thread-safe.
  37.  * <p>
  38.  * Implementors may wish to use
  39.  * {@link org.eclipse.jgit.internal.storage.dfs.DfsPackDescription#getMaxUpdateIndex()}
  40.  * as the primary key identifier for a
  41.  * {@link org.eclipse.jgit.internal.storage.pack.PackExt#REFTABLE} only pack
  42.  * description, ensuring that when there are competing transactions one wins,
  43.  * and one will fail.
  44.  */
  45. public class DfsReftableDatabase extends DfsRefDatabase {
  46.     final ReftableDatabase reftableDatabase;

  47.     private DfsReader ctx;
  48.     private DfsReftableStack stack;

  49.     /**
  50.      * Initialize the reference database for a repository.
  51.      *
  52.      * @param repo
  53.      *            the repository this database instance manages references for.
  54.      */
  55.     protected DfsReftableDatabase(DfsRepository repo) {
  56.         super(repo);
  57.         reftableDatabase = new ReftableDatabase() {
  58.             @Override
  59.             public MergedReftable openMergedReftable() throws IOException {
  60.                 Lock l = DfsReftableDatabase.this.getLock();
  61.                 l.lock();
  62.                 try {
  63.                     return new MergedReftable(stack().readers());
  64.                 } finally {
  65.                     l.unlock();
  66.                 }
  67.             }
  68.         };
  69.         stack = null;
  70.     }

  71.     /** {@inheritDoc} */
  72.     @Override
  73.     public boolean hasVersioning() {
  74.         return true;
  75.     }

  76.     /** {@inheritDoc} */
  77.     @Override
  78.     public boolean performsAtomicTransactions() {
  79.         return true;
  80.     }

  81.     /** {@inheritDoc} */
  82.     @Override
  83.     public BatchRefUpdate newBatchUpdate() {
  84.         DfsObjDatabase odb = getRepository().getObjectDatabase();
  85.         return new DfsReftableBatchRefUpdate(this, odb);
  86.     }

  87.     /**
  88.      * Get configuration to write new reftables with.
  89.      *
  90.      * @return configuration to write new reftables with.
  91.      */
  92.     public ReftableConfig getReftableConfig() {
  93.         return new ReftableConfig(getRepository());
  94.     }

  95.     /**
  96.      * Get the lock protecting this instance's state.
  97.      *
  98.      * @return the lock protecting this instance's state.
  99.      */
  100.     protected ReentrantLock getLock() {
  101.         return reftableDatabase.getLock();
  102.     }

  103.     /**
  104.      * Whether to compact reftable instead of extending the stack depth.
  105.      *
  106.      * @return {@code true} if commit of a new small reftable should try to
  107.      *         replace a prior small reftable by performing a compaction,
  108.      *         instead of extending the stack depth.
  109.      */
  110.     protected boolean compactDuringCommit() {
  111.         return true;
  112.     }


  113.     /**
  114.      * Obtain a handle to the stack of reftables. Must hold lock.
  115.      *
  116.      * @return (possibly cached) handle to the stack.
  117.      * @throws java.io.IOException
  118.      *             if tables cannot be opened.
  119.      */
  120.     protected DfsReftableStack stack() throws IOException {
  121.         if (!getLock().isLocked()) {
  122.             throw new IllegalStateException("most hold lock to access stack"); //$NON-NLS-1$
  123.         }
  124.         DfsObjDatabase odb = getRepository().getObjectDatabase();

  125.         if (ctx == null) {
  126.             ctx = odb.newReader();
  127.         }
  128.         if (stack == null) {
  129.             stack = DfsReftableStack.open(ctx, Arrays.asList(odb.getReftables()));
  130.         }
  131.         return stack;
  132.     }

  133.     @Override
  134.     public boolean isNameConflicting(String refName) throws IOException {
  135.         return reftableDatabase.isNameConflicting(refName, new TreeSet<>(), new HashSet<>());
  136.     }

  137.     /** {@inheritDoc} */
  138.     @Override
  139.     public Ref exactRef(String name) throws IOException {
  140.         return reftableDatabase.exactRef(name);
  141.     }

  142.     /** {@inheritDoc} */
  143.     @Override
  144.     public Map<String, Ref> getRefs(String prefix) throws IOException {
  145.         List<Ref> refs = reftableDatabase.getRefsByPrefix(prefix);
  146.         RefList.Builder<Ref> builder = new RefList.Builder<>(refs.size());
  147.         for (Ref r : refs) {
  148.             builder.add(r);
  149.         }
  150.         return new RefMap(prefix, builder.toRefList(), RefList.emptyList(),
  151.             RefList.emptyList());
  152.     }

  153.     /** {@inheritDoc} */
  154.     @Override
  155.     public List<Ref> getRefsByPrefix(String prefix) throws IOException {

  156.         return reftableDatabase.getRefsByPrefix(prefix);
  157.     }

  158.     /** {@inheritDoc} */
  159.     @Override
  160.     public List<Ref> getRefsByPrefixWithExclusions(String include, Set<String> excludes)
  161.             throws IOException {
  162.         return reftableDatabase.getRefsByPrefixWithExclusions(include, excludes);
  163.     }

  164.     /** {@inheritDoc} */
  165.     @Override
  166.     public Set<Ref> getTipsWithSha1(ObjectId id) throws IOException {
  167.         if (!getReftableConfig().isIndexObjects()) {
  168.             return super.getTipsWithSha1(id);
  169.         }
  170.         return reftableDatabase.getTipsWithSha1(id);
  171.     }

  172.     /** {@inheritDoc} */
  173.     @Override
  174.     public boolean hasFastTipsWithSha1() throws IOException {
  175.         return reftableDatabase.hasFastTipsWithSha1();
  176.     }

  177.     /** {@inheritDoc} */
  178.     @Override
  179.     public Ref peel(Ref ref) throws IOException {
  180.         Ref oldLeaf = ref.getLeaf();
  181.         if (oldLeaf.isPeeled() || oldLeaf.getObjectId() == null) {
  182.             return ref;
  183.         }
  184.         return recreate(ref, doPeel(oldLeaf), hasVersioning());
  185.     }

  186.     @Override
  187.     boolean exists() throws IOException {
  188.         DfsObjDatabase odb = getRepository().getObjectDatabase();
  189.         return odb.getReftables().length > 0;
  190.     }

  191.     @Override
  192.     void clearCache() {
  193.         ReentrantLock l = getLock();
  194.         l.lock();
  195.         try {
  196.             if (ctx != null) {
  197.                 ctx.close();
  198.                 ctx = null;
  199.             }
  200.             reftableDatabase.clearCache();
  201.             if (stack != null) {
  202.                 stack.close();
  203.                 stack = null;
  204.             }
  205.         } finally {
  206.             l.unlock();
  207.         }
  208.     }

  209.     /** {@inheritDoc} */
  210.     @Override
  211.     protected boolean compareAndPut(Ref oldRef, @Nullable Ref newRef)
  212.             throws IOException {
  213.         ReceiveCommand cmd = ReftableDatabase.toCommand(oldRef, newRef);
  214.         try (RevWalk rw = new RevWalk(getRepository())) {
  215.             rw.setRetainBody(false);
  216.             newBatchUpdate().setAllowNonFastForwards(true).addCommand(cmd)
  217.                     .execute(rw, NullProgressMonitor.INSTANCE);
  218.         }
  219.         switch (cmd.getResult()) {
  220.         case OK:
  221.             return true;
  222.         case REJECTED_OTHER_REASON:
  223.             throw new IOException(cmd.getMessage());
  224.         case LOCK_FAILURE:
  225.         default:
  226.             return false;
  227.         }
  228.     }

  229.     /** {@inheritDoc} */
  230.     @Override
  231.     protected boolean compareAndRemove(Ref oldRef) throws IOException {
  232.         return compareAndPut(oldRef, null);
  233.     }

  234.     /** {@inheritDoc} */
  235.     @Override
  236.     protected RefCache scanAllRefs() throws IOException {
  237.         throw new UnsupportedOperationException();
  238.     }

  239.     @Override
  240.     void stored(Ref ref) {
  241.         // Unnecessary; DfsReftableBatchRefUpdate calls clearCache().
  242.     }

  243.     @Override
  244.     void removed(String refName) {
  245.         // Unnecessary; DfsReftableBatchRefUpdate calls clearCache().
  246.     }

  247.     /** {@inheritDoc} */
  248.     @Override
  249.     protected void cachePeeledState(Ref oldLeaf, Ref newLeaf) {
  250.         // Do not cache peeled state in reftable.
  251.     }

  252. }