Sound Advice - patterns

Tales from the homeworld

My current feeds

Published: Sat Nov 15 17:41:47 EST 2008

Updated: Sat Nov 15 17:41:47 EST 2008

Pessimistic PUT Transaction

Intent

Safely modify a defined set of server-owned information when the starting state for that information is not known and the likelihood of interference during a transaction is relatively high. 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 pessimistic PUT transaction. This pattern allows the current state of the speed resource to be sampled before setting it to a higher value, and locks the resource while a transformation is applied to the document and the result is resubmitted.

Applicability

An Pessimistic 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. Concurrent clients can be supported, however a failed client may lead to inefficient processing.

Structure

Pessimistic 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 lock_and_GET request.
  • Retries and resubmits its lock_and_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
  • Submits the UNLOCK request after the PUT completes
  • 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 GET_and_lock, PUT, and UNLOCK to operate initially
  • Guarantees that the GET_and_unlock, PUT, and UNLOCK 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. The UNLOCK operation should return Success if the identified lock is not currently held.
  • 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.
  • Times out the lock if an UNLOCK has not been received, allowing other clients or a restarted instance of the same client to continue operating.

Collaboration

Pessimistic 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
Pessimistic PUT Transaction Retry Case

Consequences

The Pessimistic 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

Pessimistic PUT Transaction can be implemented with HTTP and WebDAV extensions using the following mappings:

lock_and_GET(url, weighted acceptable types list)
LOCK url HTTP/1.1
Timeout: expected operation time

And as a separate request:

GET url HTTP/1.1
Accept: weighted acceptable types list
PUT(url, lock id, document, type)
PUT url HTTP/1.1
Content-Type: type
If: lock id

document

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

UNLOCK(url, condition, document, type)
UNLOCK url HTTP/1.1
Lock-Token: lock i
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.

UNLOCKSuccess()
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.

Lock Identifier Mismatch()
HTTP/1.1 412 Precondition Failed
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