DfsReftableBatchRefUpdate.java

  1. /*
  2.  * Copyright (C) 2019, 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 org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
  12. import org.eclipse.jgit.internal.storage.io.BlockSource;
  13. import org.eclipse.jgit.internal.storage.pack.PackExt;
  14. import org.eclipse.jgit.internal.storage.reftable.ReftableBatchRefUpdate;
  15. import org.eclipse.jgit.internal.storage.reftable.ReftableCompactor;
  16. import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
  17. import org.eclipse.jgit.internal.storage.reftable.ReftableReader;
  18. import org.eclipse.jgit.internal.storage.reftable.ReftableWriter;
  19. import org.eclipse.jgit.lib.Ref;
  20. import org.eclipse.jgit.transport.ReceiveCommand;

  21. import java.io.ByteArrayOutputStream;
  22. import java.io.IOException;
  23. import java.io.OutputStream;
  24. import java.util.ArrayList;
  25. import java.util.Collections;
  26. import java.util.List;
  27. import java.util.Set;

  28. import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;

  29. /**
  30.  * {@link org.eclipse.jgit.lib.BatchRefUpdate} for
  31.  * {@link org.eclipse.jgit.internal.storage.dfs.DfsReftableDatabase}.
  32.  */
  33. public class DfsReftableBatchRefUpdate extends ReftableBatchRefUpdate {
  34.     private static final int AVG_BYTES = 36;

  35.     private final DfsReftableDatabase refdb;

  36.     private final DfsObjDatabase odb;

  37.     /**
  38.      * Initialize batch update.
  39.      *
  40.      * @param refdb
  41.      *            database the update will modify.
  42.      * @param odb
  43.      *            object database to store the reftable.
  44.      */
  45.     protected DfsReftableBatchRefUpdate(DfsReftableDatabase refdb,
  46.             DfsObjDatabase odb) {
  47.         super(refdb, refdb.reftableDatabase, refdb.getLock(), refdb.getRepository());
  48.         this.refdb = refdb;
  49.         this.odb = odb;
  50.     }

  51.     @Override
  52.     protected void applyUpdates(List<Ref> newRefs, List<ReceiveCommand> pending)
  53.             throws IOException {
  54.         Set<DfsPackDescription> prune = Collections.emptySet();
  55.         DfsPackDescription pack = odb.newPack(PackSource.INSERT);
  56.         try (DfsOutputStream out = odb.writeFile(pack, REFTABLE)) {
  57.             ReftableConfig cfg = DfsPackCompactor
  58.                     .configureReftable(refdb.getReftableConfig(), out);

  59.             ReftableWriter.Stats stats;
  60.             if (refdb.compactDuringCommit()
  61.                     && newRefs.size() * AVG_BYTES <= cfg.getRefBlockSize()
  62.                     && canCompactTopOfStack(cfg)) {
  63.                 ByteArrayOutputStream tmp = new ByteArrayOutputStream();
  64.                 ReftableWriter rw = new ReftableWriter(cfg, tmp);
  65.                 write(rw, newRefs, pending);
  66.                 rw.finish();
  67.                 stats = compactTopOfStack(out, cfg, tmp.toByteArray());
  68.                 prune = toPruneTopOfStack();
  69.             } else {
  70.                 ReftableWriter rw = new ReftableWriter(cfg, out);
  71.                 write(rw, newRefs, pending);
  72.                 rw.finish();
  73.                 stats = rw.getStats();
  74.             }
  75.             pack.addFileExt(REFTABLE);
  76.             pack.setReftableStats(stats);
  77.         }

  78.         odb.commitPack(Collections.singleton(pack), prune);
  79.         odb.addReftable(pack, prune);
  80.         refdb.clearCache();
  81.     }

  82.     private boolean canCompactTopOfStack(ReftableConfig cfg)
  83.             throws IOException {
  84.         refdb.getLock().lock();
  85.         try {
  86.             DfsReftableStack stack = refdb.stack();
  87.             List<ReftableReader> readers = stack.readers();
  88.             if (readers.isEmpty()) {
  89.                 return false;
  90.             }

  91.             int lastIdx = readers.size() - 1;
  92.             DfsReftable last = stack.files().get(lastIdx);
  93.             DfsPackDescription desc = last.getPackDescription();
  94.             if (desc.getPackSource() != PackSource.INSERT
  95.                 || !packOnlyContainsReftable(desc)) {
  96.                 return false;
  97.             }

  98.             ReftableReader table = readers.get(lastIdx);
  99.             int bs = cfg.getRefBlockSize();
  100.             return table.size() <= 3 * bs;
  101.         } finally {
  102.             refdb.getLock().unlock();
  103.         }
  104.     }

  105.     private ReftableWriter.Stats compactTopOfStack(OutputStream out,
  106.             ReftableConfig cfg, byte[] newTable) throws IOException {
  107.         refdb.getLock().lock();
  108.         try {
  109.             List<ReftableReader> stack = refdb.stack().readers();

  110.             ReftableReader last = stack.get(stack.size() - 1);

  111.             List<ReftableReader> tables = new ArrayList<>(2);
  112.             tables.add(last);
  113.             tables.add(new ReftableReader(BlockSource.from(newTable)));

  114.             ReftableCompactor compactor = new ReftableCompactor(out);
  115.             compactor.setConfig(cfg);
  116.             compactor.setIncludeDeletes(true);
  117.             compactor.addAll(tables);
  118.             compactor.compact();
  119.             return compactor.getStats();
  120.         } finally {
  121.             refdb.getLock().unlock();
  122.         }
  123.     }

  124.     private Set<DfsPackDescription> toPruneTopOfStack() throws IOException {
  125.         refdb.getLock().lock();
  126.         try {
  127.             List<DfsReftable> stack = refdb.stack().files();

  128.             DfsReftable last = stack.get(stack.size() - 1);
  129.             return Collections.singleton(last.getPackDescription());
  130.         } finally {
  131.             refdb.getLock().unlock();
  132.         }
  133.     }

  134.     private boolean packOnlyContainsReftable(DfsPackDescription desc) {
  135.         for (PackExt ext : PackExt.values()) {
  136.             if (ext != REFTABLE && desc.hasFileExt(ext)) {
  137.                 return false;
  138.             }
  139.         }
  140.         return true;
  141.     }
  142. }