Slide 1

Slide 1 text

Adventures with Reactive Streams @NiteshKant Nitesh Kant @jovoordeckers Jo Voordeckers

Slide 2

Slide 2 text

Image source: https://interestingengineering.com/12-of-the-worlds-most-fascinating-dams

Slide 3

Slide 3 text

Image source: https://en.wikipedia.org/wiki/Oroville_Dam_crisis

Slide 4

Slide 4 text

@NiteshKant Nitesh Kant @jovoordeckers Jo Voordeckers

Slide 5

Slide 5 text

Find my friends GET /friends?id=3 HTTP/1.1

Slide 6

Slide 6 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF …

Slide 7

Slide 7 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} …

Slide 8

Slide 8 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} …

Slide 9

Slide 9 text

HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() Find my friends getFriendsCsv() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101

Slide 10

Slide 10 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send()

Slide 11

Slide 11 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send()

Slide 12

Slide 12 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send()

Slide 13

Slide 13 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send()

Slide 14

Slide 14 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send()

Slide 15

Slide 15 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … Producer Consumer HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send()

Slide 16

Slide 16 text

HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … Producer Consumer Control response.send()

Slide 17

Slide 17 text

I am interested in consuming data! Control

Slide 18

Slide 18 text

I am interested in consuming data! Give me 10 items! Control

Slide 19

Slide 19 text

I am interested in consuming data! Give me 10 items! I am not interested any more. Control

Slide 20

Slide 20 text

Java Message Service https://docs.oracle.com/javaee/5/tutorial/doc/bncdx.html // Synchronous Message m = consumer.receive(); Message m = consumer.receive(1000); // time out 1000ms // Asynchronous MessageListener myListener = new MyListener(); // void onMessage(Message message) consumer.setMessageListener(myListener); request(1)

Slide 21

Slide 21 text

Java Message Service https://docs.oracle.com/javaee/5/tutorial/doc/bncdx.html // Synchronous Message m = consumer.receive(); Message m = consumer.receive(1000); // time out 1000ms // Asynchronous MessageListener myListener = new MyListener(); // void onMessage(Message message) consumer.setMessageListener(myListener); flow control depends on broker

Slide 22

Slide 22 text

Apache Kafka https://kafka.apache.org/10/javadoc/?org/apache/kafka/clients/consumer/KafkaConsumer.html KafkaConsumer consumer = new KafkaConsumer<>(props); consumer.subscribe(Arrays.asList("foo", "bar")); while (true) { ConsumerRecords records = consumer.poll(100); // time out for (ConsumerRecord record : records) { // iterate over batch } consumer.commitSync(); } // Flow control public void pause(java.util.Collection partitions) public void resume(java.util.Collection partitions) request(n)

Slide 23

Slide 23 text

Apache Kafka https://kafka.apache.org/10/javadoc/?org/apache/kafka/clients/consumer/KafkaConsumer.html KafkaConsumer consumer = new KafkaConsumer<>(props); consumer.subscribe(Arrays.asList("foo", "bar")); while (true) { ConsumerRecords records = consumer.poll(100); // time out for (ConsumerRecord record : records) { // iterate over batch } consumer.commitSync(); } // Flow control public void pause(java.util.Collection partitions) public void resume(java.util.Collection partitions) additional flow control by topic/partition

Slide 24

Slide 24 text

Apache Kafka JMS // Synchronous Message m = consumer.receive(); Message m = consumer.receive(1000); // Asynchronous // void onMessage(Message message) MessageListener myListener = new MyListener(); consumer.setMessageListener(myListener); KafkaConsumer consumer = new KafkaConsumer<>(props); consumer.subscribe(Arrays.asList("foo", "bar")); while (true) { ConsumerRecords records = consumer.poll(100); for (ConsumerRecord record : records) { // iterate over batch } consumer.commitSync(); } // Flow control public void pause(java.util.Collection partitions) public void resume(java.util.Collection partitions)

Slide 25

Slide 25 text

Apache Kafka JMS // Synchronous Message m = consumer.receive(); Message m = consumer.receive(1000); // Asynchronous // void onMessage(Message message) MessageListener myListener = new MyListener(); consumer.setMessageListener(myListener); KafkaConsumer consumer = new KafkaConsumer<>(props); consumer.subscribe(Arrays.asList("foo", "bar")); while (true) { ConsumerRecords records = consumer.poll(100); for (ConsumerRecord record : records) { // iterate over batch } consumer.commitSync(); } // Flow control public void pause(java.util.Collection partitions) public void resume(java.util.Collection partitions) how do we bridge these APIs?

