Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure.

Ever since Reactive Streams 1.0.0 was released at the end of April 2015, I’ve been wondering when—if ever—I’d write another announcement blog post for a new version on Reactive Streams. For the most part, I’ve been extremely curious to see what would happen: would there be any fatal flaws in the specification, or an insurmountable design flaw in the interfaces, would the TCK prove to be valuable to implementors or would it be a mere nuisance?

Perhaps the biggest news items of all, is that Reactive Streams has achieved its goal of eventually becoming a Java standard, by being included as java.util.concurrent.Flow in JDK9!

Over the past two years collaborating and gathering feedback from implementors, from users, from watching how Reactive Streams has been received in the various communities. Not only did the initial participating reactive-streams implementing libraries continue to thrive, but new ones continued to join the efforts as well. Databases and message queues continued to receive reactive-streams compatible APIs (such as reactive-kafka, or the alpakka connector ecosystem) and even big players like Amazon’s AWS SDK adopting reactive streams in the new release.

We even saw the expansion of Reactive Streams beyond the JVM, with reactive-streams-dotnet having a number of implementations, and even completely new designs in other languages being directly inspired by akka-streams, such as the Elixir language and their new GenStage APIs.

Perhaps the biggest news items of all, is that Reactive Streams has achieved its goal of eventually becoming a Java standard, by being included as java.util.concurrent.Flow in JDK9!

The day has finally come, and I am extremely proud to note that not only is Reactive Streams 1.0.1 100% backwards compatible, but it introduces ZERO changes to the interfaces.

«But…» you say «what merits another release then?»

First and foremost, we improved the documentation, both the reference documentation and the Javadocs. Furthermore, we noted that many terms and wordings in the specification lacked unambiguous definitions, and such we set out to define them in the new Glossary section and make sure that the specification would use them wherever appropriate:

Term Definition Signal As a noun: one of the onSubscribe , onNext , onComplete , onError , request(n) or cancel -methods. As a verb: calling/invoking a signal. Demand As a noun, the aggregated number of elements requested by a Subscriber which is yet to be delivered (fulfilled) by the Publisher. As a verb, the act of request -ing more elements. Synchronous(ly) Executes on the calling Thread. Return normally Only ever returns a value of the declared type to the caller. The only legal way to signal failure to a Subscriber is via the onError method. Responsivity Readiness/ability to respond. In this document used to indicate that the different components should not impair each others ability to respond. Non-obstructing Quality describing a method which is as quick to execute as possible—on the calling thread. This means, for example, avoids heavy computations and other things that would stall the caller´s thread of execution. Terminal state For a Publisher: When onComplete or onError has been signalled. For a Subscriber: When an onComplete or onError has been received. NOP Execution that has no detectable effect to the calling thread, and can as such safely be called any number of times. External synchronization Access coordination for thread safety purposes implemented outside of the constructs defined in this specification, using techniques such as, but not limited to, atomics , monitors , or locks .

We also realized that while having a specification was valuable, what was missing was background information about the different rules, so we decided to—for every rule—include an explanation as to what that rule intends to solve/provide/achieve—an Intent section. Doing this means that it is much easier for someone to gain an understanding of the problem space which Reactive Streams operates in.

Furthermore, having to write down Intent sections for each rule, meant justifying, not only to the Special Interest Group (SIG), but to all consumers of Reactive Streams—i.e. why that rule should exist and how it contributes towards making Reactive Streams work. Here are some examples to illustrate what I mean:

ID Rule 1 The total number of onNext ´s signalled by a Publisher to a Subscriber MUST be less than or equal to the total number of elements requested by that Subscriber ´s Subscription at all times. The intent of this rule is to make it clear that Publishers cannot signal more elements than Subscribers have requested. There’s an implicit, but important, consequence to this rule: Since demand can only be fulfilled after it has been received, there’s a happens-before relationship between requesting elements and receiving elements. 2 A Publisher MAY signal fewer onNext than requested and terminate the Subscription by calling onComplete or onError . The intent of this rule is to make it clear that a Publisher cannot guarantee that it will be able to produce the number of elements requested; it simply might not be able to produce them all; it may be in a failed state; it may be empty or otherwise already completed.

In order to better help guide implementors towards making compliant Reactive Streams implementations, we decided to improve the TCK in the following ways:

Allow configuring separate timeout for "no events during N time", allowing for more aggressive timeouts in the rest of the test suite if required (#314)

New test verifying Rule 2.10, in which subscriber must be prepared to receive onError signal without having signaled request before (#374)

The verification of Rule 3.9 has been split up into 2 different tests, one to verify that an IllegalArgumentException is sent, and the other an optional check to verify that the exception message informs that non-positive request signals are illegal.

While working on the improved TCK and the Intent sections of the specification, we realized that one rule was overly strict, and as such we decided that loosening up a requirement was appropriate, worth noting is that the change is fully backwards-compatible, since it is now more lenient:

Subscription Rule 9

1.0.0: While the Subscription is not cancelled, Subscription.request(long n) MUST signal onError with a java.lang.IllegalArgumentException if the argument is <= 0. The cause message MUST include a reference to this rule and/or quote the full rule.

1.0.1: While the Subscription is not cancelled, Subscription.request(long n) MUST signal onError with a java.lang.IllegalArgumentException if the argument is <= 0. The cause message SHOULD explain that non-positive request signals are illegal.

Comment: The MUST requirement to include a reference to the rule in the exception message has been dropped, in favor of that the exception message SHOULD explain that non-positive requests are illegal.

In more concrete terms, these are the code contributions between the v1.0.0 and v1.0.1 releases:

Commits Added Removed 17 419 86 Viktor Klang 16 1223 206 akarnokd 16 629 91 Konrad `ktoso` Malawski 6 159 23 egetman 2 3 2 Anthony Vanelverdinghe 2 2 2 Luke Daley 2 2 1 Kaz Sera 2 2 1 Brian Topping 2 1 0 BjornHamels 1 9 8 Rossen Stoyanchev 1 15 0 Roland Kuhn 1 5 4 Ángel Sanz 1 2 1 Jake Wharton 1 1 1 David Moten 1 1 0 Patrik Nordwall

I’d personally like to extend the warmest of thanks to all the Reactive Streams contributors for their excellent work over the past years—and Doug Lea specifically—for his guidance, and his work with JSR-166 over the years. Reactive Streams is here to stay!

Next steps:

If you’re a Reactive Streams user, there’s nothing you need to change.

If you’re a Reactive Streams implementor, update the version of your dependencies to 1.0.1 and run a compile + test to verify that the implementation passes the tests still compile and pass the new TCK.

√

Viktor Klang, Deputy CTO of Lightbend, Inc.

Share

Comments

Cookies are required for this functionality.Allow Cookies

Filter by Tag