Presentation is loading. Please wait.

Presentation is loading. Please wait.

Advanced Topics in Concurrency and Reactive Programming: The Observable Contract Majeed Kassis.

Similar presentations


Presentation on theme: "Advanced Topics in Concurrency and Reactive Programming: The Observable Contract Majeed Kassis."— Presentation transcript:

1 Advanced Topics in Concurrency and Reactive Programming: The Observable Contract
Majeed Kassis

2 Definition: Reactive Definition: Transformational Approach:
Showing a response to a stimulus Acting in response to a situation rather than creating or controlling it Transformational Approach: Call a method Wait for result Store the return value from that method in a variable Use that variable and its new value to do something useful Reactive Approach: Define an Observer that specifies what to do with each emitted value Call a method that returns an Observable Subscribe the Observer to the Observable. This tells the Observable that it has a subscriber waiting to receive values when they’re available. Each value is emitted once available.

3 Reactive Types Functional reactive programming (FRP)
operates on values that change continuously over time each change facilitates a reaction to the change implementation is done using pure functional composition. Example: Haskell Reactive systems (architecture and design) a set of architectural design principles for building systems that are well prepared to meet the increasing demands. The foundation for a reactive system is message-passing Reactive programming (declarative event-based) operates on discrete values that are emitted over time in an asynchronous manner supports decomposing the problem into multiple discrete steps each step can be executed in an asynchronous and non-blocking fashion results are composed together to produce a workflow. Example: ReactiveX - RxJava functional composition, is using well-established combinators like map, filter, fold etc.

4 Functional Reactive Programming Example
var a = 32 var b = 10 var c = a + b println(c) // 42 a = 20 println(c) This will print 42 in the second time.

5 Functional Reactive Programming Example
var a = 32 var b = 10 var c = a + b println(c) // 42 a = 20 println(c) // 30

6 Reactive Systems: Building Blocks
A system is responsive if the system is elastic, resilient, and message driven (event-driven, in an asynchronous manner).

7 Reactive Systems Building Blocks
Elasticity - Responsiveness under load Scalable system design. Scale up when more users are coming. Scale down on less active periods to save resources. System stays responsive – to load Resiliency – Responsiveness under failure React to failure: Respond even in the face of failure Take actions to recover, as needed -> the ability to self-heal. In essence: designing systems with failures in mind. Message Driven: Decreased coupling (receiver can choose how to handle the message) Parallelize computations and combine back the results Dynamic routing and transformation; load balancing Error-handling Events are propagated in these messages.

8 Reactive programming Uses
Reactive programing suitable for: User interfaces Computer Games Control Applications Monitoring Applications Any event-heavy systems basically. ReactiveX is a combination of the best ideas from the Observer pattern, the Iterator pattern, and functional programming. ReactiveX is based on Observer Pattern: The main pattern from which reactive systems grew! Uses “Inversion of Control” or “Dependency Inversion”

9 Dependency Inversion: The Hollywood Principle
“Don’t call us, we’ll call you” After an audition, actors are asked not to call. Instead, they are contacted in case of succeeding. Instead of coupling classes at compile time You couple class instances, as needed, at runtime. Called “loose-coupling” Main Benefit: No use of unnecessary references between classes Only referencing classes that are needed at that specific time

