diff options
Diffstat (limited to 'rfc/sp-request-reply-01.xml')
-rw-r--r-- | rfc/sp-request-reply-01.xml | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/rfc/sp-request-reply-01.xml b/rfc/sp-request-reply-01.xml new file mode 100644 index 0000000..4b5f3c8 --- /dev/null +++ b/rfc/sp-request-reply-01.xml @@ -0,0 +1,246 @@ +<?xml version="1.0" encoding="US-ASCII"?> +<!DOCTYPE rfc SYSTEM "rfc2629.dtd"> + +<rfc category="info" docName="sp-request-reply-01"> + + <front> + + <title abbrev="Request/Reply SP"> + Request/Reply Scalability Protocol + </title> + + <author fullname="Martin Sustrik" initials="M." role="editor" + surname="Sustrik"> + <organization>GoPivotal Inc.</organization> + <address> + <email>msustrik@gopivotal.com</email> + </address> + </author> + + <date month="August" year="2013" /> + + <area>Applications</area> + <workgroup>Internet Engineering Task Force</workgroup> + + <keyword>Request</keyword> + <keyword>Reply</keyword> + <keyword>REQ</keyword> + <keyword>REP</keyword> + <keyword>stateless</keyword> + <keyword>service</keyword> + <keyword>SP</keyword> + + <abstract> + <t>This document defines a scalability protocol used for distributing + tasks from any number of clients among arbitrary number of stateless + processing nodes and getting the results back to the original + clients.</t> + </abstract> + + </front> + + <middle> + + <section title = "Introduction"> + + <t>One of the most common problems in distributed applications is how to + delegate a work to another processing node and get the result back to + the original node. There's a wide range of RPC systems addressing the + problem.</t> + + <t>However, in the generalised version of the problem we want to issue + processing requests from multiple clients, not just a single one and + distribute the work to any number processing nodes insead of a single + one so that the processing can be scaled up by adding new processing + nodes as necessary.</t> + + <t>Solving the generalised problem, however, requires that the processing + algorithm -- or "service" -- is stateless.</t> + + <t>To put it simply, the service is called "stateless" when there's no + way for the user to distinguish whether a request was processed by + one instance of the service or another one.</t> + + <t>So, for example, a service which accepts two integers and multiplies + them is stateless. Request for "2x2" is always going to produce "4", + no matter what instance of the service have computed it.</t> + + <t>Service that accepts empty requests and produces the number + of requests processed so far (1, 2, 3 et c.), on the other hand, is + not stateless. To prove that you can run two instances of the service. + First reply, no matter which instance produces it is going to be 1. + Second reply though is going to be either 2 (if processed by the same + instance as the first one) or 1 (if processed by the other instance). + Thus you can distinguish which instance produced the result. Thus, + according to the definition the service is not stateless.</t> + + <t>Despite the name, being "stateless" doesn't mean that the service has + no state at all. Rather it means that the service doesn't retain any + business-logic-related state in-between processing two subsequent + requests. The service is, of course, allowed to have state while + processing a single request. It can also have state that is unrelated + to its business logic, say statistics about the processing that are + used for administrative purposes and never returned to the clients.</t> + + <t>Note that "stateless" doesn't necessarily mean "fully deterministic". + For example, a service that generates random number is + non-deterministic. However, the client, after receiving a new random + number cannot tell which instance has produced it.</t> + + <t>While stateless services are often implemented by passing the entire + state inside the request, they are not required to do so. Especially + when the state is large, passing it around in each request may be + impractical. In such cases, it's often just a reference to the state + that's passed in the request, such as ID or path. The state itself + can then be retrieved by the service from a shared database, a network + file system or similar storage mechanism.</t> + + <t>Requiring services to be stateless serves a specific purpose. + It allows us to use any number of service instances to handle the + processing load. After all, the client won't be able to tell the + difference between replies from instance A and replies from instance B. + You can even start new instances on the fly and get away with it. + The client still won't be able to tell the difference. In other + words, statelessness is a prerequisite to make your service cluster + fully scalable.</t> + + <t>??? example topologies ???</t> + + </section> + + <section title = "Underlying protocol"> + + <t>The request/reply protocol can be run on top of any SP mapping, + such as, for example, SP TCP mapping.</t> + + <t>Also, given that SP protocols describe the behaviour of entire + arbitrarily complex topologies rather than of a single node-to-node + communication, several underlying protocols can be used in parallel. + For example, a client may send a request via WebSocket, then, on the + edge of the company network an intermediary node may retransimit it + via a TCP connection et c.</t> + + </section> + + <section title = "The endpoints"> + + <t>Request/reply protocol defines two different endpoint types: + The requester (the client) or REQ in short and the replier + (the service) or REP in short.</t> + + <t>Endpoint type identifiers should be assigned by IANA. For now, + value of 16 should be used for REQ endpoints and value of 17 for REP + endpoints.</t> + + <t>REQ endpoint can be connected only to a REP endpoint. REP endpoint + can be connected only to the REQ endpoint. If the underlying protocol + indicates that there's an attempt to create a channel to an + incompatible endpoint, the channel MUST be rejected. In the case of + TCP mapping, for example, the underlying TCP connection MUST + be closed.</t> + + <figure> + <artwork> + --- requests --> + ++-----+ +-----+-----+ +-----+-----+ +-----+ +| |-->| | |-->| | |-->| | +| REQ | | REP | REQ | | REP | REQ | | REP | +| |<--| | |<--| | |<--| | ++-----+ +-----+-----+ +-----+-----+ +-----+ + + <-- replies --- + </artwork> + </figure> + +<t> +TODO: +- priorities +- loop detection +- load balancing +- fair queueing +- backpressure +- resending +</t> + + </section> + + <section title = "Hop-by-hop behaviour"> + + <t>The REQ endpoint takes a message from the user and sends it to the one + of the channels associated with the endpoint. ...load-balancing... + prioritisation... back pressure...</t> + + <t>The REQ endpoint fair-queues the incoming messages. The goal is prevent + DoS attacks where a huge stream of fake replies from one channel can + block the real replies coming from different channels. Fair queueing + ensures that every channel gets a fair amount of processing capacity. + That way, even if DoS attack can slow down the processing to some + extent it can't entirely block the system.</t> + + <t>By default, the REP socket MUST fair queue the incoming requests. + While it is not possible to achieve fully fair distribution of requests + among the protcessing nodes in the topology, fair queuing ensures that + requests from one channel won't completely block out requests from + other channels.</t> + + <t>Before handing the message to the user, REP socket should prepend the + payload by an ID of the channel it was received from.</t> + + <t>REP endoint processes a message from the user (a reply) in the + following way: It tries to chop off the channel ID from the beginning + of the reply. If the reply is shorter that the channel ID, it + is malformed and should be ignored.</t> + + <t>Afterwards, REP endpoint checks its table of associated channel and + tries to find the one with corresponding ID. If there's no such + channel, either the message is malformed or the channel was closed + since the original request was routed through this endpoint. The two + cases cannot be distinguish and the endpoint should simply ignore + the message in either case.</t> + + <t>If the corresponding channel is found, REP endpoint tries to send the + reply (with the channel ID chopped off) to the channel. If it is not + possible due -- for example due to TCP pushback -- the message should + be ignored. The reason for this behaviour is that if the endpoint + blocked and waited for the channel be become available, all the + subsequent replies, possibly destined for different, unblocked channels + would be blocked in the meantime anyway. That would allow for a DoS + attack by simply firing a lot of requests and not receiving the + replies.</t> + + </section> + + <section title = "End-to-end behaviour"> + </section> + + <section title = "Loop prevention"> + + <t>It may happen that a request/reply topology contains a loop. It becomes + increasingly likely as the topology grows out of scope of a single + organisation and there are multiple administrators involved + in maintaining it. In such case unfortunate interaction between + two perfectly legitimate setups can cause loop to be created.</t> + + <t>With no additional guards against the loops, it may happen that + messages will be caugth rotating inside the loop, each message + gradually growing in size as new prefixes are added to it by each + REP endpoint on its way. Eventually, a loop could bring the whole + system to halt.</t> + + </section> + + <section anchor="IANA" title="IANA Considerations"> + <t>New SP endpoint types REQ and REP should be registered by IANA.</t> + </section> + + <section anchor="Security" title="Security Considerations"> + <t>The mapping isn't intended to provide any additional security to the + underlying protocol. DoS concerns are addressed within + the specification.</t> + </section> + + </middle> + +</rfc> + |