• Stars
    star
    204
  • Rank 191,599 (Top 4 %)
  • Language
    Java
  • Created about 10 years ago
  • Updated 11 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

centrally-planned object and thread pools

Clojars Project CircleCI

(pronounced deer-eh-jeest)

In the default JVM thread pools, once a thread is created it will only be retired when it hasn't performed a task in the last minute. In practice, this means that there are as many threads as the peak historical number of concurrent tasks handled by the pool, forever. These thread pools are also poorly instrumented, making it difficult to tune their latency or throughput.

Dirigiste provides a fast, richly instrumented version of a java.util.concurrent.ExecutorService, and provides a means to feed that instrumentation into a control mechanism that can grow or shrink the pool as needed. Default implementations that optimize the pool size for thread utilization are provided.

It also provides an object pool mechanism that uses a similar feedback mechanism to resize itself, and is significantly simpler than the Apache Commons object pool implementation.

Javadocs can be found here.

usage

In Leiningen:

[org.clj-commons/dirigiste "1.0.3"]

In Maven:

<dependency>
  <groupId>io.aleph</groupId>
  <artifactId>dirigiste</artifactId>
  <version>0.1.5</version>
</dependency>

executors

Using the default utilization executor is simple, via Executors.utilizationExecutor(...):

import io.aleph.dirigiste.Executors;

...

ExecutorService e = Executors.utilizationExecutor(0.9, 64);

This will create an executor which will try to size the pool such that 90% of the threads are active, but will not grow beyond 64 threads.

This executor exposes getStats and getLastStats methods, which can be used to examine the performance characteristics of the executor. getLastStats uses the last value passed to the control loop, so can return immediately. getStats returns the statistics gathered since the last control update, and so may contain 0 or more samples, and requires some amount of computation.

Since instrumentation will cause some small overhead, you may specify which dimensions you wish to collect, via the Metric class. The possible fields are as follows:

metric description
QUEUE_LATENCY the time spent on the queue for each task, in nanoseconds
TASK_LATENCY the time taken to complete a task, including time spent on the queue, in nanoseconds
QUEUE_LENGTH the length of the queue
TASK_ARRIVAL_RATE the rate of incoming tasks per second
TASK_COMPLETION_RATE the rate of completed tasks per second
TASK_REJECTION_RATE the rate of rejected tasks per second
UTILIZATION the portion of threads which are active, from 0 to 1

These metrics are surfaced via the Stats class, which provides getMean...() and get...(double quantile) for each metric. By default, the utilization executor will only measure utilization, but if we want to get the full range of metrics, we can instantiate it like this:

Executors.utilizationExecutor(0.9, 64, EnumSet.allOf(Executor.Metric));

This will allow us to track metrics which aren't required for the control loop, but are useful elsewhere.

pools

All pools are defined via their generator, which is used to create and destroy the pooled objects:

public interface Pool.Generator<K,V> {
  V generate(K key) throws Exception;
  void destroy(K key, V val);
}

All pooled objects have an associated key. If objects have no external resources that must be explicitly disposed, destroy can be a no-op.

Object pools have three major functions, acquire, release, and dispose. Typically, objects will be taken out of the pool via acquire, and returned back via release once they've served their purpose:

pool = Pools.utilizationPool(generator, 0.9, 4, 1024);
Object obj = pool.acquire("foo");
useObject(obj);
pool.release("foo", obj);

However, if the object has expired, we can dispose of it:

pool.dispose("foo", obj);

A pooled object can be disposed of at any time, without having first been acquired.

To support non-blocking code, we may also acquire an object via a callback mechanism:

pool.acquire("foo",
  new AcquireCallback() {
        public void handleObject(Object obj) {
                useObject(obj);
                pool.release("foo", obj);
        }
});

creating a custom controller

The Executor.Controller interface is fairly straightforward:

public interface Executor.Controller {
    boolean shouldIncrement(int currThreads);
    int adjustment(Stats stats);
}

The first method, shouldIncrement, controls whether a new thread should be spun up. This means that the thread limit can be dynamic, for instance dependent on the available memory. This method will be called whenever adjustment calls for more threads, or when a task is unable to be added to the queue.

The second method, adjustment, takes a Stats object, and returns a number representing how the pool size should be adjusted. The frequency with which adjustment is called is dictated by the controlPeriod parameter to the Executor constructor, and the number of samples in the Stats object is controlled by the samplePeriod parameter.

The utilization controller is quite simple:

Executor.Controller utilizationController(final double targetUtilization, final int maxThreadCount) {
  return new Controller() {
    public boolean shouldIncrement(int numWorkers) {
      return numWorkers < maxThreadCount;
    }

    public int adjustment(Stats stats) {
      double correction = stats.getUtilization(0.9) / targetUtilization;
      return (int) Math.ceil(stats.getNumWorkers() * correction) - stats.getNumWorkers();
    }
  };
}

It adjusts the number of threads using the targetUtilization compared against the 90th percentile measured utilization over the last controlPeriod. Obviously more sophisticated methods are possible, but they're left as an exercise for the reader.

