Flat transactions are all-or-nothing affairs. There are situations, however, where an application can make progress on a business function even without being able to complete it. The option of backing out of all of the work may be too expensive. There may be some work that is meaningful, even if other parts of the request need to be aborted.
For example, if a customer requests that you book his flight and debit his account to pay for it, the customer service representative may need to gather a great deal of information about the flight before debiting the payment account. He may gather the customer's name, preferred airline and departure time for example. If, after all this information has been gathered, the customer decides to wait on payment until his supervisor approves the flight, it would be better if the system could preserve the information, even if it can't complete the transaction.
One solution is to define a hierarchy of transactions, parallel to the hierarchy of business actions. A set of rules could be defined to allow sub-transactions to commit or abort and have the desired affect on the parent transaction and sibling sub-transactions. For example, the data being manipulated by a sub-transaction is not available to its siblings until it commits. A parent transaction cannot commit until all of its children have committed.
This can be implemented using savepoints. A savepoint is a point in a program where the state of the program itself and all of the application's objects are saved to persistent store. This is similar to a checkpoint in a database; in a checkpoint the state of the database is stored, in a savepoint the state of the running system is stored. Savepoints, are expensive to implement, but can be used to back out of part of a transaction.
This can be useful if some error path should be taken, for example, to issue an alarm instead of completing the normal business function. Queue-based systems will normally rerun a transaction that fails. In this case, we want to commit the transaction after running the error-handling code. This can be done by declaring a savepoint near the beginning of the transaction, but after the request is obtained. The exception-handling code can then return to the savepoint, raise the alarm and commit the transaction.
Savepoints can also be used to implement nested transactions if sub-transactions cannot run in parallel. Then, when a sub-transaction is run, the first time it uses any resource manager it issues a savepoint for that resource. If that sub-transaction aborts, you ask each resource it used to return to the savepoint. This effectively aborts just the effects of the one sub-transaction.
In transactions, permanent stores are known as resources, and the software that manages a resource is called a resource manager.