Slide 26

Slide 26 text

Service A Service B

Slide 27

Slide 27 text

Service A Service B cache ?

Slide 28

Slide 28 text

Service A Service B cache Service C ?

Slide 29

Slide 29 text

Service A Service B cache Service C DB ?

Slide 30

Slide 30 text

Service A Service B cache Service C DB ? define contract for interoperability

Slide 31

Slide 31 text

Service A Service B cache Service C DB write adapters for all the things \o/ ?

Slide 32

Slide 32 text

Service A Service B cache Service C DB write adapters for all the things \o/ ? ✓ cancelation ✓ buffering

Slide 33

Slide 33 text

https://github.com/reactive-streams/reactive-streams-jvm

Slide 34

Slide 34 text

https://github.com/reactive-streams/reactive-streams-jvm

Slide 35

Slide 35 text

Reactive Streams public interface Publisher { public void subscribe(Subscriber s); }

Slide 36

Slide 36 text

Reactive Streams public interface Publisher { public void subscribe(Subscriber s); } I am interested in consuming data!

Slide 37

Slide 37 text

Reactive Streams public interface Subscriber { public void onSubscribe(Subscription s); public void onNext(T t); public void onError(Throwable t); public void onComplete(); }

Slide 38

Slide 38 text

Reactive Streams public interface Subscriber { public void onSubscribe(Subscription s); public void onNext(T t); public void onError(Throwable t); public void onComplete(); } Control exchange

Slide 39

Slide 39 text

Reactive Streams public interface Subscription { public void request(long n); public void cancel(); }

Slide 40

Slide 40 text

Reactive Streams public interface Subscription { public void request(long n); public void cancel(); } Give me “n” items!

Slide 41

Slide 41 text

Reactive Streams public interface Subscription { public void request(long n); public void cancel(); } I am not interested any more.

Slide 42

Slide 42 text

under-specification Image source: https://newrepublic.com/article/118416/what-dhaka-bangladesh-traffic-capital-world-can-teach-us

Slide 43

Slide 43 text

under-specification risk of chaos between actors of the interaction rules Image source: https://newrepublic.com/article/118416/what-dhaka-bangladesh-traffic-capital-world-can-teach-us

Slide 44

Slide 44 text

under-specification risk of chaos between actors target least common denominator of the interaction rules Image source: https://newrepublic.com/article/118416/what-dhaka-bangladesh-traffic-capital-world-can-teach-us

Slide 45

Slide 45 text

under-specification risk of chaos between actors target least common denominator systemic inefficiency of the interaction rules Image source: https://newrepublic.com/article/118416/what-dhaka-bangladesh-traffic-capital-world-can-teach-us

Slide 46

Slide 46 text

subscribe() Publisher Subscriber

Slide 47

Slide 47 text

onSubscribe() subscribe() Publisher Subscription onSubscribe() Subscriber

Slide 48

Slide 48 text

request() onSubscribe() subscribe() Publisher Subscription onSubscribe() Subscriber

Slide 49

Slide 49 text

onNext() onComplete() onError() onSubscribe() subscribe() Publisher Subscription onNext() onComplete() onError() onSubscribe() Subscriber request()

Slide 50

Slide 50 text

sequence onSubscribe() onComplete() subscribe() request() onSubscribe() subscribe() onError() request() onSubscribe() subscribe() onNext() onError() request() onSubscribe() subscribe() onNext() onNext() onComplete() request() request() cancel() onSubscribe() subscribe() request() cancel() onNext() onComplete() onSubscribe() subscribe()

Slide 51

Slide 51 text

lifecycle management onNext() X subscribe() cancel() request() subscribe() onSubscribe() onNext() request() X onNext() onSubscribe() onNext() request() onComplete() X onError() subscribe() X

Slide 52

Slide 52 text

How difficult could it be to implement?

Slide 53

Slide 53 text

Single item, asynchronously produced and delivered

Slide 54

Slide 54 text