Pool.Controller works much the same, except that adjustment takes a Map of keys onto Stats objects, and returns a Map of keys onto Integer objects. The utilization controller is otherwise much the same:

public Pool.Controller utilizationController(final double targetUtilization, final int maxObjectsPerKey, final int maxTotalObjects) {

  return new Pool.Controller() {
    public boolean shouldIncrement(Objec t key, int objectsForKey, int totalObjects) {
      return (objectsForKey < maxObjectsPerKey) && (totalObjects < maxTotalObjects);
    }

    public Map adjustment(Map<K, Stats> stats) {
      Map adj = new HashMap();

      for ( e : stats.entrySet()) {
        Map.Entry entry = (Map.Entry) e;
        Stats s = (Stats) entry.getValue();
        int numWorkers = s.getNumWorkers();
        double correction = s.getUtilization(0.9) / targetUtilization;
        int n = (int) Math.ceil(s.getNumWorkers() * correction) - numWorkers;

        adj.put(entry.getKey(), new Integer(n));
      }

      return adj;
    }
  };
}

license

Copyright ยฉ 2015 Zachary Tellman

Distributed under the MIT License

More Repositories

1

aleph

Asynchronous streaming communication for Clojure - web server, web client, and raw TCP/UDP
Clojure
2,544
star
2

kibit

There's a function for that!
Clojure
1,765
star
3

seesaw

Seesaw turns the Horror of Swing into a friendly, well-documented, Clojure library
Clojure
1,445
star
4

manifold

A compatibility layer for event-driven abstractions
Clojure
1,017
star
5

etaoin

Pure Clojure Webdriver protocol implementation
Clojure
913
star
6

marginalia

Ultra-lightweight literate programming for clojure inspired by docco
Clojure
814
star
7

secretary

A client-side router for ClojureScript.
Clojure
773
star
8

hickory

HTML as data
Clojure
630
star
9

claypoole

Claypoole: Threadpool tools for Clojure
Clojure
607
star
10

pretty

Library for helping print things prettily, in Clojure - ANSI fonts, formatted exceptions
Clojure
597
star
11

rewrite-clj

Rewrite Clojure code and edn
Clojure
576
star
12

potemkin

some ideas which are almost good
Clojure
568
star
13

pomegranate

A sane Clojure API for Maven Artifact Resolver + dynamic runtime modification of the classpath
Clojure
504
star
14

gloss

speaks in bytes, so you don't have to
Clojure
483
star
15

camel-snake-kebab

A Clojure[Script] library for word case conversions
Clojure
476
star
16

cljss

Clojure Style Sheets โ€” CSS-in-JS for ClojureScript
Clojure
454
star
17

clooj

clooj, a lightweight IDE for clojure
Clojure
418
star
18

byte-streams

A Rosetta stone for JVM byte representations
Clojure
417
star
19

durable-queue

a disk-backed queue for clojure
Clojure
381
star
20

useful

Some Clojure functions we use all the time, and so can you.
Clojure
365
star
21

metrics-clojure

A thin faรงade around Coda Hale's metrics library.
Clojure
343
star
22

virgil

Recompile Java code without restarting the REPL
Clojure
302
star
23

citrus

State management library for Rum
Clojure
274
star
24

ordered

Ordered sets and maps, implemented in pure clojure
Clojure
253
star
25

clj-ssh

SSH commands via jsch
Clojure
227
star
26

iapetos

A Clojure Prometheus Client
Clojure
176
star
27

primitive-math

for the discerning arithmetician
Clojure
170
star
28

humanize

Produce human readable strings in clojure
Clojure
156
star
29

digest

Digest algorithms (md5, sha1 ...) for Clojure
Clojure
156
star
30

clj-yaml

YAML encoding and decoding for Clojure
Clojure
120
star
31

byte-transforms

methods for hashing, compressing, and encoding bytes
Clojure
104
star
32

ring-buffer

A persistent ring-buffer in Clojure
Clojure
96
star
33

tentacles

An Octocat is nothing without his tentacles
Clojure
80
star
34

fs

File system utilities for Clojure. (forked from Raynes/fs)
Clojure
72
star
35

lein-marginalia

A Marginalia plugin to Leiningen
HTML
68
star
36

meta

A meta-repo for clj-commons discussions
46
star
37

ring-gzip-middleware

GZIP your Ring responses
Clojure
41
star
38

rewrite-cljs

Traverse and rewrite Clojure/ClojureScript/EDN from ClojureScript
Clojure
41
star
39

formatter

Building blocks and discussion for building a common Clojure code formatter
36
star
40

vizdeps

Visualize Leiningen dependencies using Graphviz
Clojure
33
star
41

zprint-clj

Node.js wrapper for ZPrint Clojure source code formatter
Clojure
13
star
42

infra

Infrastructure for clj-commons
Clojure
2
star
43

clj-commons.github.io

Clojure Commons Site
HTML
1
star