├── .gitignore ├── README.md ├── SourceSansPro-Bold.otf ├── SourceSansPro-Bold.ttf ├── SourceSansPro-Italic.otf ├── SourceSansPro-Italic.ttf ├── SourceSansPro-Light.otf ├── SourceSansPro-Light.ttf ├── SourceSansPro-Regular.otf ├── SourceSansPro-Regular.ttf ├── alphabetical.html ├── categories.html ├── fonts.css ├── format.js ├── he.js ├── impressum.html ├── index.html ├── jekyll ├── Makefile ├── _config.yml ├── _layouts │ └── page.html ├── alphabetical.md ├── categories.md ├── impressum.md ├── index.md └── videos.md ├── kuhn_ReactiveDesignPatterns_err1.html ├── kuhn_cover.jpg ├── kuhn_cover150.jpg ├── lectures ├── actors.html ├── composition.html ├── design.html ├── distributed1.html ├── distributed2.html ├── eventual-consistency.html ├── failures.html ├── introduction.html ├── jekyll │ ├── Makefile │ ├── _config.yml │ ├── _layouts │ │ └── page.html │ ├── actors.md │ ├── composition.md │ ├── design.md │ ├── distributed1.md │ ├── distributed2.md │ ├── eventual-consistency.md │ ├── failures.md │ ├── introduction.md │ ├── lifecycle.md │ ├── messages.md │ ├── persistence.md │ ├── responsiveness.md │ ├── scalability.md │ ├── testing.md │ ├── typed_akka.md │ ├── typed_facilities.md │ ├── typed_persistence.md │ ├── typed_protocols.md │ ├── typed_supervision.md │ └── typed_testing.md ├── lifecycle.html ├── messages.html ├── persistence.html ├── responsiveness.html ├── scalability.html ├── testing.html ├── toc.json ├── typed_akka.html ├── typed_facilities.html ├── typed_persistence.html ├── typed_protocols.html ├── typed_supervision.html └── typed_testing.html ├── local.css ├── marked.js ├── patterns ├── active–active.html ├── active–passive.html ├── aggregator.html ├── ask-pattern.html ├── business-handshake.html ├── circuit-breaker.html ├── complex-command.html ├── conflict-detection-and-resolution.html ├── conflict-free-replicated-data-types.html ├── consensus-based.html ├── domain-object.html ├── drop.html ├── error-kernel.html ├── event-sourcing.html ├── event-stream.html ├── forward-flow.html ├── jekyll │ ├── Makefile │ ├── _config.yml │ ├── _layouts │ │ └── page.html │ ├── active–active.md │ ├── active–passive.md │ ├── aggregator.md │ ├── ask-pattern.md │ ├── buh │ ├── business-handshake.md │ ├── circuit-breaker.md │ ├── complex-command.md │ ├── conflict-detection-and-resolution.md │ ├── conflict-free-replicated-data-types.md │ ├── consensus-based.md │ ├── domain-object.md │ ├── drop.md │ ├── error-kernel.md │ ├── event-sourcing.md │ ├── event-stream.md │ ├── forward-flow.md │ ├── let-it-crash.md │ ├── managed-blocking.md │ ├── managed-queue.md │ ├── pull.md │ ├── request-response.md │ ├── resource-encapsulation.md │ ├── resource-loan.md │ ├── resource-pool.md │ ├── saga.md │ ├── self-contained-message.md │ ├── sharding.md │ ├── simple-component.md │ └── throttling.md ├── let-it-crash.html ├── managed-blocking.html ├── managed-queue.html ├── pull.html ├── request-response.html ├── resource-encapsulation.html ├── resource-loan.html ├── resource-pool.html ├── saga.html ├── self-contained-message.html ├── sharding.html ├── simple-component.html └── throttling.html ├── strapdown.css ├── strapdown.js └── videos.html /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | jekyll/_site 3 | */jekyll/_site 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This holds the source files for the website https://www.reactivedesignpatterns.com/. 2 | -------------------------------------------------------------------------------- /SourceSansPro-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactiveDesignPatterns/website/f5ab83983c871d488379a1a6a1bb81c58ecdeb92/SourceSansPro-Bold.otf -------------------------------------------------------------------------------- /SourceSansPro-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactiveDesignPatterns/website/f5ab83983c871d488379a1a6a1bb81c58ecdeb92/SourceSansPro-Bold.ttf -------------------------------------------------------------------------------- /SourceSansPro-Italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactiveDesignPatterns/website/f5ab83983c871d488379a1a6a1bb81c58ecdeb92/SourceSansPro-Italic.otf -------------------------------------------------------------------------------- /SourceSansPro-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactiveDesignPatterns/website/f5ab83983c871d488379a1a6a1bb81c58ecdeb92/SourceSansPro-Italic.ttf -------------------------------------------------------------------------------- /SourceSansPro-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactiveDesignPatterns/website/f5ab83983c871d488379a1a6a1bb81c58ecdeb92/SourceSansPro-Light.otf -------------------------------------------------------------------------------- /SourceSansPro-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactiveDesignPatterns/website/f5ab83983c871d488379a1a6a1bb81c58ecdeb92/SourceSansPro-Light.ttf -------------------------------------------------------------------------------- /SourceSansPro-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactiveDesignPatterns/website/f5ab83983c871d488379a1a6a1bb81c58ecdeb92/SourceSansPro-Regular.otf -------------------------------------------------------------------------------- /SourceSansPro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactiveDesignPatterns/website/f5ab83983c871d488379a1a6a1bb81c58ecdeb92/SourceSansPro-Regular.ttf -------------------------------------------------------------------------------- /fonts.css: -------------------------------------------------------------------------------- 1 | /* For license and copyright of all fonts below see https://www.fontsquirrel.com/license/source-sans-pro */ 2 | @font-face { 3 | font-family: 'Source Sans Pro'; 4 | font-style: italic; 5 | font-weight: 400; 6 | src: url(SourceSansPro-Italic.otf) format('opentype'), url(SourceSansPro-Italic.ttf) format('truetype'); 7 | } 8 | @font-face { 9 | font-family: 'Source Sans Pro'; 10 | font-style: normal; 11 | font-weight: 300; 12 | src: url(SourceSansPro-Light.otf) format('opentype'), url(SourceSansPro-Light.ttf) format('truetype'); 13 | } 14 | @font-face { 15 | font-family: 'Source Sans Pro'; 16 | font-style: normal; 17 | font-weight: 400; 18 | src: url(SourceSansPro-Regular.otf) format('opentype'), url(SourceSansPro-Regular.ttf) format('truetype'); 19 | } 20 | @font-face { 21 | font-family: 'Source Sans Pro'; 22 | font-style: normal; 23 | font-weight: 700; 24 | src: url(SourceSansPro-Bold.otf) format('opentype'), url(SourceSansPro-Bold.ttf) format('truetype'); 25 | } 26 | -------------------------------------------------------------------------------- /impressum.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |Angaben gemäß §5 TMG:
46 | 47 |Dr. Roland Kuhn
48 | Habichtsforstweg 10
49 | 34132 Kassel
Kontakt:
52 | 53 |info@reactivedesignpatterns.com
54 | 55 |Verantwortlich für den Inhalt nach §55 Abs. 2 RStV:
56 | 57 |Dr. Roland Kuhn
58 | 59 |This lecture gives a more formal introduction to the Actor model. In particular we cover:
51 | 52 |The video concludes with a demonstration of running an Actor System and a summary of what Actors are according to the formal model.
61 | 62 | 66 |In this lecture we build an Actor-based link checker application in which multiple actors collaborate. On this example we see how:
51 | 52 |Along this journey we find ourselves considering the interplay between Actor code and other concurrency abstractions, in particular considering where we must be careful so that the Actor encapsulation is not broken. We also see how the use of immutable data structures helps avoid mistakes in this regard.
60 | 61 | 65 |This lecture goes deeper on how the Akka Cluster works. In particular you will see:
51 | 52 |The effects are demonstrated on the link checker example as well.
58 | 59 | 63 |This lecture takes note of the fact that all communication in a distributed system takes time—there is no instantaneous understanding of what a remote component is currently doing. We discuss this starting out from locally implemented strong consistency:
51 | 52 |This lecture treats the most important characteristic of Actors, namely how they support handling failures in a principled fashion. Due to their encapsulation, failure is contained within the compartment of the system that is represented by each Actor. We therefore cover:
51 | 52 |We summarize these learnings by drawing a diagram of the Actor lifecycle.
61 | 62 | 66 |This first lecture motivates actors as a concurrency abstraction. In particular we discuss
51 | 52 |This leads to the conclusion that we want non-blocking synchronization.
59 | 60 | 64 |In this lecture we build upon what we learned about an Actor’s lifecycle and introduce facilities for monitoring it. We then discuss the consequences of hierarchical supervision for the architecture of our system. In particular we cover:
51 | 52 |In this lecture we take a deeper look at how Actors function. This is fundamentally defined by how they are processing messages. After revisiting their encapsulated nature we cover:
51 | 52 |These aspects are demonstrated on the bank account transfer process that was used in the first lecture to demonstrate race conditions and deadlocks. We conclude by enumerating the key effects of focusing on message-passing.
60 | 61 | 65 |This lecture treats the storage of persistent state for Actors. It starts out from a comparison between in-place updates and an append-only event log (event-sourcing). In particular we cover:
51 | 52 |We conclude this lecture by discussing when and how to perform non-idempotent external effects from your Actors.
61 | 62 | 66 |In this final lecture we discuss the means and tradeoffs involved in achieving the responsiveness that is required of our systems. In particular, we cover:
51 | 52 |In summary, we reach the conclusion that message-driven systems can be scaled vertically and horizontally, and that responsiveness requires both resilience and elasticity.
60 | 61 | 65 |This lecture discusses scalability, starting out from contrasting this characteristic with performance—the goal that is often inadequately placed first. We cover:
51 | 52 |This lecture completes the first walkthrough of how to use Actors by introducing the means for testing them. Due to their encapsulation, testing Actors is approached differently from traditional OO testing—mocks cannot be used to deeply introspect their behavior. We will cover:
51 | 52 |After viewing this lecture you will be well-equipped for the first exercise.
60 | 61 | 65 |Akka Typed is an evolution of the untyped Akka actor implementation to support
51 | channel types: most importantly, the ActorRef
now is parameterized with the
52 | type of messages it accepts. This has several other consequences that we
53 | discuss in this lecture.
The use of more precise types within actor interactions is then demonstrated on 56 | a small example, including how to set up a typed actor system and how to handle 57 | fully typed responses, i.e. continuations of the protocol throughout the full 58 | interchange.
59 | 60 | 64 |Working with strictly typed actor references benefits from some new facilities:
51 | 52 |These factilities are introduced in this lecture.
60 | 61 | 65 |In this lecture we take a look at the integration of typed actors with Akka 51 | Persistence, implementing the saga pattern as an example. Along the way we 52 | discover how to create a single-use message adapter with ease.
53 | 54 | 58 |This lecture sets the stage for the Akka Typed week: we explore how to express 51 | a communication protocol such that a machine can understand the message 52 | exchange — meaning who shall send which message type and when. In particular we 53 | discuss session types, consisting of a global type that describes the whole 54 | interaction at once, and local projections that specify the expected sequence 55 | of send and receive operations for any single party in isolation.
56 | 57 | 61 |This lecture discusses how supervision in Akka Typed differs from the 51 | previously discussed supervision implemented in untyped Akka actors:
52 | 53 |The interesting part is that supervision is implemented as a purely functional 60 | behavior decorator, show-casing how libraries can add powerful capabilities to 61 | the Akka Typed system.
62 | 63 | 67 |We have seen in previous weeks that testing actors in their natural habitat 51 | involves asynchronous tests, timeouts, and non-determinism. Since Akka Typed 52 | uses pure behavior functions, we can write deterministic tests without 53 | depending on timing, as demonstrated in this lecture.
54 | 55 | 59 |“Keep multiple copies of a service running in different locations, and perform all modifications at all of them.”
46 | 47 |With multiple-master replication patterns, you achieved resilience for the 48 | storage subsystem of the example batch job processing facility by replicating 49 | it across different locations (data centers, availability zones, and so on). 50 | You saw that you can achieve strong consistency only when implementing a 51 | failover mechanism; both CRDT-based replication and conflict detection avoid 52 | this at the cost of not guaranteeing full consistency. One property of failover 53 | is that it takes some time: first, you need to detect that there is trouble, 54 | and then, you must establish consensus about how to fix it—for example, by 55 | switching to another replica. Both activities require communication and 56 | therefore cannot be completed instantaneously. Where this is not tolerable, you 57 | need a different strategy, but because there is no magic bullet, you must 58 | expect different restrictions.
59 | 60 | 61 | 62 |“Keep multiple copies of the service running in different locations, but only accept modifications to the state in one location at any given time.”
46 | 47 |This pattern is also sometimes referred to as failover or master-slave 48 | replication. You have already seen one particular form of failover: the ability 49 | to restart a component means that after a failure, a new instance is created 50 | and takes over functionality, like passing the baton from one runner to the 51 | next. For a stateful service, the new instance accesses the same persistent 52 | storage location as the previously failed one, recovering the state before the 53 | crash and continuing from there. This works only as long as the persistent 54 | storage is intact; if that fails, then restarting is not possible—the service 55 | will forget all of its previous state and start from scratch. Imagine your web 56 | shop forgetting about all registered users—that would be a catastrophe!
57 | 58 | 59 | 60 |“Create an ephemeral component if multiple service responses are needed to compute a service call’s result.”
46 | 47 |We have introduced the Ask pattern for the case of requiring a request-response 48 | cycle before a service call can be answered. There are cases, though, where a 49 | larger number of these cycles need to be completed, and none of the requests 50 | depend on the responses of the others—in other words, the request-response 51 | cycles can be made in parallel. Disregarding this opportunity for parallelism 52 | means prolonging the time until the overall response can be sent back to the 53 | client, thus leaving an opportunity for latency reduction unused.
54 | 55 | 56 | 57 |“Include identifying and/or sequencing information in the message, and keep retrying until confirmation is received.”
46 | 47 |While discussing the Saga pattern, we made the implicit assumption that 48 | communication between the saga and the affected components is reliable. We 49 | pictured a group of people standing in the same office and discussing the 50 | process without external disturbances. This is a useful way to begin, because 51 | it allows you to focus on the essential parts of a conversation; but we know 52 | that life does not always work like that—in particular in distributed systems, 53 | where messages can be lost or delayed due to unforeseeable, inexorable 54 | subsystem failures.
55 | 56 |Fortunately, we can treat the two concerns on separate levels: imagining that 57 | the conversation occurs in a busy, noisy office does not invalidate the basic 58 | structure of the business process we are modeling. All that is needed is to 59 | deliver every message more carefully, transmitting it again if it is 60 | overshadowed by some other event. This second level is what the Business 61 | Handshake pattern is all about.
62 | 63 | 64 | 65 |“Protect services by breaking the connection to their users during prolonged failure conditions.”
46 | 47 |In previous patterns, we discussed how to segregate a system into a hierarchy 48 | of components and subcomponents for the purpose of isolating responsibilities 49 | and encapsulating failure domains. This pattern describes how to safely connect 50 | different parts of the system so that failures do not spread uncontrollably 51 | across them. Its origin lies in electrical engineering: in order to protect 52 | electrical circuits from each other and introduce decoupled failure domains, a 53 | technique was established of breaking the connection when the transmitted power 54 | exceeds a given threshold.
55 | 56 |Translated to a Reactive application, this means the flow of requests from one 57 | component to the next may be broken up deliberately when the recipient is 58 | overloaded or otherwise failing. Doing so serves two purposes: first, the 59 | recipient gets some breathing room to recover from possible load-induced 60 | failures; and second, the sender decides that requests will fail instead of 61 | wasting time with waiting for negative replies.
62 | 63 | 64 | 65 |“Send compound instructions to the resource to avoid excessive network usage.”
46 | 47 |You have encapsulated the resources your system uses in components that manage, 48 | represent, and directly implement their functionality. This allows you to 49 | confine responsibility not only for code-modularity reasons (chapter 6) but 50 | also for vertical and horizontal scalability (chapters 4 and 5) and principled 51 | failure handling (chapter 7). The price of all these advantages is that you 52 | introduce a boundary between the resource and the rest of the system that can 53 | only be crossed by asynchronous messaging. The Resource Loan pattern may help 54 | to move a resource as close as possible to its users, but this barrier will 55 | remain, leading to increased latency and, usually, decreased communication 56 | bandwidth. The core of the Complex Command pattern lies in sending the behavior 57 | to the resource in order to save time and network bandwidth in case of 58 | loquacious interchanges between resource and users; the user of the resource is 59 | only interested in the comparatively small result.
60 | 61 | 62 | 63 |“Keep multiple copies of a service running in different locations, accept modifications everywhere, and disseminate all modifications among them.”
46 | 47 |If you want to change your replication scheme such that it can continue to 48 | operate during a transient network partition, then you will have to make some 49 | compromises. Obviously, it is impossible to reach consensus without 50 | communication, so if all cluster nodes are to make progress in accepting and 51 | processing requests at all times, conflicting actions may be performed.
52 | 53 | 54 | 55 |“Keep multiple copies of a service running in different locations, accept modifications everywhere, and disseminate all modifications among them.”
46 | 47 |In the previous pattern, you 48 | achieved perfect availability of the batch service’s storage component—where 49 | perfect means “it works as long as one replica is reachable”—at the cost of 50 | either losing updates or having to care about manual conflict resolution. You 51 | can improve on this even further, but unfortunately at another cost: it is not 52 | possible to avoid conflicts while maintaining perfect availability without 53 | restricting the data types that can be expressed.
54 | 55 | 56 | 57 |“Keep multiple copies of a service running in different locations, accept modifications everywhere, and disseminate all modifications among them.”
46 | 47 |Given a group of people, we have a basic understanding of what consensus means: 48 | within the group, all members agree on a proposal and acknowledge that this 49 | agreement is unanimous. From personal experience, we know that reaching 50 | consensus is a process that can take quite a bit of time and coordination 51 | effort, especially if the matter starts out as being contentious—in other 52 | words, if initially there are multiple competing proposals, and the group must 53 | decide which single one to support.
54 | 55 | 56 | 57 |“Dropping requests is preferable to failing uncontrollably.”
46 | 47 |Imagine a system that is exposed to uncontrollable user input (for example, a 48 | site on the internet). Any deployment will be finite in both its processing 49 | capability and its buffering capacity, and if user input exceeds the former for 50 | long enough, the latter will be used up and something will need to fail. If 51 | this is not foreseen explicitly, then an automatic out-of-memory killer will 52 | make a decision that is likely to be less satisfactory than a planned 53 | load-shedding mechanism—and shedding load means dropping requests.
54 | 55 |This is more of a philosophy than an implementation pattern. Network protocols, 56 | operating systems, programming platforms, and libraries will all drop packets, 57 | messages, or requests when overloaded; they do so in order to protect the 58 | system so that it can recover when load decreases. In the same spirit, authors 59 | of Reactive systems need to be comfortable with the notion of sometimes 60 | deliberately losing messages.
61 | 62 | 63 | 64 |“In a supervision hierarchy, keep important application state or functionality near the root while delegating risky operations towards the leaves.”
46 | 47 |This pattern builds on the Simple Component pattern and is applicable wherever 48 | components with different failure probability and reliability requirements are 49 | combined into a larger system or application—some functions of the system must 50 | never go down, whereas others are necessarily exposed to failure. Applying the 51 | Simple Component pattern will frequently leave you in this position, so it pays 52 | to familiarize yourself well with the Error Kernel pattern.
53 | 54 |This pattern has been established in Erlang programs for decades and was one of 55 | the main inspirations for Jonas Bonér to implement an Actor framework—Akka—on 56 | the JVM. The name Akka was originally conceived as a palindrome of Actor 57 | Kernel, referring to this core design pattern.
58 | 59 | 60 | 61 |“Perform state changes only by applying events. Make them durable by storing the events in a log.”
46 | 47 |Looking at the Domain Object pattern example, you can see 48 | that all state changes the manager actor performs are coupled to an event that 49 | is sent back to the client that requested this change. Because these events 50 | contain the full history of how the state of the domain object evolved, you may 51 | as well use it for the purpose of making the state changes persistent—this, in 52 | turn, makes the state of the domain object persistent. This pattern was 53 | described in 2005 by Martin Fowler and picked up by Microsoft Research, and 54 | it has shaped the design of the Akka Persistence module.
55 | 56 | 57 | 58 |“Publish the events emitted by a component so that the rest of the system can derive knowledge from them.”
46 | 47 |The events that a component stores in its log represent the sum of all the 48 | knowledge it has ever possessed. This is a treasure trove for the rest of the 49 | system to delve into: although the shopping cart system is only interested in 50 | maintaining the current state of customer activity, other concerns are 51 | tangential to it, such as tracking the popularity of various products. This 52 | secondary concern does not need to be updated in a guaranteed fashion in real 53 | time; it does not matter if it lags behind the most current information by a 54 | few seconds (individual humans usually would not be able to notice even a delay 55 | of hours in this information). Therefore, it would be an unnecessary burden to 56 | have the shopping cart component provide this summary information, and it would 57 | also violate the Simple Component pattern by introducing a second 58 | responsibility.
59 | 60 | 61 | 62 |“Let the information and the messages flow directly toward their destination where possible.”
46 | 47 |This pattern sounds intuitive, maybe even trivial: why would you deliberately 48 | send messages via detours when that is not required? The answer is that this 49 | rarely occurs consciously; it is the result of applying a convenient, 50 | well-known pattern overeagerly. The pattern in question is your good friend the 51 | Request-Response pattern, with or without the Ask pattern’s sugar coating. 52 | Overusing this pattern leads to unnecessary consumption of network bandwidth 53 | and increased response latency; thinking about forward flow lets you recognize 54 | these cases and improve your service quality.
55 | 56 | 57 | 58 |“Prefer a full component restart to internal failure handling.”
46 | 47 |In chapter 7 of the book we discuss principled failure handling, noting that the 48 | internal recovery mechanisms of each component are limited because they are not 49 | sufficiently separated from the failing parts—everything within a component can 50 | be affected by a failure. This is especially true for hardware failures that 51 | take down the component as a whole, but it is also true for corrupted state 52 | that is the result of some programming error only observable in rare 53 | circumstances. For this reason, it is necessary to delegate failure handling to 54 | a supervisor instead of attempting to solve it within the component.
55 | 56 |This principle is also called crash-only software: the idea is that transient 57 | but rare failures are often costly to diagnose and fix, making it preferable to 58 | recover a working system by rebooting parts of it. This hierarchical 59 | restart-based failure handling makes it possible to greatly simplify the 60 | failure model and at the same time leads to a more robust system that even has 61 | a chance to survive failures that were entirely unforeseen.
62 | 63 | 64 | 65 |“Blocking a resource requires consideration and ownership.”
46 | 47 |In the example code for how the execution component in the batch job service 48 | provisions new worker nodes, you have already encountered the situation that an 49 | API you are using is designed to block its calling thread. In the case of the 50 | AWS API, there are ways around that, but this is not always the case. Many 51 | libraries or frameworks that you may want to or have to use do not offer the 52 | option of event-driven interaction. Java Database Connectivity (JDBC) is a 53 | well-known example that comes to mind. In order to use these APIs in a Reactive 54 | system component, you need to take special care to properly manage the 55 | resources that are implicitly seized, most notably the threads required for 56 | their execution.
57 | 58 | 59 | 60 |“Have the consumer ask the producer for batches of data.”
46 | 47 |One challenge in Reactive systems is how to balance the relationship between 48 | producers and consumers of messages—be they requests that need processing or 49 | facts on their way to persistent storage. The difficulty lies in the dynamic 50 | nature of the problems that may arise from incorrect implementations: only 51 | under realistic input load can you observe whether a fast producer might 52 | overwhelm a resource-constrained consumer. Often, your load test environments 53 | are based on business forecasts that may be exceeded in real usage.
54 | 55 |The formulation of the Pull pattern presented here is the result of Roland’s 56 | involvement in the Reactive Streams initiative, where the resulting behavior 57 | is also characterized as dynamic push-pull, an aspect that we will discuss 58 | later.
59 | 60 | 61 | 62 |“Include a return address in the message to receive a response.”
46 | 47 |This is the most basic interaction pattern we know; it is the foundational 48 | building block for all natural communication, deeply ingrained in human 49 | training. A parent will say something to their infant child, who will initially 50 | gesture or make some sounds in response; later, the child will articulate words 51 | and sentences. Request-response is the way you learn how to speak, and it 52 | underlies all sophisticated forms of communication that you develop later. 53 | Although the response can be nonverbal (in particular, facial expressions, but 54 | also the deliberate absence thereof), in most cases you require a response 55 | before successfully concluding a conversation. The response can be a piece of 56 | information that you asked for or an acknowledgment of receipt for a command 57 | that was given.
58 | 59 |In all these cases, there is one commonality that you need to make explicit 60 | when translating this basic means of communication into a programming pattern: 61 | the process begins with two participants A and B, where A knows how to address 62 | B; when receiving requests, B will need to learn or deduce how to address A in 63 | order to send back a response.
64 | 65 | 66 | 67 |“A resource and its lifecycle are responsibilities that must be owned by one component.”
46 | 47 |From the Simple Component pattern, you know that every 48 | component does only one thing, but does it in full; in other words, each 49 | component is fully responsible for the functionality it provides to the rest of 50 | the system. If we regard that functionality as a resource that is used by other 51 | components—inside or outside the system—then it is clear that resource, 52 | responsibility, and component exactly coincide. These three terms all denote 53 | the same boundary: in this view, resource encapsulation and the single 54 | responsibility principle are the same. The same reasoning can be applied when 55 | considering other resources, in particular those used to provide a component’s 56 | function. These are not implemented but merely are managed or represented: the 57 | essence of the Resource Encapsulation pattern is that you must identify that 58 | component into whose responsibility each resource falls, and place it there. 59 | The resource becomes part of that component’s responsibility. Sometimes this 60 | will lead you to identify the management of an external resource as a notable 61 | responsibility that needs to be broken out into its own simple component.
62 | 63 | 64 | 65 |“Give a client exclusive transient access to a scarce resource without transferring ownership.”
46 | 47 |A variant of the Resource Loan pattern is widely used in 48 | non-Reactive systems, the most prominent example being that of a database 49 | connection pool. Database access is represented by a connection object via 50 | which arbitrary operations can be performed. The creation of connections is 51 | expensive, and their number is limited; therefore, a connection is not owned by 52 | client code but is taken from a pool before performing an operation and put 53 | back afterward. The connection pool is responsible for managing the lifecycle 54 | of its connections, and client code obtains temporary permission to use them. 55 | Failures in this scenario are communicated to the client, but their effect on 56 | the connection in question is handled by the pool—the pool owns and supervises 57 | the connections.
58 | 59 |In a Reactive system, you strive to minimize contention as well as the need for 60 | coordination: hence, the classic database connection pool usually only features 61 | as an internal implementation detail of a component whose data storage is 62 | realized by means of a relational database. But you will frequently encounter 63 | the use of scarce resources in your systems, and the same philosophy that 64 | drives the connection pool abstraction is useful in Reactive system design as 65 | well.
66 | 67 | 68 | 69 |“Hide an elastic pool of resources behind their owner.”
46 | 47 |So far, we have discussed the modeling and operation of a single resource as 48 | well as its interaction with its clients. The astute reader will have noticed 49 | that something is missing from the full picture: the core principles of 50 | Reactive system design demand replication. Recalling the discussion from 51 | chapter 2, you know that resilience cannot be achieved without distributing the 52 | solution across all failure axes—software, hardware, human—and you know that 53 | elasticity requires the ability to distribute the processing load across a 54 | number of resources that are dynamically adjusted to the incoming demand.
55 | 56 |In chapter 13, you learned about different ways to replicate a component. The 57 | effort put into this mechanism depends greatly on how much the component’s 58 | state needs to be synchronized between replicas. This pattern focuses on the 59 | management and external presentation of the replicas. In keeping with the 60 | reasoning presented in chapters 4 and 5, it relies heavily on asynchronous 61 | message passing and location transparency to achieve scalability.
62 | 63 | 64 | 65 |“Divide long-lived, distributed transactions into quick local ones with compensating actions for recovery.”
46 | 47 |The term saga was coined by Hector Garcia-Molina. His paper describes a scheme 48 | for breaking up long-lived business transactions in order to shorten the time 49 | period during which databases need to hold locks—these locks are needed to 50 | ensure atomicity and consistency of the transaction, the downside of which is 51 | that other transactions touching the same data cannot proceed concurrently.
52 | 53 |In a distributed system, you need to break up transactions involving multiple 54 | participants for other reasons: obtaining a shared lock is an expensive 55 | operation that can even be impossible in the face of certain failures like 56 | network partitions. As we discussed in chapter 8, the key to scalability and 57 | loose coupling is to consider each component an island of delimited 58 | consistency. But how do you model business transactions that require inputs 59 | from multiple components while also effecting modifications to multiple 60 | components? It turns out the research topic of sagas provides an effective, 61 | robust solution for many use cases.
62 | 63 | 64 | 65 |“Each message will contain all information needed to process a request as well as to understand its response.”
46 | 47 |When sending a request, you need to remember what you want to do with the 48 | response that will eventually come back. In other words, you need to manage the 49 | state of the larger operation that this exchange is a part of while the request 50 | and response travel between components. This state management can be done 51 | entirely by the requester—storing contextual information—or it can be pushed 52 | out of it by having the entire context travel with the request and response 53 | across the network. In practice, this responsibility is usually shared, leaving 54 | part of the state in the requester and having part of it travel with the 55 | message. The point of this pattern is that you should strive to include 56 | sufficient information in the message so the state that is relevant to the 57 | current request is fully represented—removing and relocating relevant 58 | information should be considered a premature optimization until proven 59 | otherwise.
60 | 61 | 62 | 63 |“Scale out the management of a large number of domain objects by grouping them into shards based on unique and stable object properties.”
46 | 47 |The Domain Object pattern gives you the ability to wrap 48 | the domain’s state in small components that can, in principle, be distributed 49 | easily across a cluster of network nodes in order to provide the resources for 50 | representing even very large domains that cannot be held in memory by a single 51 | machine. The difficulty then becomes how to address the individual domain 52 | objects without having to maintain a directory that lists every object’s 53 | location—such a directory could easily reach a size that is impractical to hold 54 | in memory.
55 | 56 |The Sharding pattern places an upper bound on the size of the directory by 57 | grouping the domain objects into a configurable number of shards—the domain is 58 | fractured algorithmically into pieces of manageable size. The term 59 | algorithmically means the association between objects and shards is determined 60 | by a fixed formula that can be evaluated whenever an object needs to be 61 | located.
62 | 63 | 64 | 65 |“A component shall do only one thing, but do it in full.”
46 | 47 |This pattern applies wherever a system performs multiple functions or the 48 | functions it performs are so complex that they need to be broken into different 49 | components. An example is a text editor that includes spell checking: these are 50 | two separate functions (editing can be done without spell checking, and 51 | spelling can also be checked on the finished text and does not require editing 52 | capabilities), but on the other hand, neither of these functions is trivial.
53 | 54 |The Simple Component pattern derives from the single responsibility 55 | principle that was formulated by Tom DeMarco in his 1979 book Structured 56 | Analysis and System Specification (Prentice Hall). In its abstract form, it 57 | demands to “maximize cohesion and minimize coupling.” Applied to 58 | object-oriented software design, it is usually stated as follows: “A class 59 | should have only one reason to change.”
60 | 61 | 62 | 63 |“Throttle your own output rate according to contracts with other services.”
46 | 47 |We have discussed how each component can mediate, measure, and react to back 48 | pressure in order to avoid uncontrollable overload situations. With these means 49 | at your disposal, it is not just fair but obligatory that you respect the 50 | ingestion limits of other components. In situations where you outpace consumers 51 | of your outputs, you can slow to match their rate, and you can even ensure that 52 | you do not violate a prearranged rate agreement.
53 | 54 |In the Circuit Breaker pattern, you saw that a circuit 55 | breaker can be designed such that it rejects requests that would otherwise lead 56 | to a request rate that is too high. With the Pull pattern, you can turn this 57 | around and not generate requests more quickly than they are allowed to be sent.
58 | 59 | 60 | 61 |