The Statelessness constraint of REST is important, but often poorly
understood. It is more restrictive than the SOA statelessness principle and
prohibits a number of useful communication patterns from REST architecture.
REST-style Statelessness
Fielding's Dissertation
describes the constraint thusly:
We next add a constraint to the client-server interaction: communication must be stateless in nature, as in the client-stateless-server (CSS) style of Section 3.4.3 (Figure 5-3), such that each request from client to server must contain all of the information necessary to understand the request, and cannot take advantage of any stored context on the server. Session state is therefore kept entirely on the client.
Section 5.3.1 expands on this with the following:
REST enables intermediate processing by constraining messages to be self-descriptive: interaction is stateless between requests, standard methods and media types are used to indicate semantics and exchange information, and responses explicitly indicate cacheability.
The purposes of introducing the statelessness constraint include improvements
to visibility, reliability, and scalability. This means that proxies and other
intermediaries are better able to participate in communication patterns that
involve self-descriptive stateless messages, server death and failover does
not result in session state synchronisation problems, and it is easy to add
new servers to handle client load again without needing to synchronise session
state.
REST achieves statelessness through a number of mechanisms:
- By designing methods and communication patterns that they do not require
state to be retained server-side after the request.
- By designing services that expose capabilities to directly sample and
transition server-side state without left-over application state
- By "deferring" or passing back state to the client as a message at the end
of each request whenever session state or application state is required
The downside of statelessness is exposed in that last point: Applications
that demand some kind of session state persist beyond the duration of a single
request need to have that state sent back to the client as part of the response message. Next time the client wants to issue a request, the
state is again transferred to the service and then back to the client.
As noted by Roy, this is a trade-off. More network bandwidth is used in
order to achieve visibility, reliability, and scalability benefits for the
server side. Other REST constraints such as caching are intended to balance
this out so that an acceptable amount of bandwidth is used.
Deferred application state is often in the form of resource identifiers
such as URLs. This is an ideal form, because it can be stored indefinitely
and easily passed around between clients so other clients can continue the
the application at a later point in time.
Let's take a simple banking example:
So my client issues a GET request to
<http://accounts.example.com/myaccount/transactions>. In my response I get both
the current list of transactions, plus a "next" link. This hyperlink is a
reference to a resource whose identity incorporates where I am currently
up to, in this case it might be
<http://accounts.example.com/myaccount/transactions?dtstart=2009-06-11>.
This link allows me to unambiguously go back and fetch only new transactions,
yet the service doesn't have to remember where I was up to in the transaction
list. The dtstart identifier that marks the boundary between the transactions
I have seen and those I have not has been deferred back to me as part of the
returned message.
If the client is an accounting program, this will allow me to reconcile
my accounts with those of my bank in a nice unambiguous way. If I have to
restore my accounts from backup, the backup will have the old URL and will
query from the right place. If I lose my data completely I can go back to
using the original transaction URL. I can handle services being upgraded,
and individual servers being added or failing over. They don't need to know
where I was up to until I send my next request.
The gold standard of stateless interaction is that application state is
first reduced to its minimum possible set, then any remaining necessary
application state is stored with the client and not with the server.
A SOA take on Statelessness
If you read the excellent
Principles of Service Design
by Thomas Erl you will find statelessness as a key SOA principle. However,
the mechanisms described to support statelessness revolve around the use of
state databases on the server side. This is not statelessness from a REST
perspective, which would see the entire service including it state database
as "the server side". Deferring state within the service does not meet the
stringent REST criteria for this constraint. That is not to say these kinds
of databases aren't useful from time to time, but they have no place in a
strict REST architecture.
REST Statelessness in detail
Some people will tell you that the statelessness constraint can be worked
around by giving the session state a resource identifier on the server side.
Doing so certainly could improve the visibility of state, and its addressability
through hyperlinks. However, it does not address the core reliability and
scalability concerns that the REST constraint seeks to address.
There is a subtlety in the statelessness constraint that can make it
difficult to grasp. That subtlety is the difference between application state
(or session state) and service state. Application state is the state that is
built up as part of a client and server interacting in order to fulfil
application requirements. From a SOA perspective this might be seen as the
state of a service composition as opposed to the state of an individual service.
The difficulty is that you can't just point to state on the server side and
say that it must be service state. It could be application state. Certainly
taking application state and storing it on the server side does not magically
transform the state into service state.
At this point it is probably worth trying formalise the definition of
these two alternative forms of state. I think the best way to do this is
to say that service state is the state a service holds independently of the
state of any client, while application state is the state being held in some
kind of mirroring relationship with a client.
Let's take for example a straightforward PUT request. I generate some state
on the client, and through the PUT request I transfer that state (ie intent)
to the server side. If the server accepts my request it will update its service
state to match my intent. When it returns a response back to me as the client
I can forget this state. It has been transferred, and is no longer my
responsibility. It is service state, and not application state.
I can do an optimistic lock completely within REST constraints. Say
I get an ETag back from an initial GET request. I can now submit a conditional
PUT request to update that state only if the ETag matches what I saw in my
GET. This is stateless because while the server needs to remember that that
particular resource had that particular ETag and has to remember to update it
when its state changes, it doesn't really care about me. How many clients have
a reference to this ETag, and therefore hold an optimistic lock? None? One,
a thousand? The server doesn't need to keep track. It just has to honour the
interface of the conditional PUT request.
One more example of a communication pattern that can be done within the
RESTful statelessness constraint is the ability to grab a set of changes to
a resource rather than refetch the entire representation. This may at first
sound challenging, but consider a service that keeps a buffer of recent
changes. It might keep the last hundred changes, or the last hour's worth, or
use some other measure. Now, each time a client requests the set of changes
from a defined point it can return this set. If the client requests changes
that are too old, it can be directed back to the complete representation.
Again this is stateless. The server will store the same number of changes and
the same set of updates regardless of the number of clients. One? None?
A thousand? Again, the server's consumption of limited memory resources is
not affected by the number of clients that might be trying to stay synchronised
with its resource state.
Communication patterns prohibited: pub/sub and pessimistic lock
Two communication patterns that are excluded from a REST architecture
are pessimistic locking and the publish/subscribe pattern. These
are not stateless, and there are two immediate clues that tell us this.
Firstly, there is a current client expectation and state that is reflected
on the server side between requests. Secondly, the server will have a timeout
in both of these cases to deal with a client silently going away. Let's dig
into these scenarios a bit further.
A pessimistic lock requests that the service prevent modification of the
state behind a resource until the lock is released. This will typically involve
some kind of LOCK request being sent to the server, and once the lock is
created the client can go about its business before eventually submitting a
request with its new intent for the resource or resources. Pessimistic locking
is often needed more as systems scale to avoid the race conditions that can
result from an optimistic locking approach. Importantly, pessimistic locks are
typically the foundation of transactions both across multiple resources
exposed by a service and across multiple services. These transactions can
make the difference between a particular service composition being possible or
impossible.
Unfortunately, the lock maintained by the service demonstrates all of the
classic features of application state. The number of locks increases with the
number of concurrent clients. Each lock consumes server-side resources and in
this case also restricts concurrency between clients. Server-side state and
client-side state is synchronised, with both client and service having
knowledge of the lock between requests. Finally, the server side will always
have to maintain the lock in terms of a time-limited lease. If a client goes
away without releasing its lock, the server needs to eventually clean up the
lock resources or become unavailable. Giving a pessimistic lock a resource
identifier and allowing a DELETE request to be issued to it does not alter
these conditions.
Publish/subscribe carries a similar fate. If I SUBSCRIBE to a resource, I
create a subscription state object somewhere within the service that I as a
client depend on between requests. The server calling me back is nothing to
be concerned about, but the server-side subscription state is application
state. Again the number of subscriptions increases with the number of
concurrent clients, even between requests. Each subscription consumes
sever-side resources, server-side state and client-side state is synchronised
between requests, and subscriptions will always need to be obtained on a
time-limited lease to facilitate server-side cleanup.
Roy Fielding
explicitly
argues against the use of pub/sub on the Web,
so you can go and read about this trade-off in his own words.
Bending REST Statelessness
Of all the REST constraints, statelessness and the communication patterns
it prohibits grates most strongly at the enterprise scale. This isn't the
Internet, where hoards of clients can suddenly appear and knock your service
off the Web. There aren't enough clients to warrant extreme attention to
scalability. Existing enterprise systems exhibit the banned patterns, lending
credence to arguments that statelessness between requests is overly strict
in this context.
This argument has merit. REST was designed for the Web, and doesn't
particularly have the concerns of service compositions as its core objectives.
REST seeks to make things work on the big scale, and for that you need to
make some harsh decisions. On the Web it can make sense to require clients to
actively poll a server for new updates, rather than maintain subscriptions on
their behalf. The cost of extra messages and extra delay is often less than
the cost of maintaining per-client state on the sever side. The enterprise has
a different set of trade-offs that will often shift this balance the other way.
From a REST purist perspective we can tolerate stateful interactions only
within the boundaries of a single service or a single client. It is perfectly
OK to wrap up non-conforming interactions within a service boundary, so long
as the interface it exposes does not include these features. In the same way
as a WS-* interface can wrap up a legacy system for simpler and more unified
access, a REST interface can wrap up a set of WS-* services for simpler access
again and more scalable interactions on the larger scale.
Architects that choose instead to break the REST constraint may certainly
do so, but should not claim their architecture is RESTful. It might be REST
with some exceptions, and this "with exceptions" approach probably the right
for most enterprise architectures. Some exceptions even appear on the Web
itself. Bear in mind that by making exceptions reliability and scalability will
be impacted, but sufficient money thrown at these problems along with only
moderately large architectures will not yield too many adverse affects. As
always: Understand the consequences of your decisions, and think for yourself.
Conclusion
It is my opinion that constraints will be bent and specifications that bend
REST constraints will be more widely accepted as HTTP and REST thinking become
more integral parts of the Enterprise. HTTP in the future will have to serve
the dual masters of Web and Enterprise, and find the least perverse ways of
matching the two. We have already seen one attempt to fork HTTP for the
enterprise in the form of the Web Services stack. The lack of synergy between
the Web and WS-* has since rebounded back on its authors, and many of us are
back at this point of having to decide whether to embrace and extend HTTP or
to produce yet another alternative.
I tend to think that both the Web and the Enterprise will be well served by
a minimal set of extensions to HTTP that support some features currently
common at the enterprise scale. In particular, I see cross-service transactions
and publish/subscribe as important in some use cases. I would rather define
these in a way that breaks REST and is synergistic with HTTP than to start
again from scratch. I release that by adding non-compliant features to HTTP
we risk that these will leak out onto the web in a way that harms the features
of Web architecture, however I see this risk as moderate compared to the
potential benefit of bringing the Web and Enterprise into rough protocol
alignment. Whether it is the job of the w3c, IETF, or another body to build
these non-conformant synergistic HTTP extensions is not quite clear to me.
If this sort of work can't be housed within the IETF I suspect a new standards
body would have to be formed along the lines of OASIS.
Benjamin