Single item, asynchronously produced and delivered (Attempt #1) class SingleValue implements Publisher { private T value; private boolean subscriberReady; private Subscriber subscriber; @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } } * This is a hot/eager source, i.e. the work has started before a Subscriber arrives

Slide 55

Slide 55 text

Single item, asynchronously produced and delivered (Attempt #1) class SingleValue implements Publisher { private T value; private boolean subscriberReady; private Subscriber subscriber; @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } } * This is a hot/eager source, i.e. the work has started before a Subscriber arrives emit() subscribe() request() cancel()

Slide 56

Slide 56 text

Single item, asynchronously produced and delivered (Attempt #1) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives Asynchrony emit() subscribe() request() cancel() class SingleValue implements Publisher { private T value; private boolean subscriberReady; private Subscriber subscriber; @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }

Slide 57

Slide 57 text

Single item, asynchronously produced and delivered (Attempt #1) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives Asynchrony emit() subscribe() request() cancel() class SingleValue implements Publisher { private T value; private boolean subscriberReady; private Subscriber subscriber; @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }

Slide 58

Slide 58 text

Single item, asynchronously produced and delivered (Attempt #1) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives Asynchrony emit() subscribe() request() cancel() class SingleValue implements Publisher { private T value; private boolean subscriberReady; private Subscriber subscriber; @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }

Slide 59

Slide 59 text

class SingleValue implements Publisher { private T value; private boolean subscriberReady; private Subscriber subscriber; @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } } Single item, asynchronously produced and delivered (Attempt #1) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives Asynchrony emit() subscribe() request() cancel()

Slide 60

Slide 60 text

Single item, asynchronously produced and delivered (Attempt #1) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives Concurrency emit() subscribe() request() cancel() class SingleValue implements Publisher { private T value; private boolean subscriberReady; private Subscriber subscriber; @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }

Slide 61

Slide 61 text

Single item, asynchronously produced and delivered (Attempt #1) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives Concurrency emit() subscribe() request() cancel() class SingleValue implements Publisher { private T value; private boolean subscriberReady; private Subscriber subscriber; @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }

Slide 62

Slide 62 text

Single item, asynchronously produced and delivered (Attempt #1) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives Concurrency emit() subscribe() request() cancel() class SingleValue implements Publisher { private T value; private boolean subscriberReady; private Subscriber subscriber; @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }

Slide 63

Slide 63 text

Single item, asynchronously produced and delivered (Attempt #1) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives Concurrency emit() subscribe() request() cancel() X class SingleValue implements Publisher { private T value; private boolean subscriberReady; private Subscriber subscriber; @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }

Slide 64

Slide 64 text

Single item, asynchronously produced and delivered (Attempt #1) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives Concurrency emit() subscribe() request() cancel() https://github.com/reactive-streams/reactive-streams-jvm#1.3 class SingleValue implements Publisher { private T value; private boolean subscriberReady; private Subscriber subscriber; @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); s.onComplete(); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }

Slide 65

Slide 65 text

Single item, asynchronously produced and delivered (Attempt #1) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives Concurrency emit() subscribe() request() cancel() https://github.com/reactive-streams/reactive-streams-jvm#2.7 class SingleValue implements Publisher { private T value; private boolean subscriberReady; private Subscriber subscriber; @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); s.onComplete(); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }

Slide 66

Slide 66 text

Single item, asynchronously produced and delivered (Attempt #2) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives Concurrency emit() subscribe() request() cancel() class SingleValue implements Publisher { private T value; private boolean subscriberReady; private Subscriber subscriber; @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); s.onComplete(); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }

Slide 67

Slide 67 text

Single item, asynchronously produced and delivered (Attempt #2) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives Concurrency emit() subscribe() request() cancel() class SingleValue implements Publisher { private T value; private boolean subscriberReady; private Subscriber subscriber; @Override public synchronized void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); s.onComplete(); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public synchronized void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }

Slide 68

Slide 68 text

Cost of synchronized? Image source: https://www.shutterstock.com/image-photo/iceberg-above-underwater-view-taken-greenland-1127373485

Slide 69

Slide 69 text

s.onNext(value); public synchronized void emit(T value) ¯\_(ツ)_/¯

Slide 70

Slide 70 text

Single item, asynchronously produced and delivered (Attempt #3) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives https://github.com/reactive-streams/reactive-streams-jvm#3.4

Slide 71

Slide 71 text

class SingleValue implements Publisher { private static final int IDLE = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private T value; private Subscriber subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { int old = state.getAndSet(REQUESTED); if (old == EMITTED) { s.onNext(value); s.onComplete(); } } @Override public void cancel() { } }); } public void emit(T value) { this.value = value; if (state.getAndSet(EMITTED) == REQUESTED) { subscriber.onNext(value); subscriber.onComplete(); } } } Single item, asynchronously produced and delivered (Attempt #4) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives State Machine IDLE EMITTED REQUESTED

Slide 72

Slide 72 text

Single item, asynchronously produced and delivered (Attempt #4) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives State Machine IDLE class SingleValue implements Publisher { private static final int IDLE = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private T value; private Subscriber subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { int old = state.getAndSet(REQUESTED); if (old == EMITTED) { s.onNext(value); s.onComplete(); } } @Override public void cancel() { } }); } public void emit(T value) { this.value = value; if (state.getAndSet(EMITTED) == REQUESTED) { subscriber.onNext(value); subscriber.onComplete(); } } } EMITTED REQUESTED

Slide 73

Slide 73 text

Single item, asynchronously produced and delivered (Attempt #5) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives State Machine IDLE class SingleValue implements Publisher { private static final int IDLE = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private static final int CANCELLED = 3; private T value; private Subscriber subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { int old = state.getAndSet(REQUESTED); if (old == EMITTED) { s.onNext(value); s.onComplete(); } } @Override public void cancel() { state.set(CANCELLED); } }); } public void emit(T value) { this.value = value; if (state.getAndSet(EMITTED) == REQUESTED) { subscriber.onNext(value); subscriber.onComplete(); } } } EMITTED REQUESTED CANCELLED

Slide 74

Slide 74 text

class SingleValue implements Publisher { private static final int IDLE = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private static final int CANCELLED = 3; private T value; private Subscriber subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { int old = state.getAndSet(REQUESTED); if (old == EMITTED) { s.onNext(value); s.onComplete(); } } @Override public void cancel() { state.set(CANCELLED); } }); } public void emit(T value) { this.value = value; if (state.getAndSet(EMITTED) == REQUESTED) { subscriber.onNext(value); subscriber.onComplete(); } } } Single item, asynchronously produced and delivered (Attempt #5) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives State Machine IDLE EMITTED REQUESTED CANCELLED

Slide 75

Slide 75 text

Single item, asynchronously produced and delivered (Attempt #5) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives subscription.cancel(); subscription.request(1) class SingleValue implements Publisher { private static final int IDLE = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private static final int CANCELLED = 3; private T value; private Subscriber subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { int old = state.getAndSet(REQUESTED); if (old == EMITTED) { s.onNext(value); s.onComplete(); } } @Override public void cancel() { state.set(CANCELLED); } }); } public void emit(T value) { this.value = value; if (state.getAndSet(EMITTED) == REQUESTED) { subscriber.onNext(value); subscriber.onComplete(); } } }

Slide 76

Slide 76 text

Single item, asynchronously produced and delivered (Attempt #6) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives State Machine IDLE class SingleValue implements Publisher { private static final int IDLE = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private T value; private Subscriber subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { int old = state.getAndSet(REQUESTED); if (old == EMITTED) { s.onNext(value); s.onComplete(); } } @Override public void cancel() { } }); } public void emit(T value) { this.value = value; if (state.getAndSet(EMITTED) == REQUESTED) { subscriber.onNext(value); subscriber.onComplete(); } } } EMITTED REQUESTED https://github.com/reactive-streams/reactive-streams-jvm#3.6

Slide 77

Slide 77 text

Single item, asynchronously produced and delivered (Attempt #6) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives class SingleValue implements Publisher { private static final int IDLE = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private static final int CANCELLED = 3; private T value; private Subscriber subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber s) { s.onSubscribe(new Subscription() { @Override public void request(long n) { int old = state.getAndUpdate(val -> val == CANCELLED ? CANCELLED : REQUESTED); if (old == EMITTED) { s.onNext(value); s.onComplete(); } } @Override public void cancel() { state.set(CANCELLED); } }); subscriber = s; } public void emit(T value) { this.value = value; int old = state.getAndUpdate(val -> val == CANCELLED ? CANCELLED : EMITTED); if (old == REQUESTED) { subscriber.onNext(value); subscriber.onComplete(); } } }

Slide 78

Slide 78 text

class SingleValue implements Publisher { private static final int IDLE = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private static final int CANCELLED = 3; private T value; private Subscriber subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber s) { s.onSubscribe(new Subscription() { @Override public void request(long n) { int old = state.getAndUpdate(val -> val == CANCELLED ? CANCELLED : REQUESTED); if (old == EMITTED) { s.onNext(value); s.onComplete(); } } @Override public void cancel() { state.set(CANCELLED); } }); subscriber = s; } public void emit(T value) { this.value = value; int old = state.getAndUpdate(val -> val == CANCELLED ? CANCELLED : EMITTED); if (old == REQUESTED) { subscriber.onNext(value); subscriber.onComplete(); } } } Single item, asynchronously produced and delivered (Attempt #6) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives

Slide 79

Slide 79 text

Single item, asynchronously produced and delivered (Attempt #6) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives class SingleValue implements Publisher { private static final int IDLE = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private static final int TERMINATED = 3; private T value; private Subscriber subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber s) { s.onSubscribe(new Subscription() { @Override public void request(long n) { if (n <= 0 && state.getAndSet(TERMINATED) != TERMINATED) { s.onError(new IllegalArgumentException("Non positive request signals are illegal.")); return; } int old = state.getAndUpdate(val -> val == TERMINATED ? TERMINATED : REQUESTED); if (old == EMITTED) { state.set(TERMINATED); s.onNext(value); s.onComplete(); } } @Override public void cancel() { state.set(TERMINATED); } }); subscriber = s; } public void emit(T value) { this.value = value; int old = state.getAndUpdate(val -> val == TERMINATED ? TERMINATED : EMITTED); if (old == REQUESTED) { state.set(TERMINATED); subscriber.onNext(value); subscriber.onComplete(); } } } Want to see how multiple item emission will look like?

Slide 80

Slide 80 text

Let’s get real … ✋

Slide 81

Slide 81 text

response.send() Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} …

Slide 82

Slide 82 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} …

Slide 83

Slide 83 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher

Slide 84

Slide 84 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher Control

Slide 85

Slide 85 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher subscription.request(1)

Slide 86

Slide 86 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher subscription.request(1) Demand is required to compose through multiple components

Slide 87

Slide 87 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher subscription.cancel() Cancellation is required to compose through multiple components

Slide 88

Slide 88 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher Publisher client.get("friends?id=1") response.send(Publisher resp)

Slide 89

Slide 89 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher response.send( client.get("friends?id=1") );

Slide 90

Slide 90 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher List transformToJson(List csvs) { List friends; for (String csv : csvs) { friends.add(toJson(csv)); } return friends; }

Slide 91

Slide 91 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher response.send( client.get("friends?id=1") );

Slide 92

Slide 92 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher response.send( client.get("friends?id=1") .map(csv -> toJson(csv)) );

Slide 93

Slide 93 text

Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher response.send( client.get("friends?id=1") .map(csv -> toJson(csv)) ); Function composition.

Slide 94

Slide 94 text

Reactive Streams is not sufficient as an end-user API.

Slide 95

Slide 95 text

Function Composition Lack of standards Project Reactor RxJava JDK Streams Akka Streams Kafka Streams

Slide 96

Slide 96 text

Practically, most applications have blocking code.

Slide 97

Slide 97 text

https://github.com/reactive-streams/reactive-streams-jvm#2.2

Slide 98

Slide 98 text

https://github.com/reactive-streams/reactive-streams-jvm#2.2 Embracing this reality is necessary…

Slide 99

Slide 99 text

Not everything is a stream Publisher getUser(String id);

Slide 100

Slide 100 text

Not everything is a stream Publisher getUser(String id); Publisher fireAndForget(String log);

Slide 101

Slide 101 text

Not everything is a stream Publisher getUser(String id); Publisher fireAndForget(String log); Publisher request(HttpRequest request);

Slide 102

Slide 102 text

Reactive Streams HTTP Kafka Cassandra RPC Redis () Function Composition Not everything is a stream Blocking safety

Slide 103

Slide 103 text

Image source: http://sciencemeetsfood.org/team-work/

Slide 104

Slide 104 text

Thank You! @NiteshKant Nitesh Kant @jovoordeckers Jo Voordeckers +