Sound Advice - patterns

Tales from the homeworld

My current feeds

Published: Sat Nov 8 13:10:10 EST 2008

Updated: Sat Nov 15 17:43:18 EST 2008

Optimistic PUT Transaction

Intent

Safely modify a defined set of server-owned information when the starting state for that information is not known. The content is transmitted in a form the server understands.

Motivation

The PUT pattern is applicable when the clients wants to see the server reach a known state. It issues its idempotent PUT request repeatedly until the pattern either succeeds or fails. However, sometimes the target state that the client desires will depend on the starting state of the resource.

For example, a logical operation may require a vehicle's speed setting to be increased by five units. Simply PUTing a 5 to a speed resource won't work. It would set the speed to 5 only. Creating a speed increment resource and issuing a PUT to that resource would not work either: Several increment operations may be issued, and the idempotency of PUT requires that the increment only take effect once.

A simple solution in many cases is to apply an optimistic PUT transaction. This pattern allows the current state of the speed resource to be sampled before setting it to a higher value, and does so safely when a limited number of concurrent clients are attempting similar operations.

Applicability

An Optimistic PUT Transaction is appropriate when a client wants to update the information behind a known and predetermined URL based partially on its current content and partially on its own processing. A small number of concurrent clients can be supported, however the expected time to completion of the pattern may increase towards infinity when the number of active clients is high.

Structure

Optimistic PUT Transaction pattern structure

Participants

Client
  • Keeps a URL that lets it access the Server
  • Has a defined set of document types that contain enough information to sample, modify, and resubmit to the server.
  • Provides this list to the server as part of its GET request.
  • Retries and resubmits its GET as per the GET pattern
  • Transforms the document
  • Issues a PUT request with the transformed document, its type, the url, and the condition returned in the GETSuccess response (or DELETE if the transformed document is the null document).
  • Selects the most appropriate encoding based based on an initial guess. Subsequent requests are based on the on the supplied weighted acceptable types list if a Type Not Understood response is returned.
  • Retries and resubmits the PUT or DELETE as per the PUT pattern
  • Retries the whole pattern application if Condition Not Met is returned from the PUT request
  • Is responsible for overall successful execution of the operation, including modifications to the requests and resubmissions of the request
  • Treats a lost response as equivalent to a Resubmit response with no required changes
  • Aborts the operation on a failure response, on a resubmission response that cannot or will not be satisfied, or after too many retries.
Server
  • Checks that the type of the document and list of acceptable types is understood before performing significant processing
  • Selects the information to return and update based on the supplied URL
  • (optional) Is configured with mechanism to require the client to resubmit their request with or without modifications
  • Allows the PUT and GET to operate initially
  • Guarantees that the PUT and GET are safe to repeat. That is: Server updates its state to match a PUT initially. However, a PUT of the same state must not be interpreted as a request to modify state. It should return a Success response without any further action.
  • Returns a PUTSuccess response only once the information update can be considered permanent, allowing the client to forget it. The definition of permanent will depend on the possible consequences of client forgetfulness. It would typically range from "information updated on disk" to "information replicated to all sites, and stored to backup media" for important data.

Collaboration

Optimistic PUT Transaction Normal Case
  • Client issues requests to Server via the Request Interface, modifying and resubmitting its request as needed until:
    1. A success response is elicited
    2. The client is unable to make changes required by a Resubmit response
    3. Client policy prevents either changes required by a Resubmit response, or further resubmissions in general
Optimistic PUT Transaction Retry Case

Consequences

The Optimistic PUT Transaction pattern introduces a Uniform Interface for modifying server-side state. Clients and servers of different ages can communicate without impediment, and communication failures can be overcome. It partially fills a gap left by the PUT pattern for operations that are not logically idempotent, but only applies when the number of concurrent clients is bounded and sufficiently small.

Multiple required applications of the pattern may be queued in the client and executed in order. This pattern does not completely replace the previous state of the server-side resource, therefore subsequent requests cannot trivially be collapsed into one another. However, a well-understood sequence of transformations may be able to be logically combined to improve efficiency and reduce the risk of unbounded memory growth. For example, a sequence of additions and subtractions to a speed setting URL could be combined on the client side before being submitted at the first possible opportunity as an aggregate number to the Server.

Twin consequences of collapsing multiple transformations together are that interim transitions at URLs may never arrive at the Server, and that the architecture as a whole does not become overloaded as it is put under stress. Clients voluntarily discard intermediate states, so a server might have to make several internal transitions in order to catch up.

The flip-side of this behaviour is that servers are never stuck processing old data. They come completely up to date quickly. Many algorithms for real-time processing will behave better under this scenario than if they are fed through old requests. The pattern can be adapted to PUT to a new URL for each request for algorithms that are sensitive to discarding of interim states.

Implementation

PUT can be implemented with HTTP using the following mappings:

GET(url, weighted acceptable types list)
GET url HTTP/1.1
Accept: weighted acceptable types list
PUT(url, condition, document, type)
PUT url HTTP/1.1
Content-Type: type
If-Match: condition (entity tag)

document

DELETE (PUT null) is a DELETE request to the URL in HTTP

GETSuccess(document, type, condition)
HTTP/1.1 200 OK
Content-Type: type
ETag: condition (entity tag)

document

All 2xx series response codes can be treated as Success responses for GET.

PUTSuccess()
HTTP/1.1 200 OK

All 2xx series response codes can be treated as Success responses for PUT. If the request was a DELETE, then 404 Not Found and 410 Gone are also treated as Success codes.

Fail(reason)
HTTP/1.1 400 Bad Request

reason

Unknown 1xx series response codes can be treated as a Fail for PUT and GET. 3xx series codes that are not understood should be treated as Fail. 4xx series response codes are Fail, except for 401 Unauthorised and 407 Proxy Authentication Required. These are Resubmit responses and should only be treated as failures if they are not understood. 404 Not Found and 410 Gone are excluded from the failed codes list for DELETE requests, as they may be returned as the result of a duplicate request that has already succeeded. 5xx series responses should be treated as Fail, except for 503 Service Unavailable and 504 Gateway Timeout. These are Resubmit and Response Lost responses, respectively.

Resubmit(required changes)

Any of: 301 Moved Permanently, 302 Found, 303 See Other, 305 Use Proxy, 307 Temporary Redirect, 401 Unauthorized, or 407 Proxy Authentication Required.

Response Lost()

Any loss of communication before a response is received. This may include application or TCP/IP level timeouts, or an explicitly terminated connection. The 504 Gateway Timeout response is also equivalent to Response Lost, and indicates a loss occurred somewhere past the TCP connection made directly by the client.

Sample Code

url="http://example.com/publication-date/first-edition"
latest_update = 5; // metres per second
transform[url] += latest_update; // Fold transformations together
if (blocked())
{
	// Record that we still need to send a request. We have already
	// folded our transformations together, above.
	request_pending(url)
}
else
{
	try 
	{
	retry_get:
		(document, type, condition) = GET(url, "text/plain")
	}
	catch (...)
	{
		// etc
	}

	resource_state = decode(document, type);

	// Transformation made
	resource_state += transform[url]

	newdocument = encode(resource_state, type);

	try
	{
		PUT(url, condition, newdocument, type)
	}
	catch (...)
	{
		// etc
	}
}

Known Uses

Related Patterns