DfsBlockCacheConfig.java

  1. /*
  2.  * Copyright (C) 2011, 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 static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
  12. import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_SECTION;
  13. import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_LIMIT;
  14. import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_SIZE;
  15. import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONCURRENCY_LEVEL;
  16. import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_RATIO;

  17. import java.text.MessageFormat;
  18. import java.time.Duration;
  19. import java.util.Collections;
  20. import java.util.Map;
  21. import java.util.function.Consumer;

  22. import org.eclipse.jgit.internal.JGitText;
  23. import org.eclipse.jgit.internal.storage.pack.PackExt;
  24. import org.eclipse.jgit.lib.Config;

  25. /**
  26.  * Configuration parameters for
  27.  * {@link org.eclipse.jgit.internal.storage.dfs.DfsBlockCache}.
  28.  */
  29. public class DfsBlockCacheConfig {
  30.     /** 1024 (number of bytes in one kibibyte/kilobyte) */
  31.     public static final int KB = 1024;

  32.     /** 1024 {@link #KB} (number of bytes in one mebibyte/megabyte) */
  33.     public static final int MB = 1024 * KB;

  34.     /** Default number of max cache hits. */
  35.     public static final int DEFAULT_CACHE_HOT_MAX = 1;

  36.     private long blockLimit;
  37.     private int blockSize;
  38.     private double streamRatio;
  39.     private int concurrencyLevel;

  40.     private Consumer<Long> refLock;
  41.     private Map<PackExt, Integer> cacheHotMap;

  42.     private IndexEventConsumer indexEventConsumer;

  43.     /**
  44.      * Create a default configuration.
  45.      */
  46.     public DfsBlockCacheConfig() {
  47.         setBlockLimit(32 * MB);
  48.         setBlockSize(64 * KB);
  49.         setStreamRatio(0.30);
  50.         setConcurrencyLevel(32);
  51.         cacheHotMap = Collections.emptyMap();
  52.     }

  53.     /**
  54.      * Get maximum number bytes of heap memory to dedicate to caching pack file
  55.      * data.
  56.      *
  57.      * @return maximum number bytes of heap memory to dedicate to caching pack
  58.      *         file data. <b>Default is 32 MB.</b>
  59.      */
  60.     public long getBlockLimit() {
  61.         return blockLimit;
  62.     }

  63.     /**
  64.      * Set maximum number bytes of heap memory to dedicate to caching pack file
  65.      * data.
  66.      * <p>
  67.      * It is strongly recommended to set the block limit to be an integer multiple
  68.      * of the block size. This constraint is not enforced by this method (since
  69.      * it may be called before {@link #setBlockSize(int)}), but it is enforced by
  70.      * {@link #fromConfig(Config)}.
  71.      *
  72.      * @param newLimit
  73.      *            maximum number bytes of heap memory to dedicate to caching
  74.      *            pack file data; must be positive.
  75.      * @return {@code this}
  76.      */
  77.     public DfsBlockCacheConfig setBlockLimit(long newLimit) {
  78.         if (newLimit <= 0) {
  79.             throw new IllegalArgumentException(MessageFormat.format(
  80.                     JGitText.get().blockLimitNotPositive,
  81.                     Long.valueOf(newLimit)));
  82.         }
  83.         blockLimit = newLimit;
  84.         return this;
  85.     }

  86.     /**
  87.      * Get size in bytes of a single window mapped or read in from the pack
  88.      * file.
  89.      *
  90.      * @return size in bytes of a single window mapped or read in from the pack
  91.      *         file. <b>Default is 64 KB.</b>
  92.      */
  93.     public int getBlockSize() {
  94.         return blockSize;
  95.     }

  96.     /**
  97.      * Set size in bytes of a single window read in from the pack file.
  98.      *
  99.      * @param newSize
  100.      *            size in bytes of a single window read in from the pack file.
  101.      *            The value must be a power of 2.
  102.      * @return {@code this}
  103.      */
  104.     public DfsBlockCacheConfig setBlockSize(int newSize) {
  105.         int size = Math.max(512, newSize);
  106.         if ((size & (size - 1)) != 0) {
  107.             throw new IllegalArgumentException(
  108.                     JGitText.get().blockSizeNotPowerOf2);
  109.         }
  110.         blockSize = size;
  111.         return this;
  112.     }

  113.     /**
  114.      * Get the estimated number of threads concurrently accessing the cache.
  115.      *
  116.      * @return the estimated number of threads concurrently accessing the cache.
  117.      *         <b>Default is 32.</b>
  118.      */
  119.     public int getConcurrencyLevel() {
  120.         return concurrencyLevel;
  121.     }

  122.     /**
  123.      * Set the estimated number of threads concurrently accessing the cache.
  124.      *
  125.      * @param newConcurrencyLevel
  126.      *            the estimated number of threads concurrently accessing the
  127.      *            cache.
  128.      * @return {@code this}
  129.      */
  130.     public DfsBlockCacheConfig setConcurrencyLevel(
  131.             final int newConcurrencyLevel) {
  132.         concurrencyLevel = newConcurrencyLevel;
  133.         return this;
  134.     }

  135.     /**
  136.      * Get highest percentage of {@link #getBlockLimit()} a single pack can
  137.      * occupy while being copied by the pack reuse strategy.
  138.      *
  139.      * @return highest percentage of {@link #getBlockLimit()} a single pack can
  140.      *         occupy while being copied by the pack reuse strategy. <b>Default
  141.      *         is 0.30, or 30%</b>.
  142.      */
  143.     public double getStreamRatio() {
  144.         return streamRatio;
  145.     }

  146.     /**
  147.      * Set percentage of cache to occupy with a copied pack.
  148.      *
  149.      * @param ratio
  150.      *            percentage of cache to occupy with a copied pack.
  151.      * @return {@code this}
  152.      */
  153.     public DfsBlockCacheConfig setStreamRatio(double ratio) {
  154.         streamRatio = Math.max(0, Math.min(ratio, 1.0));
  155.         return this;
  156.     }

  157.     /**
  158.      * Get the consumer of the object reference lock wait time in milliseconds.
  159.      *
  160.      * @return consumer of wait time in milliseconds.
  161.      */
  162.     public Consumer<Long> getRefLockWaitTimeConsumer() {
  163.         return refLock;
  164.     }

  165.     /**
  166.      * Set the consumer for lock wait time.
  167.      *
  168.      * @param c
  169.      *            consumer of wait time in milliseconds.
  170.      * @return {@code this}
  171.      */
  172.     public DfsBlockCacheConfig setRefLockWaitTimeConsumer(Consumer<Long> c) {
  173.         refLock = c;
  174.         return this;
  175.     }

  176.     /**
  177.      * Get the map of hot count per pack extension for {@code DfsBlockCache}.
  178.      *
  179.      * @return map of hot count per pack extension for {@code DfsBlockCache}.
  180.      */
  181.     public Map<PackExt, Integer> getCacheHotMap() {
  182.         return cacheHotMap;
  183.     }

  184.     /**
  185.      * Set the map of hot count per pack extension for {@code DfsBlockCache}.
  186.      *
  187.      * @param cacheHotMap
  188.      *            map of hot count per pack extension for {@code DfsBlockCache}.
  189.      * @return {@code this}
  190.      */
  191.     public DfsBlockCacheConfig setCacheHotMap(
  192.             Map<PackExt, Integer> cacheHotMap) {
  193.         this.cacheHotMap = Collections.unmodifiableMap(cacheHotMap);
  194.         return this;
  195.     }

  196.     /**
  197.      * Get the consumer of cache index events.
  198.      *
  199.      * @return consumer of cache index events.
  200.      */
  201.     public IndexEventConsumer getIndexEventConsumer() {
  202.         return indexEventConsumer;
  203.     }

  204.     /**
  205.      * Set the consumer of cache index events.
  206.      *
  207.      * @param indexEventConsumer
  208.      *            consumer of cache index events.
  209.      * @return {@code this}
  210.      */
  211.     public DfsBlockCacheConfig setIndexEventConsumer(
  212.             IndexEventConsumer indexEventConsumer) {
  213.         this.indexEventConsumer = indexEventConsumer;
  214.         return this;
  215.     }

  216.     /**
  217.      * Update properties by setting fields from the configuration.
  218.      * <p>
  219.      * If a property is not defined in the configuration, then it is left
  220.      * unmodified.
  221.      * <p>
  222.      * Enforces certain constraints on the combination of settings in the config,
  223.      * for example that the block limit is a multiple of the block size.
  224.      *
  225.      * @param rc
  226.      *            configuration to read properties from.
  227.      * @return {@code this}
  228.      */
  229.     public DfsBlockCacheConfig fromConfig(Config rc) {
  230.         long cfgBlockLimit = rc.getLong(
  231.                 CONFIG_CORE_SECTION,
  232.                 CONFIG_DFS_SECTION,
  233.                 CONFIG_KEY_BLOCK_LIMIT,
  234.                 getBlockLimit());
  235.         int cfgBlockSize = rc.getInt(
  236.                 CONFIG_CORE_SECTION,
  237.                 CONFIG_DFS_SECTION,
  238.                 CONFIG_KEY_BLOCK_SIZE,
  239.                 getBlockSize());
  240.         if (cfgBlockLimit % cfgBlockSize != 0) {
  241.             throw new IllegalArgumentException(MessageFormat.format(
  242.                     JGitText.get().blockLimitNotMultipleOfBlockSize,
  243.                     Long.valueOf(cfgBlockLimit),
  244.                     Long.valueOf(cfgBlockSize)));
  245.         }

  246.         setBlockLimit(cfgBlockLimit);
  247.         setBlockSize(cfgBlockSize);

  248.         setConcurrencyLevel(rc.getInt(
  249.                 CONFIG_CORE_SECTION,
  250.                 CONFIG_DFS_SECTION,
  251.                 CONFIG_KEY_CONCURRENCY_LEVEL,
  252.                 getConcurrencyLevel()));

  253.         String v = rc.getString(
  254.                 CONFIG_CORE_SECTION,
  255.                 CONFIG_DFS_SECTION,
  256.                 CONFIG_KEY_STREAM_RATIO);
  257.         if (v != null) {
  258.             try {
  259.                 setStreamRatio(Double.parseDouble(v));
  260.             } catch (NumberFormatException e) {
  261.                 throw new IllegalArgumentException(MessageFormat.format(
  262.                         JGitText.get().enumValueNotSupported3,
  263.                         CONFIG_CORE_SECTION,
  264.                         CONFIG_DFS_SECTION,
  265.                         CONFIG_KEY_STREAM_RATIO, v), e);
  266.             }
  267.         }
  268.         return this;
  269.     }

  270.     /** Consumer of DfsBlockCache loading and eviction events for indexes. */
  271.     public interface IndexEventConsumer {
  272.         /**
  273.          * Accept an event of an index requested. It could be loaded from either
  274.          * cache or storage.
  275.          *
  276.          * @param packExtPos
  277.          *            position in {@code PackExt} enum
  278.          * @param cacheHit
  279.          *            true if an index was already in cache. Otherwise, the
  280.          *            index was loaded from storage into the cache in the
  281.          *            current request,
  282.          * @param loadMicros
  283.          *            time to load an index from cache or storage in
  284.          *            microseconds
  285.          * @param bytes
  286.          *            number of bytes loaded
  287.          * @param lastEvictionDuration
  288.          *            time since last eviction, 0 if was not evicted yet
  289.          */
  290.         void acceptRequestedEvent(int packExtPos, boolean cacheHit,
  291.                 long loadMicros, long bytes, Duration lastEvictionDuration);

  292.         /**
  293.          * Accept an event of an index evicted from cache.
  294.          *
  295.          * @param packExtPos
  296.          *            position in {@code PackExt} enum
  297.          * @param bytes
  298.          *            number of bytes evicted
  299.          * @param totalCacheHitCount
  300.          *            number of times an index was accessed while in cache
  301.          * @param lastEvictionDuration
  302.          *            time since last eviction, 0 if was not evicted yet
  303.          */
  304.         default void acceptEvictedEvent(int packExtPos, long bytes,
  305.                 int totalCacheHitCount, Duration lastEvictionDuration) {
  306.             // Off by default.
  307.         }

  308.         /**
  309.          * @return true if reporting evicted events is enabled.
  310.          */
  311.         default boolean shouldReportEvictedEvent() {
  312.             return false;
  313.         }
  314.     }
  315. }