One of the interesting thing about credit cards is that actions performed on your account are not operations, but instead modeled as things that are first related to your account, then that actually change the state of your account. When you buy something at a store online or in person, you are not actually manipulating your account directly. A credit card purchase is converted into a thing: specifically a Debit. A refund of a purchase is converted into a thing called a Credit. This credit or debit is logged at Visa or Mastercard. Once a week (i forget the frequency), a company like Capital One (I used to work for them in the mid-90s) receives a huge magnetic tap roll with all the transactions that are applicable to Capital One’s accounts. Capital One then pulls these individual credits and debits and matches them with a specific credit card account. Finally it processes the debits and credits for a account to calculate a final balance.
The key thing to take from this is that credit card transactions are not operations, but actual things. Another similar construct is an Airline Reservation. A reservation is very similar to a credit or debit. It is a representation of the final thing you want to do. Create a ticket on an airplane.
Where REST and Transactions meet
I think if we constrain ourselves to modeling transactions as things instead that are themselves resources REST and Transactions can co-exist. So, what transaction processing becomes in RESTful terms is relating one resource to another. For Credit Cards, transaction processing is the result of relating Debit and Credit resources with Credt Card Account resources.
I think the wrong way to merge REST and Transactions is the way the O’Reilly RESTful Web Services book suggests. In the book, the authors talk about manipulating an account balance by creating a parallel resource of an account as a subresource of a transaction object:
The transaction sub resource is manipulated with representations of the account. At “commit” time, the “real” account is updated with a new representation of the account. The funny thing is, as I talked about earlier, the real world doesn’t work in this manner. This idea that a RESTful Transaction must work in the same way you interact (with JDBC or something) with your database and that uncommitted state modifications to a resource must be viewable just isn’t true and not really feasible, and it is just not RESTful.
Where do Transaction Managers fit in?
A Transaction Manager is a generic service whose sole purpose is to ensure that a distributed system is in a consistent state. It does this by coordinating actions between various things on the network. I think if we model actions as things and model transaction processing as the action of relating one or more resource to one or more other resources, Transactions, Transaction Managers, and REST can all fit together. To illustrate this let’s model a Travel Agent transaction where the Travel Agent is reserving a flight and a room within a hotel. While there are many different transactional protocols, we’ll model the protocol that RESTafarians deem the most-unRESTful, 2-Phase-Commit.
The resources in our example will be: Reservation, Ticket, Room, Transaction-State, Transaction, and Transaction Manager. Let’s walk through on how these resources are created. The end product of these interactions will be a Ticket and Room.
1. Travel Agent creates a transaction
The Travel Agent interacts withte Transaction Manager resource to create a transaction resource. For example:
POST /transaction-manager Content-Type: application/tx+xml <transaction timeout="1 minute"/>
The Transaction Manager responds with:
HTTP/1.1 200, OK Content-Type application/tx+xml Location: /transaction-manager/transactions/11 <transaction timeout="11" state="uncommitted"> <link rel="participants" href="/transaction-manager/transactions/11/participants"/> </transaction>
2. The Travel Agent makes an Airline Reservation
POST /airline/reservations Content-Type: application/airline-rsv+xml <reservation> <date>...</date> <flight>...</flight> <seat>...</seat> <link rel="tranasaction" href="/transaction-manager/transactions/11" type="application/tx+xml"/> </reservation>
Here the client is creating the reservation. Notice that the reservation created is related to a transaction. The server responds with:
HTTP/1.1 200, OK Location: /airline/reservations/222 Content-Type: application/airline-res+xml <reservation> <date>...</date> <flight>...</flight> <seat>...</seat> <link rel="transaction" href="/transaction-manager/transactions/11" type="application/tx+xml"/> <link rel="transaction-state" href="/airline/reservations/22/transaction-state"/ type="application/tx-state+xml"/> </reservation>
Notice that a new relationship is defined on the Reservation: “transaction-state”. This is the state of the reservation. Another thing to note is that this representation returned with the response of the POST would be the same representation returned if you did a GET /airline/reservations/22.
3. Register Transactional State With Transaction
IMO, it doesn’t matter who relates the Reservation’s Transactional State resource with the Transaction resource. Both the Travel Agent and the Reservation have the links needed to perform the operation. Whoever does this, the operation might look like this:
POST /transaction-manager/transactions/11/participants Content-Type: uri-list http:.../airline/reservations/22/transaction-state
Another way to do this might be to use Link headers and the HTTP LINK operation (don’t quote me on how exactly to do this!):
LINK /transaction-manager/transactions/11/participants Link: <http:.../airline/reservations/22/transaction-state>; rel="airline-reservation"; type="application/tx-state+xml"
4. Travel Agent Creates Hotel Room Reservation
The Hotel reservation works in the same way as the Airline Reservation, so I don’t want to burden you with reading duplicate interactions🙂
5. Client Commits the Transaction
To Commit the transaction, the client simply POSTs to the Transaction resource
POST /transaction-manager/transactions/11 Content-Type: application/tx+xml <transaction state="committed"/>
This would block until the Transaction Manager has coordinated all services. Or, it could return a 202, Accepted and the client could check back later to see if the state changed or not.
6. Transaction Manager Prepares
The particpants of the transaction have been registered. The TM initiates the “Prepare” phase of the 2pc protocol by trying to change the state of each partcipants Transaction-State resource to “prepared”, i.e.:
PUT /airline/reservations/22/transaction-state Content-Type: application/tx-state+xml <transactional-state state="prepared"/>
What’s happening behind the scenes? IMO, it doesn’t really matter. Maybe we’re locking up database resources, maybe we’re just checking to see if it is still possible to purchase a ticket for that flight.
7. Commit each participant
Now that all the participants are in prepare mode, the TM will guarantee that each participant commits by logging the vote to its transaction log. If any body fails, ( a crash or something), including the Tranactaion Manager, the TM can recover and put things in a consistent state, or if, it can’t, notify a human that it must do something. This is what a transaction maanger was designed for. Reliability. So, the TM changes the state of each particpant to COMMIT. i.e.
PUT /airline/reservations/22/transaction-state Content-Type: application/tx-state+xml <transactional-state state="committed"/>
8. Retrieve the Ticket and Room
In this example system, the act of committing the reservation will create a Ticket or Room behind the scenes. So, now, when the Travel Agent does a GET on the Airline or Hotel Reservation, a new link will pop up that points to a Ticket or Hotel Resource:
GET /airline/reservations/22 Content-Type: application/airline-res+xml <reservation> ... <link rel="ticket" href="/tickets/333" type="application/ticket+xml"/> </reservation>
An interesting thing to note about this example is that when the interaction completes, there is a complete log of who participated within the transaction through the use of link relationships.
Finally, the point of this blog was not to promote the use of 2PC or Transaction Managers, but to show that it may be possible to model transactions RESTfully by turning actions into things and modeling transaction processing by relating one resource to another. This is how I envision REST-*.org defining the relationship with REST and Transactions. If you want to discuss this more, ping us on our working group.