@Override
 String formatInfo(ThreadPool.Info info) {
   return String.format(
       Locale.ROOT,
       "name [%s], core [%d], max [%d], keep alive [%s]",
       info.getName(),
       info.getMin(),
       info.getMax(),
       info.getKeepAlive());
 }
 public void testCorrectThreadPoolTypePermittedInSettings() throws InterruptedException {
   String threadPoolName = randomThreadPoolName();
   ThreadPool.ThreadPoolType correctThreadPoolType =
       ThreadPool.THREAD_POOL_TYPES.get(threadPoolName);
   ThreadPool threadPool = null;
   try {
     threadPool =
         new ThreadPool(
             Settings.builder()
                 .put("node.name", "testCorrectThreadPoolTypePermittedInSettings")
                 .put("threadpool." + threadPoolName + ".type", correctThreadPoolType.getType())
                 .build());
     ThreadPool.Info info = info(threadPool, threadPoolName);
     if (ThreadPool.Names.SAME.equals(threadPoolName)) {
       assertNull(info); // we don't report on the "same" threadpool
     } else {
       // otherwise check we have the expected type
       assertEquals(info.getThreadPoolType(), correctThreadPoolType);
     }
   } 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);
    }
  }
  private Table buildTable(
      RestRequest req,
      ClusterStateResponse state,
      NodesInfoResponse nodesInfo,
      NodesStatsResponse nodesStats) {
    final String[] threadPools = req.paramAsStringArray("thread_pool_patterns", new String[] {"*"});
    final DiscoveryNodes nodes = state.getState().nodes();
    final Table table = getTableWithHeader(req);

    // collect all thread pool names that we see across the nodes
    final Set<String> candidates = new HashSet<>();
    for (final NodeStats nodeStats : nodesStats.getNodes()) {
      for (final ThreadPoolStats.Stats threadPoolStats : nodeStats.getThreadPool()) {
        candidates.add(threadPoolStats.getName());
      }
    }

    // collect all thread pool names that match the specified thread pool patterns
    final Set<String> included = new HashSet<>();
    for (final String candidate : candidates) {
      if (Regex.simpleMatch(threadPools, candidate)) {
        included.add(candidate);
      }
    }

    for (final DiscoveryNode node : nodes) {
      final NodeInfo info = nodesInfo.getNodesMap().get(node.getId());
      final NodeStats stats = nodesStats.getNodesMap().get(node.getId());

      final Map<String, ThreadPoolStats.Stats> poolThreadStats;
      final Map<String, ThreadPool.Info> poolThreadInfo;

      if (stats == null) {
        poolThreadStats = Collections.emptyMap();
        poolThreadInfo = Collections.emptyMap();
      } else {
        // we use a sorted map to ensure that thread pools are sorted by name
        poolThreadStats = new TreeMap<>();
        poolThreadInfo = new HashMap<>();

        ThreadPoolStats threadPoolStats = stats.getThreadPool();
        for (ThreadPoolStats.Stats threadPoolStat : threadPoolStats) {
          poolThreadStats.put(threadPoolStat.getName(), threadPoolStat);
        }
        if (info != null) {
          for (ThreadPool.Info threadPoolInfo : info.getThreadPool()) {
            poolThreadInfo.put(threadPoolInfo.getName(), threadPoolInfo);
          }
        }
      }
      for (Map.Entry<String, ThreadPoolStats.Stats> entry : poolThreadStats.entrySet()) {

        if (!included.contains(entry.getKey())) continue;

        table.startRow();

        table.addCell(node.getName());
        table.addCell(node.getId());
        table.addCell(node.getEphemeralId());
        table.addCell(info == null ? null : info.getProcess().getId());
        table.addCell(node.getHostName());
        table.addCell(node.getHostAddress());
        table.addCell(node.getAddress().address().getPort());
        final ThreadPoolStats.Stats poolStats = entry.getValue();
        final ThreadPool.Info poolInfo = poolThreadInfo.get(entry.getKey());

        Long maxQueueSize = null;
        String keepAlive = null;
        Integer minThreads = null;
        Integer maxThreads = null;

        if (poolInfo != null) {
          if (poolInfo.getQueueSize() != null) {
            maxQueueSize = poolInfo.getQueueSize().singles();
          }
          if (poolInfo.getKeepAlive() != null) {
            keepAlive = poolInfo.getKeepAlive().toString();
          }
          if (poolInfo.getMin() >= 0) {
            minThreads = poolInfo.getMin();
          }
          if (poolInfo.getMax() >= 0) {
            maxThreads = poolInfo.getMax();
          }
        }

        table.addCell(entry.getKey());
        table.addCell(poolInfo == null ? null : poolInfo.getThreadPoolType().getType());
        table.addCell(poolStats == null ? null : poolStats.getActive());
        table.addCell(poolStats == null ? null : poolStats.getThreads());
        table.addCell(poolStats == null ? null : poolStats.getQueue());
        table.addCell(maxQueueSize);
        table.addCell(poolStats == null ? null : poolStats.getRejected());
        table.addCell(poolStats == null ? null : poolStats.getLargest());
        table.addCell(poolStats == null ? null : poolStats.getCompleted());
        table.addCell(minThreads);
        table.addCell(maxThreads);
        table.addCell(keepAlive);

        table.endRow();
      }
    }

    return table;
  }
  @Test(timeout = 20000)
  public void testUpdatingThreadPoolSettings() throws Exception {
    internalCluster().startNodesAsync(2).get();
    ThreadPool threadPool = internalCluster().getDataNodeInstance(ThreadPool.class);
    // Check that settings are changed
    assertThat(
        ((ThreadPoolExecutor) threadPool.executor(Names.SEARCH)).getKeepAliveTime(TimeUnit.MINUTES),
        equalTo(5L));
    client()
        .admin()
        .cluster()
        .prepareUpdateSettings()
        .setTransientSettings(settingsBuilder().put("threadpool.search.keep_alive", "10m").build())
        .execute()
        .actionGet();
    assertThat(
        ((ThreadPoolExecutor) threadPool.executor(Names.SEARCH)).getKeepAliveTime(TimeUnit.MINUTES),
        equalTo(10L));

    // Make sure that threads continue executing when executor is replaced
    final CyclicBarrier barrier = new CyclicBarrier(2);
    Executor oldExecutor = threadPool.executor(Names.SEARCH);
    threadPool
        .executor(Names.SEARCH)
        .execute(
            new Runnable() {
              @Override
              public void run() {
                try {
                  barrier.await();
                } catch (InterruptedException ex) {
                  Thread.currentThread().interrupt();
                } catch (BrokenBarrierException ex) {
                  //
                }
              }
            });
    client()
        .admin()
        .cluster()
        .prepareUpdateSettings()
        .setTransientSettings(settingsBuilder().put("threadpool.search.type", "fixed").build())
        .execute()
        .actionGet();
    assertThat(threadPool.executor(Names.SEARCH), not(sameInstance(oldExecutor)));
    assertThat(((ThreadPoolExecutor) oldExecutor).isShutdown(), equalTo(true));
    assertThat(((ThreadPoolExecutor) oldExecutor).isTerminating(), equalTo(true));
    assertThat(((ThreadPoolExecutor) oldExecutor).isTerminated(), equalTo(false));
    barrier.await();

    // Make sure that new thread executor is functional
    threadPool
        .executor(Names.SEARCH)
        .execute(
            new Runnable() {
              @Override
              public void run() {
                try {
                  barrier.await();
                } catch (InterruptedException ex) {
                  Thread.currentThread().interrupt();
                } catch (BrokenBarrierException ex) {
                  //
                }
              }
            });
    client()
        .admin()
        .cluster()
        .prepareUpdateSettings()
        .setTransientSettings(settingsBuilder().put("threadpool.search.type", "fixed").build())
        .execute()
        .actionGet();
    barrier.await();
    Thread.sleep(200);

    // Check that node info is correct
    NodesInfoResponse nodesInfoResponse =
        client().admin().cluster().prepareNodesInfo().all().execute().actionGet();
    for (int i = 0; i < 2; i++) {
      NodeInfo nodeInfo = nodesInfoResponse.getNodes()[i];
      boolean found = false;
      for (ThreadPool.Info info : nodeInfo.getThreadPool()) {
        if (info.getName().equals(Names.SEARCH)) {
          assertThat(info.getType(), equalTo("fixed"));
          found = true;
          break;
        }
      }
      assertThat(found, equalTo(true));

      Map<String, Object> poolMap =
          getPoolSettingsThroughJson(nodeInfo.getThreadPool(), Names.SEARCH);
    }
  }