DfsPackDescription.java

  1. /*
  2.  * Copyright (C) 2011, 2013 Google Inc., and others. 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 static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
  12. import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;

  13. import java.util.Arrays;
  14. import java.util.Comparator;

  15. import org.eclipse.jgit.annotations.NonNull;
  16. import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
  17. import org.eclipse.jgit.internal.storage.pack.PackExt;
  18. import org.eclipse.jgit.internal.storage.reftable.ReftableWriter;
  19. import org.eclipse.jgit.storage.pack.PackStatistics;

  20. /**
  21.  * Description of a DFS stored pack/index file.
  22.  * <p>
  23.  * Implementors may extend this class and add additional data members.
  24.  * <p>
  25.  * Instances of this class are cached with the DfsPackFile, and should not be
  26.  * modified once initialized and presented to the JGit DFS library.
  27.  */
  28. public class DfsPackDescription {
  29.     /**
  30.      * Comparator for packs when looking up objects in indexes.
  31.      * <p>
  32.      * This comparator tries to position packs in the order readers should examine
  33.      * them when looking for objects by SHA-1. The default tries to sort packs
  34.      * with more recent modification dates before older packs, and packs with
  35.      * fewer objects before packs with more objects.
  36.      * <p>
  37.      * Uses {@link PackSource#DEFAULT_COMPARATOR} for the portion of comparison
  38.      * where packs are sorted by source.
  39.      *
  40.      * @return comparator.
  41.      */
  42.     public static Comparator<DfsPackDescription> objectLookupComparator() {
  43.         return objectLookupComparator(PackSource.DEFAULT_COMPARATOR);
  44.     }

  45.     /**
  46.      * Comparator for packs when looking up objects in indexes.
  47.      * <p>
  48.      * This comparator tries to position packs in the order readers should examine
  49.      * them when looking for objects by SHA-1. The default tries to sort packs
  50.      * with more recent modification dates before older packs, and packs with
  51.      * fewer objects before packs with more objects.
  52.      *
  53.      * @param packSourceComparator
  54.      *            comparator for the {@link PackSource}, used as the first step in
  55.      *            comparison.
  56.      * @return comparator.
  57.      */
  58.     public static Comparator<DfsPackDescription> objectLookupComparator(
  59.             Comparator<PackSource> packSourceComparator) {
  60.         return Comparator.comparing(
  61.                     DfsPackDescription::getPackSource, packSourceComparator)
  62.             .thenComparing((a, b) -> {
  63.                 PackSource as = a.getPackSource();
  64.                 PackSource bs = b.getPackSource();

  65.                 // Tie break GC type packs by smallest first. There should be at most
  66.                 // one of each source, but when multiple exist concurrent GCs may have
  67.                 // run. Preferring the smaller file selects higher quality delta
  68.                 // compression, placing less demand on the DfsBlockCache.
  69.                 if (as == bs && isGC(as)) {
  70.                     int cmp = Long.signum(a.getFileSize(PACK) - b.getFileSize(PACK));
  71.                     if (cmp != 0) {
  72.                         return cmp;
  73.                     }
  74.                 }

  75.                 // Newer packs should sort first.
  76.                 int cmp = Long.signum(b.getLastModified() - a.getLastModified());
  77.                 if (cmp != 0) {
  78.                     return cmp;
  79.                 }

  80.                 // Break ties on smaller index. Readers may get lucky and find
  81.                 // the object they care about in the smaller index. This also pushes
  82.                 // big historical packs to the end of the list, due to more objects.
  83.                 return Long.signum(a.getObjectCount() - b.getObjectCount());
  84.             });
  85.     }

  86.     static Comparator<DfsPackDescription> reftableComparator() {
  87.         return (a, b) -> {
  88.                 // GC, COMPACT reftables first by reversing default order.
  89.                 int c = PackSource.DEFAULT_COMPARATOR.reversed()
  90.                         .compare(a.getPackSource(), b.getPackSource());
  91.                 if (c != 0) {
  92.                     return c;
  93.                 }

  94.                 // Lower maxUpdateIndex first.
  95.                 c = Long.signum(a.getMaxUpdateIndex() - b.getMaxUpdateIndex());
  96.                 if (c != 0) {
  97.                     return c;
  98.                 }

  99.                 // Older reftable first.
  100.                 return Long.signum(a.getLastModified() - b.getLastModified());
  101.             };
  102.     }

  103.     static Comparator<DfsPackDescription> reuseComparator() {
  104.         return (a, b) -> {
  105.             PackSource as = a.getPackSource();
  106.             PackSource bs = b.getPackSource();

  107.             if (as == bs && DfsPackDescription.isGC(as)) {
  108.                 // Push smaller GC files last; these likely have higher quality
  109.                 // delta compression and the contained representation should be
  110.                 // favored over other files.
  111.                 return Long.signum(b.getFileSize(PACK) - a.getFileSize(PACK));
  112.             }

  113.             // DfsPackDescription.compareTo already did a reasonable sort.
  114.             // Rely on Arrays.sort being stable, leaving equal elements.
  115.             return 0;
  116.         };
  117.     }

  118.     private final DfsRepositoryDescription repoDesc;
  119.     private final String packName;
  120.     private PackSource packSource;
  121.     private long lastModified;
  122.     private long[] sizeMap;
  123.     private int[] blockSizeMap;
  124.     private long objectCount;
  125.     private long deltaCount;
  126.     private long minUpdateIndex;
  127.     private long maxUpdateIndex;

  128.     private PackStatistics packStats;
  129.     private ReftableWriter.Stats refStats;
  130.     private int extensions;
  131.     private int indexVersion;
  132.     private long estimatedPackSize;

  133.     /**
  134.      * Initialize a description by pack name and repository.
  135.      * <p>
  136.      * The corresponding index file is assumed to exist. If this is not true
  137.      * implementors must extend the class and override
  138.      * {@link #getFileName(PackExt)}.
  139.      * <p>
  140.      * Callers should also try to fill in other fields if they are reasonably
  141.      * free to access at the time this instance is being initialized.
  142.      *
  143.      * @param name
  144.      *            name of the pack file. Must end with ".pack".
  145.      * @param repoDesc
  146.      *            description of the repo containing the pack file.
  147.      * @param packSource
  148.      *            the source of the pack.
  149.      */
  150.     public DfsPackDescription(DfsRepositoryDescription repoDesc, String name,
  151.             @NonNull PackSource packSource) {
  152.         this.repoDesc = repoDesc;
  153.         int dot = name.lastIndexOf('.');
  154.         this.packName = (dot < 0) ? name : name.substring(0, dot);
  155.         this.packSource = packSource;

  156.         int extCnt = PackExt.values().length;
  157.         sizeMap = new long[extCnt];
  158.         blockSizeMap = new int[extCnt];
  159.     }

  160.     /**
  161.      * Get description of the repository.
  162.      *
  163.      * @return description of the repository.
  164.      */
  165.     public DfsRepositoryDescription getRepositoryDescription() {
  166.         return repoDesc;
  167.     }

  168.     /**
  169.      * Adds the pack file extension to the known list.
  170.      *
  171.      * @param ext
  172.      *            the file extension
  173.      */
  174.     public void addFileExt(PackExt ext) {
  175.         extensions |= ext.getBit();
  176.     }

  177.     /**
  178.      * Whether the pack file extension is known to exist.
  179.      *
  180.      * @param ext
  181.      *            the file extension
  182.      * @return whether the pack file extension is known to exist.
  183.      */
  184.     public boolean hasFileExt(PackExt ext) {
  185.         return (extensions & ext.getBit()) != 0;
  186.     }

  187.     /**
  188.      * Get file name
  189.      *
  190.      * @param ext
  191.      *            the file extension
  192.      * @return name of the file.
  193.      */
  194.     public String getFileName(PackExt ext) {
  195.         return packName + '.' + ext.getExtension();
  196.     }

  197.     /**
  198.      * Get cache key for use by the block cache.
  199.      *
  200.      * @param ext
  201.      *            the file extension.
  202.      * @return cache key for use by the block cache.
  203.      */
  204.     public DfsStreamKey getStreamKey(PackExt ext) {
  205.         return DfsStreamKey.of(getRepositoryDescription(), getFileName(ext),
  206.                 ext);
  207.     }

  208.     /**
  209.      * Get the source of the pack.
  210.      *
  211.      * @return the source of the pack.
  212.      */
  213.     @NonNull
  214.     public PackSource getPackSource() {
  215.         return packSource;
  216.     }

  217.     /**
  218.      * Set the source of the pack.
  219.      *
  220.      * @param source
  221.      *            the source of the pack.
  222.      * @return {@code this}
  223.      */
  224.     public DfsPackDescription setPackSource(@NonNull PackSource source) {
  225.         packSource = source;
  226.         return this;
  227.     }

  228.     /**
  229.      * Get time the pack was created, in milliseconds.
  230.      *
  231.      * @return time the pack was created, in milliseconds.
  232.      */
  233.     public long getLastModified() {
  234.         return lastModified;
  235.     }

  236.     /**
  237.      * Set time the pack was created, in milliseconds.
  238.      *
  239.      * @param timeMillis
  240.      *            time the pack was created, in milliseconds. 0 if not known.
  241.      * @return {@code this}
  242.      */
  243.     public DfsPackDescription setLastModified(long timeMillis) {
  244.         lastModified = timeMillis;
  245.         return this;
  246.     }

  247.     /**
  248.      * Get minUpdateIndex for the reftable, if present.
  249.      *
  250.      * @return minUpdateIndex for the reftable, if present.
  251.      */
  252.     public long getMinUpdateIndex() {
  253.         return minUpdateIndex;
  254.     }

  255.     /**
  256.      * Set minUpdateIndex for the reftable.
  257.      *
  258.      * @param min
  259.      *            minUpdateIndex for the reftable.
  260.      * @return {@code this}
  261.      */
  262.     public DfsPackDescription setMinUpdateIndex(long min) {
  263.         minUpdateIndex = min;
  264.         return this;
  265.     }

  266.     /**
  267.      * Get maxUpdateIndex for the reftable, if present.
  268.      *
  269.      * @return maxUpdateIndex for the reftable, if present.
  270.      */
  271.     public long getMaxUpdateIndex() {
  272.         return maxUpdateIndex;
  273.     }

  274.     /**
  275.      * Set maxUpdateIndex for the reftable.
  276.      *
  277.      * @param max
  278.      *            maxUpdateIndex for the reftable.
  279.      * @return {@code this}
  280.      */
  281.     public DfsPackDescription setMaxUpdateIndex(long max) {
  282.         maxUpdateIndex = max;
  283.         return this;
  284.     }

  285.     /**
  286.      * Set size of the file in bytes.
  287.      *
  288.      * @param ext
  289.      *            the file extension.
  290.      * @param bytes
  291.      *            size of the file in bytes. If 0 the file is not known and will
  292.      *            be determined on first read.
  293.      * @return {@code this}
  294.      */
  295.     public DfsPackDescription setFileSize(PackExt ext, long bytes) {
  296.         int i = ext.getPosition();
  297.         if (i >= sizeMap.length) {
  298.             sizeMap = Arrays.copyOf(sizeMap, i + 1);
  299.         }
  300.         sizeMap[i] = Math.max(0, bytes);
  301.         return this;
  302.     }

  303.     /**
  304.      * Get size of the file, in bytes.
  305.      *
  306.      * @param ext
  307.      *            the file extension.
  308.      * @return size of the file, in bytes. If 0 the file size is not yet known.
  309.      */
  310.     public long getFileSize(PackExt ext) {
  311.         int i = ext.getPosition();
  312.         return i < sizeMap.length ? sizeMap[i] : 0;
  313.     }

  314.     /**
  315.      * Get blockSize of the file, in bytes.
  316.      *
  317.      * @param ext
  318.      *            the file extension.
  319.      * @return blockSize of the file, in bytes. If 0 the blockSize size is not
  320.      *         yet known and may be discovered when opening the file.
  321.      */
  322.     public int getBlockSize(PackExt ext) {
  323.         int i = ext.getPosition();
  324.         return i < blockSizeMap.length ? blockSizeMap[i] : 0;
  325.     }

  326.     /**
  327.      * Set blockSize of the file, in bytes.
  328.      *
  329.      * @param ext
  330.      *            the file extension.
  331.      * @param blockSize
  332.      *            blockSize of the file, in bytes. If 0 the blockSize is not
  333.      *            known and will be determined on first read.
  334.      * @return {@code this}
  335.      */
  336.     public DfsPackDescription setBlockSize(PackExt ext, int blockSize) {
  337.         int i = ext.getPosition();
  338.         if (i >= blockSizeMap.length) {
  339.             blockSizeMap = Arrays.copyOf(blockSizeMap, i + 1);
  340.         }
  341.         blockSizeMap[i] = Math.max(0, blockSize);
  342.         return this;
  343.     }

  344.     /**
  345.      * Set estimated size of the .pack file in bytes.
  346.      *
  347.      * @param estimatedPackSize
  348.      *            estimated size of the .pack file in bytes. If 0 the pack file
  349.      *            size is unknown.
  350.      * @return {@code this}
  351.      */
  352.     public DfsPackDescription setEstimatedPackSize(long estimatedPackSize) {
  353.         this.estimatedPackSize = Math.max(0, estimatedPackSize);
  354.         return this;
  355.     }

  356.     /**
  357.      * Get estimated size of the .pack file in bytes.
  358.      *
  359.      * @return estimated size of the .pack file in bytes. If 0 the pack file
  360.      *         size is unknown.
  361.      */
  362.     public long getEstimatedPackSize() {
  363.         return estimatedPackSize;
  364.     }

  365.     /**
  366.      * Get number of objects in the pack.
  367.      *
  368.      * @return number of objects in the pack.
  369.      */
  370.     public long getObjectCount() {
  371.         return objectCount;
  372.     }

  373.     /**
  374.      * Set number of objects in the pack.
  375.      *
  376.      * @param cnt
  377.      *            number of objects in the pack.
  378.      * @return {@code this}
  379.      */
  380.     public DfsPackDescription setObjectCount(long cnt) {
  381.         objectCount = Math.max(0, cnt);
  382.         return this;
  383.     }

  384.     /**
  385.      * Get number of delta compressed objects in the pack.
  386.      *
  387.      * @return number of delta compressed objects in the pack.
  388.      */
  389.     public long getDeltaCount() {
  390.         return deltaCount;
  391.     }

  392.     /**
  393.      * Set number of delta compressed objects in the pack.
  394.      *
  395.      * @param cnt
  396.      *            number of delta compressed objects in the pack.
  397.      * @return {@code this}
  398.      */
  399.     public DfsPackDescription setDeltaCount(long cnt) {
  400.         deltaCount = Math.max(0, cnt);
  401.         return this;
  402.     }

  403.     /**
  404.      * Get statistics from PackWriter, if the pack was built with it.
  405.      *
  406.      * @return statistics from PackWriter, if the pack was built with it.
  407.      *         Generally this is only available for packs created by
  408.      *         DfsGarbageCollector or DfsPackCompactor, and only when the pack
  409.      *         is being committed to the repository.
  410.      */
  411.     public PackStatistics getPackStats() {
  412.         return packStats;
  413.     }

  414.     DfsPackDescription setPackStats(PackStatistics stats) {
  415.         this.packStats = stats;
  416.         setFileSize(PACK, stats.getTotalBytes());
  417.         setObjectCount(stats.getTotalObjects());
  418.         setDeltaCount(stats.getTotalDeltas());
  419.         return this;
  420.     }

  421.     /**
  422.      * Get stats from the sibling reftable, if created.
  423.      *
  424.      * @return stats from the sibling reftable, if created.
  425.      */
  426.     public ReftableWriter.Stats getReftableStats() {
  427.         return refStats;
  428.     }

  429.     void setReftableStats(ReftableWriter.Stats stats) {
  430.         this.refStats = stats;
  431.         setMinUpdateIndex(stats.minUpdateIndex());
  432.         setMaxUpdateIndex(stats.maxUpdateIndex());
  433.         setFileSize(REFTABLE, stats.totalBytes());
  434.         setBlockSize(REFTABLE, stats.refBlockSize());
  435.     }

  436.     /**
  437.      * Discard the pack statistics, if it was populated.
  438.      *
  439.      * @return {@code this}
  440.      */
  441.     public DfsPackDescription clearPackStats() {
  442.         packStats = null;
  443.         refStats = null;
  444.         return this;
  445.     }

  446.     /**
  447.      * Get the version of the index file written.
  448.      *
  449.      * @return the version of the index file written.
  450.      */
  451.     public int getIndexVersion() {
  452.         return indexVersion;
  453.     }

  454.     /**
  455.      * Set the version of the index file written.
  456.      *
  457.      * @param version
  458.      *            the version of the index file written.
  459.      * @return {@code this}
  460.      */
  461.     public DfsPackDescription setIndexVersion(int version) {
  462.         indexVersion = version;
  463.         return this;
  464.     }

  465.     /** {@inheritDoc} */
  466.     @Override
  467.     public int hashCode() {
  468.         return packName.hashCode();
  469.     }

  470.     /** {@inheritDoc} */
  471.     @Override
  472.     public boolean equals(Object b) {
  473.         if (b instanceof DfsPackDescription) {
  474.             DfsPackDescription desc = (DfsPackDescription) b;
  475.             return packName.equals(desc.packName) &&
  476.                     getRepositoryDescription().equals(desc.getRepositoryDescription());
  477.         }
  478.         return false;
  479.     }

  480.     static boolean isGC(PackSource s) {
  481.         switch (s) {
  482.         case GC:
  483.         case GC_REST:
  484.             return true;
  485.         default:
  486.             return false;
  487.         }
  488.     }

  489.     /** {@inheritDoc} */
  490.     @Override
  491.     public String toString() {
  492.         return getFileName(PackExt.PACK);
  493.     }
  494. }