Skip to content

0.42.0

Compare
Choose a tag to compare
@bondolo bondolo released this 20 Dec 23:21
· 1165 commits to main since this release

Overview

This is a major ServiceTalk feature release appropriate for all users containing numerous new features and many improvements. We are actively working towards ServiceTalk 1.0 and this intermediate release helps us to refine and stabilize the API. Existing ServiceTalk applications will need to be recompiled to use the 0.42 release and will likely require some code changes or migration to the new API. Also, make sure all other libraries or extensions that depends on the ServiceTalk API in your classpath are also recompiled with 0.42.

Changes

The 0.42.0 release includes significant new features and API changes but incorporates as closely as possible the same dependencies and bug fixes as the prior 0.41.12 release.

Offloading Changes

In addition to the API changes, ServiceTalk 0.42 includes some significant behavioral differences that may impact existing applications. Offloading, the execution of user code on non-I/O threads, is one area that has changed significantly. In ServiceTalk 0.41 and earlier the strategy used for offloading was applied more generally than in 0.42. This meant that most user code, even code which did not expect to be offloaded, was offloaded from I/O threads. This approach had the advantages of consistency and safety; the execution environment for user code was predictable and the Netty I/O threads were protected from being unavailable while executing application code.
In ServiceTalk 0.42 the offloading strategy is more focused and offloading is done in fewer situations, primarily only those situations which explicitly require offloading. Offloading is an expensive activity. If the application or ServiceTalk logic which needs to be executed is insignificant or carefully bounded and never blocks then avoiding offloading has a positive impact on both latency and throughput. Offloading is controlled by the use of the publishOn and subscribeOn offloading operators and for filters and services by implementing the StrategyInfluencer interface.

Executors and other operators

In prior versions of ServiceTalk all operators could access the most recently referenced Executor in the operator pipeline. In 0.42 the intended executor for executing the operations pipeline is only exposed to offloading operators. Other operators are effectively “immediate” meaning that they expect to run on whatever thread is active when they are invoked, the timeout and liftAsync operators are affected.

Retrying / Auto-Retrying filters

RetryingHttpRequesterFilter & Auto-Retrying filters/API were unified in a new RetryingHttpRequesterFilter now available under io.servicetalk.http.netty package. The new filter offers support for handling lower level transport errors and retrying them (ie. what Auto-Retry was offering before) as well as allowing users to define their own retry rules per request/response basis.

RetryingHttpRequesterFilter offers fine grain control over retry behavior, here are some highlights:

  • retryRetryableExceptions
    Supports retry behavior for RetryableException which are transport layer errors that occur inside ST and signal erroneous behavior before a request touches the “wire”. By default this is enabled and behaves the same way Auto-Retry was before.
  • retryIdempotentRequests
    Supports retry behavior for requests that are marked as idempotent when an I/O exception occurs.
  • retryDelayedRetries
    Supports retry behavior for errors that are type of DelayedRetry a marker interface that holds additional backoff time info, to be evaluated when the total back-off time is computed. One possible situation that this can be useful is if users want to translate a Retry-After header in a response, and allow the retry-filter to wait for that amount of time (ie. retry-after period) before retrying.
  • responseMapper & retryResponses
    Supports mapping any response to an HttpResponseException giving the ability to users to retry particular responses according to their own criteria. First you map the response to an error with responseMapper then you define the retry behavior through retryResponses.

All above flavors offer fine control over the retry behavior of each retry criterion, e.g retry up-to 5 times retryable exceptions, and up-to 2 times delayed retries with max-delay of 2 seconds. On top of that, users can define a max-total retry count, that can be used to exit the retry logic if the cumulative amount of retries (of all retry configurations) reaches that count.

The RetryingHttpRequesterFilter is by default enabled and added as the last filter in the filter chain, similarly to how auto-retry behaved before, only retrying RetryableExceptions, however if users define their own version of that and apply it in a particular placement in their filter chain, then this order/configuration takes precedence over the default behavior.

This work was ignited as an effort to minimize the places users have to define retry logic, and avoid situations where these placements conflict with each other resulting in retry logic that wasn’t expected.

Serialization