10 Observer Pattern void addObserver(Observer o) { observers.add(o) } void removeObserver(Observer o) { observers.remove(o) private void notify(Event ev) { for (o <- observers) o(ev) Observer = Listener “Observable” – implements the ability to attach observers to it “Observer” – the class that handles an event in case it happens at the “observable” object

11 Issues in Observer Pattern
Unpredictable order of events The order in which events are received depends on the order of listeners registered. Missing first event problem How can we be sure that we registered our listener before receiving first event? Threading issues By default observer pattern is not thread safe! Leaking listeners Not deregistering listeners causes memory leaks. Accidental recursion The order of updating local state and notifying listeners is critical. Compounding complexity Using the pattern extensively will dramatically increase system complexity Leaking Listeners: Threading issues: Pitfalls:

12 Extended Observer Pattern: Two New Additions
In case of no more data to be emitted: The producer signals the consumer that there is no more data available. An Observable calls its observer’s onCompleted method. In case of exceptions at producer: The producer signals the consumer that an error has occurred An Observable calls its observer’s onError method Restriction: Observer is always the Consumer Observable is always the Producer In ReactiveX – Observable Sequences represent the source of the data Data which was generated by some producer.

13 Example of Reactive System: Netflix
The backend maintains multiple threadpools which their purpose is to generate responses to requests done by clients. Example: The user wishes to get the list of Movies, once they open the client. Process: Client sends three calls to sever: Movie, Bookmark, and Rating List The client then subscribes to the streams to be returned. The server asynchronously generates the requested data, and returns to client each time part is the data is ready to be sent. Once (part of) the data returns, the client applies functions in a functional way to generate the needed result.

14 The Observable Contract (Rules)
Comes to solves the nightmare created by unrestricted use of the observer pattern. These assumptions must be followed by all producers and consumers of Rx types. Purpose is to ensures that it is easy to reason about and prove the correctness of operators and user code. The Observable Contract defines: Observer and Observable Interface Communication between Observer/Observable Observable Termination Subscribing and Unsubscribing The use of multiple Observers Backpressure – what happens when consumer is slower than producer?

15 The Observable Contract: Observable Interface
Which defines the functions used to facilitate communication between Observer and Observable. OnNext An Observable calls this method whenever the Observable emits an item. This method takes as a parameter the item emitted by the Observable. OnComplete Indicates that the Observable has completed successfully and that it will be emitting no further items Once called, the subscription ends. OnError An Observable calls this method to indicate that it has failed to generate the expected data. It will not make further calls to onNext or onCompleted. The onError method takes as its parameter an indication of what caused the error. OnSubscribe (optional) indicates that the Observable is ready to accept Request notifications from the observer

16 The Observable Contract: Observer Interface
Subscribe indicates that the observer is ready to receive notifications from the Observable Unsubscribe indicates that the observer no longer wants to receive notifications from the Observable Request (optional) indicates that the observer wants no more than a particular number of additional OnNext notifications from the Observable

17 The Observable Contract: Observable Notifications
An Observable may make zero or more OnNext notifications Each notification representing a single emitted item An observable may then follow those emission notifications by either an OnCompleted or an OnError notification, but not both. If one of these two notifications is sent, it will not issue any further notifications. Observables must issue notifications to observers serially there must be a formal happens-before relationship between the notifications It can be done in parallel as long as happens-before relationship is kept intact.

18 In simpler words After an Observer calls an Observable's subscribe() method First the Observable calls onSubscribe(Disposable) with a Disposable that allows cancelling the sequence at any time. The Disposable object contains a Boolean and acts exactly as ‘shouldStop’. Then the Observable may call the Observer's onNext(T) method any number of times to provide notifications. An Observable will call an Observer's onComplete() method exactly once or the Observer's onError(java.lang.Throwable) method exactly once.

19 The Observable Contract: Observable Termination
If an Observable has not issued an OnCompleted or OnError notification, an observer may consider it to be still active even if it is not currently emitting items And It may issue it notifications, such as an Unsubscribe or Request notification When an Observable does issue an OnCompleted or OnError notification, the Observable may release its resources and terminate. Its observers should not attempt to communicate with it any further. An OnError notification must contain the cause of the error it is invalid to call OnError with a null value Before an Observable terminates it must first issue either an OnCompleted or OnError notification to all of the observers that are subscribed to it.

20 The Observable Contract: Subscribing and Unsubscribing
Once the Observable receives a Subscribe notification from the observer. An Observable may begin issuing notifications to an observer immediately Once an observer issues an Unsubscribe notification to an Observable the Observable will attempt to stop issuing notifications to the observer. The observable might issue notifications, even after the unsubscribe command it sent, until the unsubscribe request is handled! When an Observable issues an OnError or OnComplete notification to its observers, this also ends the subscription. No need to manually send an unsubscribe command.

21 The Observable Contract: Multiple Observers
If a second observer subscribes to an Observable that is already emitting items to a first observer The observable may emit the same items to each observer from its current index. The observable may emit the complete sequence of items from the beginning to the second observer. The observable may emit a wholly different sequence of items to the second observer. There is no general guarantee that two observers of the same Observable will see the same sequence of items! It all depends on the implementation logic.

22 The Observable Contract: Backpressure
An Observable may implement backpressure if it detects that its observer implements Request notifications and understands OnSubscribe notifications. The Observable will not begin to emit items to the observer immediately upon subscription. Instead, it will issue an OnSubscribe notification to the observer! Then, observer may issue a Request notification to the Observable it has subscribed to. Requesting a number of items. Requests are cumulative. If Observer sends three Request notification to an Observable, for 3, 5 and 10. The Observable will return at most 18 items.

23 Iterable vs Observable

24 Converting Iterable to Observable
// Observable<String> // that emits 75 Strings getDataFromNetwork() .skip(10) .take(5) .map({ s -­‐> return s + "_transformed"}) .subscribe( { println "onNext => " + it}) // Iterable<String> // that contains 75 Strings getDataFromLocalMemory() .skip(10) .take(5) .map({ s -­‐> return s + "_transformed"}) .forEach( { println "next => " + it}) forEach is a blocking function! Using subscribe is make it asynchronous.

25 Sync vs Async Single Multiple Sync T getData()
Iterable<T> getData() Async Future<T> getData() Observable<T> getData() Interaction with data can be done in 4 different ways.

26 Iterable<T> getData()
Single Multiple Sync T getData() Iterable<T> getData() Async Future<T> getData() Observable<T> getData() String s = getData(args); if (s.equals(x)) { // do something } else { // do something else } Typical synchronous scalar response with subsequent conditional logic. Data is fetched, and if-conditional is applied on the result to apply some logic.

27 Iterable<T> getData()
Single Multiple Sync T getData() Iterable<T> getData() Async Future<T> getData() Observable<T> getData() Iterable<String> values = getData(args); { for (String s : values) if (s.equals(x)) { // do something } else { // do something else } Similar to scalar value except conditional logic happens within a loop.

28 Iterable<T> getData()
Single Multiple Sync T getData() Iterable<T> getData() Async Future<T> getData() Observable<T> getData() Future<String> s = getData(args); if (s.get().equals(x)) { // do something } else { // do something else } As we move to async a normal Java Future is asynchronous but to apply conditional logic requires dereferencing the value via ‘get()’.

29 Iterable<T> getData()
Single Multiple Sync T getData() Iterable<T> getData() Async Future<T> getData() Observable<T> getData() Future<String> s = getData(args); if (s.get().equals(x)) { // do something } else { // do something else } And this leads to the typical issue in nested, conditional asynchronous code with Java Futures where asynchronous quickly becomes synchronous and blocking again.

30 Guava, and Guava ListenableFuture Object
Google Guava is an open-source set of common libraries for Java, mainly developed by Google engineers. ListenableFuture allows register callbacks, which solves the problem with the blocking get() use in ordinary Java Future. callbacks are executed once the computation is complete, or if the computation is already complete, immediately. Callbacks handle two cases: onSuccess – If the execution of the asynchronous call succeeds. onFailure – if the execution of the asynchronous call throws an exception.

31 Iterable<T> getData()
Single Multiple Sync T getData() Iterable<T> getData() Async Future<T> getData() Observable<T> getData() ListenableFuture<String> s = getData(args); Futures.addCallback(s, new FutureCallback<String> { public void if onSuccess(String s) { (s.get().equals(x)) { // do something } else { // do something else public void if onFailure(Throwable t) { (s.equals(x)) { // handle error } });

32 Iterable<T> getData()
Single Multiple Sync T getData() Iterable<T> getData() Async Future<T> getData() Observable<T> getData() CompletableFuture<String> s.thenApply((v) -> { if (v.equals(x)) { s = getData(args); // do something } else // { do something else } }); New CompletableFuture in Java 8 are composable with higher-order functions.

33 Iterable<T> getData()
Single Multiple Sync T getData() Iterable<T> getData() Async Future<T> getData() Observable<T> getData() CompletableFuture<String> s.thenApply((v) -> { if (v.equals(x)) { s = getData(args); // do something } else // { do something else } });

34 Iterable<T> getData()
Single Multiple Sync T getData() Iterable<T> getData() Async Future<T> getData() Observable<T> getData() Future<String> s = getData(args); s.map({ s -> if (s.equals(x)) { // do something } else { // do something else } }); Akka Futures are also composable and provide higher-order functions.

35 Iterable<T> getData()
Single Multiple Sync T getData() Iterable<T> getData() Async Future<T> getData() Observable<T> getData() Future<String> s.map({ s -> s = getData(args); if (s.equals(x)) { // do something } else { // do something else } }); That get us to where we want to be so that we can now compose conditional, nested data flows while remaining asynchronous.

36 Iterable<T> getData()
Single Multiple Sync T getData() Iterable<T> getData() Async Future<T> getData() Observable<T> getData() Observable<String> s = getData(args); s.map({ s -> if (s.equals(x)) { // do something } else { // do something else } }); Rx Observable supports multiple values which means it can handle a single value, a sequence of values or an infinite stream.

37 Iterable<T> getData()
Single Multiple Sync T getData() Iterable<T> getData() Async Future<T> getData() Observable<T> getData() Observable<String> s = getData(args); s.map({ s -> if (s.equals(x)) { // do something } else { // do something else } }); Rx Observable accommodates both, single and multi-valued responses Rx Observable facilitates higher-order functions to compose nested, conditional logic in a reactive manner.

38


Download ppt "Advanced Topics in Concurrency and Reactive Programming: The Observable Contract Majeed Kassis."

Similar presentations


Ads by Google