twitter/util

Name: util

Owner: Twitter, Inc.

Description: Wonderful reusable code from Twitter

Created: 2011-03-26 01:23:21.0

Updated: 2018-01-17 17:05:21.0

Pushed: 2018-01-17 22:22:17.0

Homepage: http://twitter.github.com/util

Size: 16560

Language: Scala

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

Twitter Util

Build status Codecov Project status Gitter Maven Central

A bunch of idiomatic, small, general purpose tools.

See the Scaladoc here.

Status

This project is used in production at Twitter (and many other organizations), and is being actively developed and maintained.

Releases

Releases are done on an approximately monthly schedule. While semver is not followed, the changelogs are detailed and include sections on public API breaks and changes in runtime behavior.

Contributing

The master branch of this repository contains the latest stable release of Util, and weekly snapshots are published to the develop branch. In general pull requests should be submitted against develop. See CONTRIBUTING.md for more details about how to contribute.

Using in your project

An example SBT dependency string for the util-collection tools would look like this:

collUtils = "com.twitter" %% "util-collection" % "18.2.0"

Units

Time
rt com.twitter.conversions.time._

duration1 = 1.second
duration2 = 2.minutes
tion1.inMillis // => 1000L
Space
rt com.twitter.conversions.storage._
amount = 8.megabytes
nt.inBytes // => 8388608L
nt.inKilobytes // => 8192L

Futures

A Non-actor re-implementation of Scala Futures.

rt com.twitter.conversions.time._
rt com.twitter.util.{Await, Future, Promise}

f = new Promise[Int]
g = f.map { result => result + 1 }
tValue(1)
t.result(g, 1.second) // => this blocks for the futures result (and eventually returns 2)

nother option:
Success { result =>
intln(result) // => prints "2"


sing for expressions:
xFuture = Future(1)
yFuture = Future(2)

{
<- xFuture
<- yFuture

intln(x + y) // => prints "3"

Collections

LruMap

The LruMap is an LRU with a maximum size passed in. If the map is full it expires items in FIFO order. Reading a value will move an item to the top of the stack.

rt com.twitter.util.LruMap

map = new LruMap[String, String](15) // this is of type mutable.Map[String, String]

Object Pool

The pool order is FIFO.

A pool of constants
rt scala.collection.mutable
rt com.twitter.util.{Await, SimplePool}

queue = new mutable.Queue[Int] ++ List(1, 2, 3)
pool = new SimplePool(queue)

ote that the pool returns Futures, it doesn't block on exhaustion.
rt(Await.result(pool.reserve()) == 1)
.reserve().onSuccess { item =>
intln(item) // prints "2"

A pool of dynamically created objects

Here is a pool of even-number generators. It stores 4 numbers at a time:

rt com.twitter.util.{Future, FactoryPool}

pool = new FactoryPool[Int](4) {
r count = 0
f makeItem() = { count += 1; Future(count) }
f isHealthy(i: Int) = i % 2 == 0

It checks the health when you successfully reserve an object (i.e., when the Future yields).

Hashing

util-hashing is a collection of hash functions and hashing distributors (eg. ketama).

To use one of the available hash functions:

rt com.twitter.hashing.KeyHasher

asher.FNV1_32.hashKey("string".getBytes)

Available hash functions are:

_32
A_32
_64
A_64
MA
2_ITU
H

To use KetamaDistributor:

rt com.twitter.hashing.{KetamaDistributor, KetamaNode, KeyHasher}

nodes = List(KetamaNode("host:port", 1 /* weight */, "foo" /* handle */))
distributor = new KetamaDistributor(nodes, 1 /* num reps */)
ributor.nodeForHash("abc".##) // => client

Logging

util-logging is a small wrapper around Java's built-in logging to make it more Scala-friendly.

Using

To access logging, you can usually just use:

rt com.twitter.logging.Logger
ate val log = Logger.get(getClass)

This creates a Logger object that uses the current class or object's package name as the logging node, so class “com.example.foo.Lamp” will log to node com.example.foo (generally showing “foo” as the name in the logfile). You can also get a logger explicitly by name:

ate val log = Logger.get("com.example.foo")

Logger objects wrap everything useful from java.util.logging.Logger, as well as adding some convenience methods:

og a string with sprintf conversion:
info("Starting compaction on zone %d...", zoneId)

{
.
tch {
 log an exception backtrace with the message:
se e: IOException =>
log.error(e, "I/O exception: %s", e.getMessage)

Each of the log levels (from “fatal” to “trace”) has these two convenience methods. You may also use log directly:

rt com.twitter.logging.Level
Level.DEBUG, "Logging %s at debug level.", name)

An advantage to using sprintf (“%s”, etc) conversion, as opposed to:

Level.DEBUG, s"Logging $name at debug level.")

is that Java & Scala perform string concatenation at runtime, even if nothing will be logged because the log file isn't writing debug messages right now. With sprintf parameters, the arguments are just bundled up and passed directly to the logging level before formatting. If no log message would be written to any file or device, then no formatting is done and the arguments are thrown away. That makes it very inexpensive to include verbose debug logging which can be turned off without recompiling and re-deploying.

If you prefer, there are also variants that take lazily evaluated parameters, and only evaluate them if logging is active at that level:

ifDebug(s"Login from $name at $date.")

The logging classes are done as an extension to the java.util.logging API, and so you can use the Java interface directly, if you want to. Each of the Java classes (Logger, Handler, Formatter) is just wrapped by a Scala class.

Configuring

In the Java style, log nodes are in a tree, with the root node being “” (the empty string). If a node has a filter level set, only log messages of that priority or higher are passed up to the parent. Handlers are attached to nodes for sending log messages to files or logging services, and may have formatters attached to them.

Logging levels are, in priority order of highest to lowest:

Each node may also optionally choose to not pass messages up to the parent node.

The LoggerFactory builder is used to configure individual log nodes, by filling in fields and calling the apply method. For example, to configure the root logger to filter at INFO level and write to a file:

rt com.twitter.logging._

factory = LoggerFactory(
de = "",
vel = Some(Level.INFO),
ndlers = List(
FileHandler(
  filename = "/var/log/example/example.log",
  rollPolicy = Policy.SigHup
)



logger = factory()

As many LoggerFactorys can be configured as you want, so you can attach to several nodes if you like. To remove all previous configurations, use:

er.clearHandlers()
Handlers
Formatters

Handlers usually have a formatter attached to them, and these formatters generally just add a prefix containing the date, log level, and logger name.

Future interrupts

Method raise on Future (def raise(cause: Throwable)) raises the interrupt described by cause to the producer of this Future. Interrupt handlers are installed on a Promise using setInterruptHandler, which takes a partial function:

p = new Promise[T]
tInterruptHandler {
se exc: MyException =>
// deal with interrupt..

Interrupts differ in semantics from cancellation in important ways: there can only be one interrupt handler per promise, and interrupts are only delivered if the promise is not yet complete.

Time and Duration

Like arithmetic on doubles, Time and Duration arithmetic is now free of overflows. Instead, they overflow to Top and Bottom values, which are analogous to positive and negative infinity.

Since the resolution of Time.now has been reduced (and is also more expensive due to its use of system time), a new Stopwatch API has been introduced in order to calculate durations of time.

It's used simply:

rt com.twitter.util.{Duration, Stopwatch}
elapsed: () => Duration = Stopwatch.start()

which is read by applying elapsed:

duration: Duration = elapsed()

This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.