Interfaces from the io.servicetalk.serialization.api package have been deprecated (SerializationProvider, ..). The deprecated interfaces have been replaced with new interfaces in io.servicetalk.serializer.api. The new APIs apply lessons learned from usage of deprecated APIs and here are some highlights:

  • Composition between scalar serializers in streaming use cases. Streaming serializers provide reusable framing (Length Delimited VarInt/Fixed, gRPC) across different serialization technologies (Protobuf, thrift, ..).
  • Encapsulation of serialization allows exposing streaming building blocks while hiding custom framing (gRPC SerializerDescriptor).
  • Streaming APIs leverage Reactive Streams lifecycle and primitives instead of custom APIs and lifecycle.
  • Support for JDK primitive types such as byte[] and String. See how these utilities are composed/leveraged in HttpSerializers.

Checkout the http serialization examples to see the new APIs in action. Javadocs on each deprecated type include @deprecated notices referencing the replacement classes, and here is a summary of commonly used types:

Builders

General

  • All builders (client & server, HTTP & gRPC) have been changed from abstract classes to interfaces to allows users wrap them to enhance/modify behavior. Method names didn’t change. HTTP users should not notice this change as soon as they recompile with 0.42, gRPC users may need to migrate their HTTP-specific configuration to HttpInitializer API (see #1867 and #1861). Resolving all deprecations highlighted building for 0.41.12 will make this transition smooth when upgrading to 0.42.
  • All one-way builder methods, like HttpServerBuilder.disableDrainingRequestPayloadBody(), have been changed to take a boolean argument to allow users enable/disable a feature it controls. Migrate from all deprecated builder methods in 0.41.12 or see #1750, #1823, and #1825 for the full list of methods.

Server builders

  • All listen* methods return a protocol-specific context now: Single<HttpServerContext> for HTTP and Single<GrpcServerContext> for gRPC. Previously, these methods returned Single<ServerContext>.

MultiAddressHttpClientBuilder & PartitionedHttpClientBuilder

  • Both builders now use SingleAddressInitializer abstraction to give access to the underlying SingleAddressHttpClientBuilder(s)

MultiAddressHttpClientBuilder

  • maxRedirects(int)followRedirects(RedirectConfig)
  • redirects are disable by default now, use followRedirects(RedirectConfig) to enable them

Filters API

  • 0.41.12 simplified client-side filters API to remove HttpExecutionStrategy from the arguments, see #1956.

Users should update their filters to override one or more of the request or reserveConnection methods that does not take HttpExecutionStrategy as an argument. If modifications for the HttpExecutionStrategy are required, use HttpContextKeys#HTTP_EXECUTION_STRATEGY_KEY. See changes for HostHeaderHttpRequesterFilter as an example of how to migrate a filter. After this migration is done with 0.41.12 version, 0.42 won’t cause any issues.

Note: All StreamingHttpClientFilter implementations should use delegate.request(...) instead of invoking super.request(delegate, ...). Invoking a super class implementation may lead to unexpected behavior change with new releases.

  • append method have been removed from all filter factories: ConnectionFactoryFilter, GrpcServiceFilterFactory, MultiAddressHttpClientFilterFactory, StreamingHttpClientFilterFactory, StreamingHttpConnectionFilterFactory, StreamingHttpServiceFilterFactory because the resulting factories did not allow correct computation of the combined execution strategy. Consider using appropriate append* methods on the client/server builder intead.
  • MultiAddressHttpClientFilterFactory have been removed, migrate to MultiAddressHttpClientBuilder#SingleAddressInitializer and use SingleAddressHttpClientBuilder#appendClientFilter(...).
  • GrpcClientBuilder#MultiClientBuilder and GrpcClientBuilder#buildMulti have been removed, use GrpcClientFactory#newClient(GrpcClientCallFactory) if necessary.

ServiceDiscoverer API

A new ServiceDiscovererEvent.Status type was introduced to let SD use a non-binary state of the address. ServiceDiscovererEvent#isAvailable() is replaced with ServiceDiscovererEvent#status().

DefaultDnsServiceDiscoverer is using ServiceDiscovererEvent.Status.EXPIRED by default.

ServiceDiscoveryRetryStrategy and DefaultServiceDiscoveryRetryStrategy have been deprecated in 0.41.12 and removed in 0.42 in favor of a standard BiIntFunction<Throwable, ? extends Completable> API of RetryStrategies.

LoadBalancer API

  • LoadBalancerFactory#newLoadBalancer :

    • an extra String argument was added that describes a target resource for connections. It helps with debuggability.
    • eventStream publisher was changed from Publisher<? extends ServiceDiscovererEvent> to Publisher<? extends Collection<? extends ServiceDiscovererEvent>> to align it with ServiceDiscoverer API.
  • RoundRobinLoadBalancer.RoundRobinLoadBalancerFactory become a top level class with the builder.

  • RoundRobinLoadBalancer changed its visibility from public to pkg-private, RoundRobinLoadBalancerFactory should be used instead.

  • RoundRobinLoadBalancer.RoundRobinLoadBalancerFactory.Builder#eagerConnectionShutdown(boolean) was removed because ServiceDiscovererEvent.Status can provide all necessary information. The same behavior can be achieved by configuring DefaultDnsServiceDiscovererBuilder#missingRecordStatus(ServiceDiscovererEvent.Status.UNAVAILABE).

If you have a custom implementation of the LoadBalancer interface, consider supporting all new statuses from the ServiceDiscoverer: AVAILABLE, UNAVAILABLE, EXPIRED.

HTTP API

  • Parent interface of StatelessTrailersTransformer have been changed from TrailersTransformer<Object, Payload> to TrailersTransformer<Void, Payload>.
  • BlockingStreamingHttpServerResponse was converted from an abstract class to an interface.
  • HttpClients.*viaProxy(...) methods have been removed in favor of SingleAddressHttpClientBuilder#proxyAddress(...) configuration option after using a regular HttpClients.forSingleAddress(...) factory.

Exceptions

Removed exception types that were public but never thrown by ServiceTalk (see #1920):

  • ConnectionClosedException
  • InvalidRedirectException
  • MaxRequestLimitExceededException
  • MaxRequestLimitExceededRejectedSubscribeException

Some exception types were changed to use more appropriate parent type:

  • ConnectionRejectedException extends RuntimeExceptionSocketException;
  • NoAvailableHostException extends RuntimeExceptionIOException;
  • DuplicateAttributeException extends RuntimeExceptionIllegalStateException;
  • TerminateRepeatException extends ExceptionRuntimeException;
  • ClosedServiceDiscovererException extends RuntimeExceptionClosedChannelException;

Observability

TransportObserver - gives low level visibility into L4 network level

Some existing callbacks were improved to take extra arguments that help to increase visibility:

  • TransportObserver#onNewConnection()TransportObserver#onNewConnection(Object, Object) - tells which local/remote address is used for a new connection. Previously, it was impossible to know what was the remote peer if a connect attempt fails.
  • ReadObserver#itemRead()ReadObserver#itemRead(Object) - tells which object have been read from the network.
  • WriteObserver#itemReceived()WriteObserver#itemReceived(Object) - tells which object have been received for a write operation.
  • WriteObserver#itemWritten()WriteObserver#itemWritten(Object) - tells which object have been serialized and written to the OS network buffer.

New callbacks that have to be implemented:

  • WriteObserver#itemFlushed() - notifies when a previously written object have been flushed to the network.
  • StreamObserver#streamIdAssigned(long) - notifies when a steam of a multiplexed connection (HTTP/2) gets an assigned id number.
  • ConnectionObserver#onTransportHandshakeComplete() - notifies when a transport protocol completes a handshake (like TCP 3-way handshake).

Tracing

Due to a bug in our B3 header handling, we weren’t able to handle all possible sampling states as defined by the specification. More specifically the lack of presence of x-b3-sampled, was being mishandled as if the carrier was suggesting no-sampling.

The behavior was address by changing the API to support Boolean type sampling flag, restoring the possibility of 3-state option.

  • InMemorySpanContext#isSampled boolean → Boolean

Additional API changes:

  • Removed InMemoryTraceState, SpanContext was enhanced to bridge API gap.
  • Trace identifiers were removed from the Span level, instead the SpanContext houses them now.

Other

  • NettyIoExecutors#createIoExecutor(ThreadFactory) now requires io.servicetalk.transport.api.IoThreadFactory.

Removed functionality

  • gRPC filters have been removed. HTTP filters should be used to cover most use-cases. When an API-specific filter is required or when you need to filter deserialized proto objects extending/wrapping your GrpcClient or GrpcService should provide the same level of control.

Upgrading to ServiceTalk 0.42

ServiceTalk 0.42 contains significant API and behavior updates as well as new API deprecations. Most applications will require some changes to use the 0.42 release. For applications developed using ServiceTalk 0.41 or earlier the best approach for updating an application to use the 0.42 release is to begin by updating the application to use the latest 0.41 release, currently 0.41.12. Once all deprecation warning have been resolved and tests are passing, only then begin upgrading to the 0.42 release.
Applications built with much older ServiceTalk releases should upgrade incrementally through the latest release of each minor version until arriving at ServiceTalk 0.42. Fully rebuilding applications with each intermediate ServiceTalk version will provide the most complete deprecation and upgrading advice. Fully rebuilding applications with each intermediate ServiceTalk release will provide the most complete deprecation and upgrading advice and ensure that generated gRPC code is remains compatible with the application usage.
Application developers should also pay careful attention to the API deprecations found in 0.42. Future 0.42.X releases, when they occur, will be compatible with this 0.42.0 release, but 0.43 and beyond versions of ServiceTalk will remove the currently deprecated APIs. Fortunately, it is currently expected that the ServiceTalk API will be more stable in future releases through the eventual ServiceTalk 1.0 release. Thank you for your understanding and cooperation on the way to 1.0!

New features

  • 4ce1aa6 - New Serializer APIs, consolidation of ContentCodec, and gRPC MethodDescriptor (#1673)
  • f40a200 - gRPC add MethodDescriptor Collection to services (#1764)
  • 5569e81 - Introduce TimeSource in Executor (#1972)
  • f5ad671 - Introduce HttpServerContext and GrpcServerContext (#1990)
  • 583aa24 - Add weight factor to the load balanced addresses (#1988)
  • d1d577e - Add ExecutionStrategyInfluencer (#1832)
  • 506cfd2 - Configurable offloading for async close (#1948)
  • c15ff24 - Configurable TransportEvent offloading (#1957)
  • 6af1f60 - ConnectionAcceptor ConnectionFactory offloading control (#1921)
  • 9db9b7f - HTTP OpenTracing filters Format<TextMap> support (#1758)
  • 6beebe8 - API to support start/stop accepting connections (#1741)
  • 6017f38 - Add Publisher#multicast operator, fix Publisher#groupBy backpressure (#1472)
  • c38ddff - Completable#concat(Completable...) (#1941)
  • 9f0a924 - Conditional publishOn & subscribeOn operators (#1689)
  • b980aaa - Add support for nonOffloading HTTP Server Filters (#1710)
  • d3cc787 - Support additional constant delay for retries (#1754)

Behavior changes

  • 83530f2 - Multi-address client: throw MalformedURLException instead of IllegalArgumentException (#1955)
  • e9d69fa - Adjust parent types for exceptions (#1924)
  • 8f50732 - Disable offloading for routes if server offloading disabled (#1842)

Bug fixes

  • 8cf0d86 - DefaultCompositeCloseable StackOverflowException (#1942)
  • 72a0e64 - Avoid error from connection closure when signals should be queued (#1850)
  • c4699d2 - Fix B3 sampling API to support missing values (#1640)
  • 76c30bf - Correctly accept responses with grpc-encoding: identity (#1745)

Improvements

  • 68ddd1d - HTTP/2 messages with empty body should not have empty trailers (#1883)
  • e7927b7 - ZipkinPublisher remove unnecessary data (#1767)
  • a4951c9 - Http conditional offload (#1724)

API Improvements

  • f3e611b - Convert BlockingStreamingHttpServerResponse to an interface (#1986)
  • 73cf04b - StatelessTrailersTransformer: Object → Void state (#1985)
  • 40ba1fa - Rename special execution strategies (#1987)
  • f581029 - publishOn/subscribeOn take io.servicetalk.concurrent.Executor (#1983)
  • 17c9a1d - Rename subscribeShareContext to shareContextOnSubscribe (#1975)
  • 7f456a8 - Use connection execution context in proxy connect (#1951)
  • b3c356a - Converting GrpcClientBuilder to interface (#1908)
  • 9eb3bf2 - Converting GrpcServerBuilder to interface (#1903)
  • 1c61791 - Returns for some AsyncContext methods (#1839) (#1898)
  • 3c9bd5c - Untangling GrpcClientBuilder from SingleAddressHttpClientBuilder (#1867)
  • eedf988 - Converting HttpServerBuilder to an interface (#1820)
  • d77785d - Converting HttpClientBuilders to interfaces (#1819)
  • 603b2dc - Conditional offload consistent exception handling (#1705)
  • 1a2aa05 - Relax OpenTracing HttpHeaders formatter to allow custom containers (#1703)
  • 418e4a4 - NettyIoExecutors executors support IoThread
  • 84be6a3 - Simplify API hierarchy of HTTP and gRPC client builders (#1743)

Removed API

  • 3ac1487 - Remove gRPC filters (#1893)
  • 2725206 - Retry unification deprecations removal (#2019)
  • d6c8ce3 - Clean up TransportObserver after #2012 (#2015)
  • a234235 - Clean up Read/WriteObserver after #2013 (#2014)
  • 6498a4a - Remove deprecated constructors from HttpHeadersFactories (#2002)
  • a2b2bbf - Remove deprecated HttpClients factories for proxy (#1980)
  • bf17e6a - Remove deprecated AbstractRetryingFilterBuilder build methods (#1981)
  • c953e16 - Remove default impl from ConnectionObserver (#1982)
  • d410975 - Remove subscribeShareContext method (#1984)
  • 689246c - Cleanup LoadBalancer APIs (#1976)
  • 8b578f7 - Removing deprecated ServiceDiscovererEvent methods (#1962)
  • 1e96f77 - Clean up deprecated requester/client/filter API (#1960)
  • 8387995 - Cleanup default methods from HttpMetaData (#1961)
  • b2cb23a - Clean up AsyncContext API (#1959)
  • b095506 - Removing deprecated ServiceDiscoveryRetryStrategy (#1949)
  • d9947bb - Remove unused exception types (#1922)
  • 15e6a75 - Removing the public deprecated methods from gRPC builders (#1899)
  • bc9fbbe - Clean up deprecated API after changes for redirects in #1792 (#1862)
  • 8a9488e - Removing default implementations from HttpClientBuilder (#1866)
  • 3f58ab7 - Removal of deprecated one-way builder methods (#1844)
  • 91d678a - Remove remaining disable* methods in builder APIs (#1826)
  • 74ac390 - Removed GrpcClientBuilder#MultiClientBuilder (#1809)
  • 7d5e191 - Remove payloadBody(CloseableIterable<Buffer>) methods (#1802)
  • c4f3bdc - Remove ExecutionStrategy offload* methods (#1794)
  • 171a765 - Cleanup of PartitionedHttpClientBuilder (#1731)
  • e0dcd99 - MultiAddressHttpClientBuilder API cleanup (#1721)
  • 8706542 - Remove publishAndSubscribeOn (#1649)
  • decf81d - Remove recoverWith operators that were deprecated in #1435 (#1635)
  • bfaf539 - Remove Verifiers factory that was deprecated in #1392 (#1633)
  • 7058755 - Remove deprecated CharSequences methods (#1634)
  • c304d8c - Remove append methods that were deprecated in #1578 (#1631)
  • edd0fd2 - Remove backlog(int) server builder option that was deprecated in #1375 (#1636)
  • c108732 - Remove disableHostnameVerification() that was deprecated in #1561 (#1637)
  • 2d8530c - Remove allowPrematureClosureBeforePayloadBody() that was deprecated in #1475 (#1638)
  • a6170f2 - Remove deprecated idleTimeout() methods (#1602)
  • 6aaa59a - Remove deprecated compression implementation (#1537)
  • 3e357a9 - Remove deprecated SecurityConfigurator and Builder.secure() APIs (#1474)
  • 4abbb69 - Remove deprecated enableWireLogging and enableFrameLogging overloads (#1420)

Build/Release/Github actions

Dependency upgrades

Documentation

  • 3f12fbf - Add [Http|Grpc]LifecycleObserver examples (#1857)
  • 5b9d7a9 - Improve deprecation docs on TypeHolder (#1782)
  • 6c9b8cc - Javadoc improvements for HttpServerBuilder#append* methods (#1729)
  • 8da1b25 - Conditional publishOn and subscribeOn javadoc clarification (#1713)

Thank you

Every idea, review, and bug-report counts and so we thought it is worth mentioning those who helped in this area. Please report an unintended omission.

@bondolo
@cancecen
@chemicL
@danfaer
@idelpivnitskiy
@Krupskis
@madrob
@mikhail-shibanov
@rnett
@Scottmitch
@sikevux
@sullis
@tkountis
@vglushak