I have a few different RESTful services that are hosted on different servers which use different DBs. I have a few RESTful services which call multiple such services above in what is supposed to be a transactional unit. We end up with data consistency issues if any of these RESTful services fail. Is there a neat architectural way of orchestrating a rollback? Or is having transaction managers the way to go?
As a simplistic example, RESTful service 1 has a POST request which reduces item count of thingamajig by 1. RESTful service 2 POSTs a payment. If service 2 fails, how can we cleanly implement a rollback on service 1, without having a new RESTful refund service (it is ok if this has to be the way to go). I am looking for an architectural answer to above issue, which is in keeping with REST principles.
Your answer: https://stackoverflow.com/a/1390393/607033 You cannot use transactions because by REST the client maintains the client state and the server maintains the resource state. So if you want the resource state to be maintained by the client then it is not REST, because it would violate the stateless constraint. Violating the stateless constraint usually causes bad scalability. In this case it will cause bad horizontal scalability because you have to sync ongoing transactions between the instances. So please, don't try to build multi-phase commits on top of REST services.
Possible solutions:
Another, probably the ugliest workaround to store every transaction as a resource, so you could POST commits and rollbacks. I think this possible solution is not viable, because it would violate the uniform interface constraint. We would usePOST /transactions/ {resource: "/forums/12/messages/45", method: "PUT", data: "..."}
andPOST /transactions/1/commit
instead of for examplePUT /forums/12/messages/45
Basically the problem is that you need to use transactions in an environment (HTTP) that by default is not transactional - in DB sense (because in HTTP a transaction is a successful request - response cycle).
The content of @leeor response is fully correct, what I'd like to add is how I'd solve the problem from the design site.
So you need a single endpoint, may be
/transactions
. ViaPOST
method you add a new transaction (with all the necessary details) that is immutable - after creation you can only ask for it's data/status viaGET
method. What can update the transaction status/data is the server itself only.Under the hood (during transaction creation) a snapshot (that could be reversed later on) for each resource taking part in the transaction should be created. Then execution for all the operations should begin and in case of any fail all the snapshots should be reversed. You do not mention any technologies so it's really hard to advise something reasonable. What you'd need for sure is comprehensive logging.
Distributed transactions are complex and require each participating system to support a notion of rollback. In the case of your services, they would each have to support some form of rollback. Co-ordinating something like this in a distributed system may not be practical or advisable in a synchronous way. In a situation like this, you would want to asynchronously roll back and the system would eventually reach consistency at a certain point in the future. There are obviously many other details (timeouts, error handling, retries, etc.).
For more details on eventual consistency, check out the wikipedia entry here.