Scala 2.12
This release finally brings Scala 2.12 support (see #668, thanks @ilya-murzinov, @travisbrown, @clhodapp). As for 0.12, Finch is cross-published for both 2.11 and 2.12.
String-less Encoding with Circe/Jackson
Since 0.11, Finch parses JSON directly from bytes (not strings), which improved its throughput quite dramatically (see #671). This time we're moving towards "string-less encoding" (see #676) with Circe and Jackson by printing directly into a byte buffer and returning that as an HTTP payload (see #717 and #714, thanks @imliar, @travisbrown).
The benchmark numbers look inspiring and demonstrate quite well how the initial idea of hiding the serialization/deserialization logic behind functional abstractions is starting paying back. Being able to control this part of request lifecycle allows for optimizations specific to a concrete JSON library.
Overall, the numbers confirm that printing directly to bytes cuts allocations in half (and improves the throughput).
circe-core (string)
[info] Benchmark Mode Cnt Score Error Units
[info] ToServiceBenchmark.foos thrpt 20 463.981 ± 24.913 ops/s
[info] ToServiceBenchmark.foos:·gc.alloc.rate.norm thrpt 20 5002135.084 ± 3252.704 B/op
[info] ToServiceBenchmark.ints thrpt 20 2118.919 ± 49.846 ops/s
[info] ToServiceBenchmark.ints:·gc.alloc.rate.norm thrpt 20 873272.466 ± 106.930 B/op
circe-core (bytes)
[info] Benchmark Mode Cnt Score Error Units
[info] ToServiceBenchmark.foos thrpt 20 523.730 ± 21.871 ops/s
[info] ToServiceBenchmark.foos:·gc.alloc.rate.norm thrpt 20 2602833.269 ± 76.806 B/op
[info] ToServiceBenchmark.ints thrpt 20 2234.631 ± 111.156 ops/s
[info] ToServiceBenchmark.ints:·gc.alloc.rate.norm thrpt 20 545837.968 ± 43806.910 B/op
EndpointResult
Previously, Endpoint
result was modeled as Option[Tuple2[Input, Rerunnable[Output[_]]]]
indicating that result could be either skipped (i.e., None
) or matched and both remainder and the output is returned. This release introduces a new type EndpointResult
(see #707) that encodes this very behavior as an ADT with two cases Matched
and Skipped
.
A standalone abstraction not only simplifies the reasoning about endpoints but also improves their performance. Technically, EndpointResult
is a flattened version of Option[Tuple2[_, _]]
so instead of two nested objects we only need one to carry the result. This simple idea impacts nearly all endpoint operations slightly reducing allocations and improving throughput (up to 5% better on some benchmarks).
Here are the numbers from map*
variants (before and after):
[info] MapBenchmark.mapAsync avgt 6 429.113 ± 43.297 ns/op
[info] MapBenchmark.mapAsync:·gc.alloc.rate.norm avgt 6 776.000 ± 0.001 B/op
[info] MapBenchmark.mapAsync avgt 6 407.126 ± 12.807 ns/op
[info] MapBenchmark.mapAsync:·gc.alloc.rate.norm avgt 6 720.000 ± 0.001 B/op
[info] MapBenchmark.mapOutputAsync avgt 6 821.786 ± 52.045 ns/op
[info] MapBenchmark.mapOutputAsync:·gc.alloc.rate.norm avgt 6 1376.001 ± 0.001 B/op
[info] MapBenchmark.mapOutputAsync avgt 6 777.654 ± 26.444 ns/op
[info] MapBenchmark.mapOutputAsync:·gc.alloc.rate.norm avgt 6 1320.001 ± 0.001 B/op
Better Testing APIs
Methods for querying a rerunnable Output
returned from an Endpoint
have been renamed to be explicitly alerting about their blocking nature. Now they all prefixed with await
and take an optional Duration
indicating the upper bound of await time (previously 10 seconds).
Deprecation schema:
value
->awaitValueUnsafe()
tryValue
->awaitValue()
output
->awaitOutputUnsafe()
tryOutput
->awaitOutput()
Basic HTTP Auth
Finch's implementation of Basic HTTP Auth has been moved to its own project finagle-http-auth and promoted to Finagle filter so it could be used with bare metal finagle-http.
To enable the Basic HTTP Auth on Finch's endpoint, apply the BasicAuth.Server
filter to the service yield from toService
call.
scala> import com.twitter.finagle.http.BasicAuth, io.finch._
scala> val ba = BasicAuth.serverFromCredentials("admin", "12345")
ba: com.twitter.finagle.http.BasicAuth.Server = <function2>
scala> val s = ba.andThen(Endpoint.const("foo").toServiceAs[Text.Plain])
s: com.twitter.finagle.Service[Request, Response]
Other Changes
- New method
Endpoint.liftToTry
that works in a similar fashion toFuture.liftToTry
(see #710) - New method
Endpoint.productWith
that also accepts a function(A, B) => C
(see #692, thanks @imliar) - Jackson's
ObjectMapper
is now embedded inio.finch.jackson
. No need for an implicit instance anymore (see #714)
Dependencies
- Finagle 6.41
- Circe 0.7
- Cats 0.9
- Shapeless 2.3.2