public void testScalingExecutorType() throws InterruptedException {
    String threadPoolName = randomThreadPool(ThreadPool.ThreadPoolType.SCALING);
    ThreadPool threadPool = null;
    try {
      Settings nodeSettings =
          Settings.builder()
              .put("threadpool." + threadPoolName + ".size", 10)
              .put("node.name", "testScalingExecutorType")
              .build();
      threadPool = new ThreadPool(nodeSettings);
      ClusterSettings clusterSettings =
          new ClusterSettings(nodeSettings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
      threadPool.setClusterSettings(clusterSettings);
      final int expectedMinimum = "generic".equals(threadPoolName) ? 4 : 1;
      assertThat(info(threadPool, threadPoolName).getMin(), equalTo(expectedMinimum));
      assertThat(info(threadPool, threadPoolName).getMax(), equalTo(10));
      final long expectedKeepAlive = "generic".equals(threadPoolName) ? 30 : 300;
      assertThat(
          info(threadPool, threadPoolName).getKeepAlive().seconds(), equalTo(expectedKeepAlive));
      assertEquals(
          info(threadPool, threadPoolName).getThreadPoolType(), ThreadPool.ThreadPoolType.SCALING);
      assertThat(threadPool.executor(threadPoolName), instanceOf(EsThreadPoolExecutor.class));

      // Change settings that doesn't require pool replacement
      Executor oldExecutor = threadPool.executor(threadPoolName);
      clusterSettings.applySettings(
          Settings.builder()
              .put("threadpool." + threadPoolName + ".keep_alive", "10m")
              .put("threadpool." + threadPoolName + ".min", "2")
              .put("threadpool." + threadPoolName + ".size", "15")
              .build());
      assertEquals(
          info(threadPool, threadPoolName).getThreadPoolType(), ThreadPool.ThreadPoolType.SCALING);
      assertThat(threadPool.executor(threadPoolName), instanceOf(EsThreadPoolExecutor.class));
      assertThat(
          ((EsThreadPoolExecutor) threadPool.executor(threadPoolName)).getCorePoolSize(),
          equalTo(2));
      assertThat(
          ((EsThreadPoolExecutor) threadPool.executor(threadPoolName)).getMaximumPoolSize(),
          equalTo(15));
      assertThat(info(threadPool, threadPoolName).getMin(), equalTo(2));
      assertThat(info(threadPool, threadPoolName).getMax(), equalTo(15));
      // Make sure keep alive value changed
      assertThat(info(threadPool, threadPoolName).getKeepAlive().minutes(), equalTo(10L));
      assertThat(
          ((EsThreadPoolExecutor) threadPool.executor(threadPoolName))
              .getKeepAliveTime(TimeUnit.MINUTES),
          equalTo(10L));
      assertThat(threadPool.executor(threadPoolName), sameInstance(oldExecutor));
    } finally {
      terminateThreadPoolIfNeeded(threadPool);
    }
  }
  public void testIndexingThreadPoolsMaxSize() throws InterruptedException {
    String threadPoolName = randomThreadPoolName();
    for (String name : new String[] {ThreadPool.Names.BULK, ThreadPool.Names.INDEX}) {
      ThreadPool threadPool = null;
      try {

        int maxSize = EsExecutors.boundedNumberOfProcessors(Settings.EMPTY);

        // try to create a too-big (maxSize+1) thread pool
        threadPool =
            new ThreadPool(
                Settings.builder()
                    .put("node.name", "testIndexingThreadPoolsMaxSize")
                    .put("threadpool." + name + ".size", maxSize + 1)
                    .build());

        // confirm it clipped us at the maxSize:
        assertEquals(
            maxSize, ((ThreadPoolExecutor) threadPool.executor(name)).getMaximumPoolSize());

        ClusterSettings clusterSettings =
            new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
        threadPool.setClusterSettings(clusterSettings);

        // update it to a tiny size:
        clusterSettings.applySettings(
            Settings.builder().put("threadpool." + name + ".size", 1).build());

        // confirm it worked:
        assertEquals(1, ((ThreadPoolExecutor) threadPool.executor(name)).getMaximumPoolSize());

        // try to update to too-big size:
        clusterSettings.applySettings(
            Settings.builder().put("threadpool." + name + ".size", maxSize + 1).build());

        // confirm it clipped us at the maxSize:
        assertEquals(
            maxSize, ((ThreadPoolExecutor) threadPool.executor(name)).getMaximumPoolSize());
      } finally {
        terminateThreadPoolIfNeeded(threadPool);
      }
    }
  }
  public void testUpdateSettingsCanNotChangeThreadPoolType() throws InterruptedException {
    String threadPoolName = randomThreadPoolName();
    ThreadPool.ThreadPoolType invalidThreadPoolType = randomIncorrectThreadPoolType(threadPoolName);
    ThreadPool.ThreadPoolType validThreadPoolType =
        ThreadPool.THREAD_POOL_TYPES.get(threadPoolName);
    ThreadPool threadPool = null;
    try {
      threadPool =
          new ThreadPool(
              Settings.builder()
                  .put("node.name", "testUpdateSettingsCanNotChangeThreadPoolType")
                  .build());
      ClusterSettings clusterSettings =
          new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
      threadPool.setClusterSettings(clusterSettings);

      clusterSettings.applySettings(
          Settings.builder()
              .put("threadpool." + threadPoolName + ".type", invalidThreadPoolType.getType())
              .build());
      fail("expected IllegalArgumentException");
    } catch (IllegalArgumentException e) {
      assertEquals(
          "illegal value can't update [threadpool.] from [{}] to [{"
              + threadPoolName
              + ".type="
              + invalidThreadPoolType.getType()
              + "}]",
          e.getMessage());
      assertThat(
          e.getCause().getMessage(),
          is(
              "setting threadpool."
                  + threadPoolName
                  + ".type to "
                  + invalidThreadPoolType.getType()
                  + " is not permitted; must be "
                  + validThreadPoolType.getType()));
    } finally {
      terminateThreadPoolIfNeeded(threadPool);
    }
  }
  public void testShutdownNowInterrupts() throws Exception {
    String threadPoolName = randomThreadPool(ThreadPool.ThreadPoolType.FIXED);
    ThreadPool threadPool = null;
    try {
      Settings nodeSettings =
          Settings.builder()
              .put("threadpool." + threadPoolName + ".queue_size", 1000)
              .put("node.name", "testShutdownNowInterrupts")
              .build();
      threadPool = new ThreadPool(nodeSettings);
      ClusterSettings clusterSettings =
          new ClusterSettings(nodeSettings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
      threadPool.setClusterSettings(clusterSettings);
      assertEquals(info(threadPool, threadPoolName).getQueueSize().getSingles(), 1000L);

      final CountDownLatch latch = new CountDownLatch(1);
      ThreadPoolExecutor oldExecutor = (ThreadPoolExecutor) threadPool.executor(threadPoolName);
      threadPool
          .executor(threadPoolName)
          .execute(
              () -> {
                try {
                  new CountDownLatch(1).await();
                } catch (InterruptedException ex) {
                  latch.countDown();
                  Thread.currentThread().interrupt();
                }
              });
      clusterSettings.applySettings(
          Settings.builder().put("threadpool." + threadPoolName + ".queue_size", 2000).build());
      assertThat(threadPool.executor(threadPoolName), not(sameInstance(oldExecutor)));
      assertThat(oldExecutor.isShutdown(), equalTo(true));
      assertThat(oldExecutor.isTerminating(), equalTo(true));
      assertThat(oldExecutor.isTerminated(), equalTo(false));
      threadPool.shutdownNow(); // should interrupt the thread
      latch.await(
          3, TimeUnit.SECONDS); // If this throws then ThreadPool#shutdownNow didn't interrupt
    } finally {
      terminateThreadPoolIfNeeded(threadPool);
    }
  }
  public void testCustomThreadPool() throws Exception {
    ThreadPool threadPool = null;
    try {
      Settings nodeSettings =
          Settings.builder()
              .put("threadpool.my_pool1.type", "scaling")
              .put("threadpool.my_pool1.min", 1)
              .put(
                  "threadpool.my_pool1.size", EsExecutors.boundedNumberOfProcessors(Settings.EMPTY))
              .put("threadpool.my_pool1.keep_alive", "1m")
              .put("threadpool.my_pool2.type", "fixed")
              .put("threadpool.my_pool2.size", "1")
              .put("threadpool.my_pool2.queue_size", "1")
              .put("node.name", "testCustomThreadPool")
              .build();
      threadPool = new ThreadPool(nodeSettings);
      ClusterSettings clusterSettings =
          new ClusterSettings(nodeSettings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
      threadPool.setClusterSettings(clusterSettings);
      ThreadPoolInfo groups = threadPool.info();
      boolean foundPool1 = false;
      boolean foundPool2 = false;
      outer:
      for (ThreadPool.Info info : groups) {
        if ("my_pool1".equals(info.getName())) {
          foundPool1 = true;
          assertEquals(info.getThreadPoolType(), ThreadPool.ThreadPoolType.SCALING);
        } else if ("my_pool2".equals(info.getName())) {
          foundPool2 = true;
          assertEquals(info.getThreadPoolType(), ThreadPool.ThreadPoolType.FIXED);
          assertThat(info.getMin(), equalTo(1));
          assertThat(info.getMax(), equalTo(1));
          assertThat(info.getQueueSize().singles(), equalTo(1L));
        } else {
          for (Field field : Names.class.getFields()) {
            if (info.getName().equalsIgnoreCase(field.getName())) {
              // This is ok it is a default thread pool
              continue outer;
            }
          }
          fail("Unexpected pool name: " + info.getName());
        }
      }
      assertThat(foundPool1, is(true));
      assertThat(foundPool2, is(true));

      // Updating my_pool2
      Settings settings = Settings.builder().put("threadpool.my_pool2.size", "10").build();
      clusterSettings.applySettings(settings);

      groups = threadPool.info();
      foundPool1 = false;
      foundPool2 = false;
      outer:
      for (ThreadPool.Info info : groups) {
        if ("my_pool1".equals(info.getName())) {
          foundPool1 = true;
          assertEquals(info.getThreadPoolType(), ThreadPool.ThreadPoolType.SCALING);
        } else if ("my_pool2".equals(info.getName())) {
          foundPool2 = true;
          assertThat(info.getMax(), equalTo(10));
          assertThat(info.getMin(), equalTo(10));
          assertThat(info.getQueueSize().singles(), equalTo(1L));
          assertEquals(info.getThreadPoolType(), ThreadPool.ThreadPoolType.FIXED);
        } else {
          for (Field field : Names.class.getFields()) {
            if (info.getName().equalsIgnoreCase(field.getName())) {
              // This is ok it is a default thread pool
              continue outer;
            }
          }
          fail("Unexpected pool name: " + info.getName());
        }
      }
      assertThat(foundPool1, is(true));
      assertThat(foundPool2, is(true));
    } finally {
      terminateThreadPoolIfNeeded(threadPool);
    }
  }
  public void testFixedExecutorType() throws InterruptedException {
    String threadPoolName = randomThreadPool(ThreadPool.ThreadPoolType.FIXED);
    ThreadPool threadPool = null;

    try {
      Settings nodeSettings = Settings.builder().put("node.name", "testFixedExecutorType").build();
      threadPool = new ThreadPool(nodeSettings);
      ClusterSettings clusterSettings =
          new ClusterSettings(nodeSettings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
      threadPool.setClusterSettings(clusterSettings);
      assertThat(threadPool.executor(threadPoolName), instanceOf(EsThreadPoolExecutor.class));
      Settings settings =
          clusterSettings.applySettings(
              Settings.builder().put("threadpool." + threadPoolName + ".size", "15").build());

      int expectedSize = getExpectedThreadPoolSize(nodeSettings, threadPoolName, 15);
      assertEquals(
          info(threadPool, threadPoolName).getThreadPoolType(), ThreadPool.ThreadPoolType.FIXED);
      assertThat(threadPool.executor(threadPoolName), instanceOf(EsThreadPoolExecutor.class));
      assertThat(
          ((EsThreadPoolExecutor) threadPool.executor(threadPoolName)).getCorePoolSize(),
          equalTo(expectedSize));
      assertThat(
          ((EsThreadPoolExecutor) threadPool.executor(threadPoolName)).getMaximumPoolSize(),
          equalTo(expectedSize));
      assertThat(info(threadPool, threadPoolName).getMin(), equalTo(expectedSize));
      assertThat(info(threadPool, threadPoolName).getMax(), equalTo(expectedSize));
      // keep alive does not apply to fixed thread pools
      assertThat(
          ((EsThreadPoolExecutor) threadPool.executor(threadPoolName))
              .getKeepAliveTime(TimeUnit.MINUTES),
          equalTo(0L));

      // Put old type back
      settings = clusterSettings.applySettings(Settings.EMPTY);
      assertEquals(
          info(threadPool, threadPoolName).getThreadPoolType(), ThreadPool.ThreadPoolType.FIXED);
      // Make sure keep alive value is not used
      assertThat(info(threadPool, threadPoolName).getKeepAlive(), nullValue());
      // Make sure keep pool size value were reused
      assertThat(info(threadPool, threadPoolName).getMin(), equalTo(expectedSize));
      assertThat(info(threadPool, threadPoolName).getMax(), equalTo(expectedSize));
      assertThat(threadPool.executor(threadPoolName), instanceOf(EsThreadPoolExecutor.class));
      assertThat(
          ((EsThreadPoolExecutor) threadPool.executor(threadPoolName)).getCorePoolSize(),
          equalTo(expectedSize));
      assertThat(
          ((EsThreadPoolExecutor) threadPool.executor(threadPoolName)).getMaximumPoolSize(),
          equalTo(expectedSize));

      // Change size
      Executor oldExecutor = threadPool.executor(threadPoolName);
      settings =
          clusterSettings.applySettings(
              Settings.builder()
                  .put(settings)
                  .put("threadpool." + threadPoolName + ".size", "10")
                  .build());

      expectedSize = getExpectedThreadPoolSize(nodeSettings, threadPoolName, 10);

      // Make sure size values changed
      assertThat(info(threadPool, threadPoolName).getMax(), equalTo(expectedSize));
      assertThat(info(threadPool, threadPoolName).getMin(), equalTo(expectedSize));
      assertThat(
          ((EsThreadPoolExecutor) threadPool.executor(threadPoolName)).getMaximumPoolSize(),
          equalTo(expectedSize));
      assertThat(
          ((EsThreadPoolExecutor) threadPool.executor(threadPoolName)).getCorePoolSize(),
          equalTo(expectedSize));
      // Make sure executor didn't change
      assertEquals(
          info(threadPool, threadPoolName).getThreadPoolType(), ThreadPool.ThreadPoolType.FIXED);
      assertThat(threadPool.executor(threadPoolName), sameInstance(oldExecutor));

      // Change queue capacity
      clusterSettings.applySettings(
          Settings.builder()
              .put(settings)
              .put("threadpool." + threadPoolName + ".queue", "500")
              .build());
    } finally {
      terminateThreadPoolIfNeeded(threadPool);
    }
  }