Research
University of Newcastle
Return to Research List
- Contact:
Dr. Mark Little
Arjuna Project
Distributed Systems Research
Department of Computing Science,
University of Newcastle
Tyne, UK, NE1 7RU
m.c.little@newcastle.ac.uk
Providing
end-to-end transactional Web applications using the Object Transaction
Service
1.
Introduction
The Web frequently suffers from failures which can
affect both the performance and consistency of applications running over
it. For example, if a user purchases a cookie granting access to a
newspaper site, it is important that the cookie is delivered and stored
if the user’s account is debited; a failure could prevent either from
occurring, and leave the system in an inconsistent state. For resources
such as documents, failures may simply be annoying to users; for
commercial services, they can result in loss of revenue and credibility.
Atomic transactions, with their
“all-or-nothing” property, are a well-known technique for
guaranteeing application consistency in the presence of failures.
Although Web applications exist which offer transactional guarantees to
users, these guarantees only extend to resources used at Web servers, or
between servers; clients (browsers) are not included, despite their role
being significant in applications such as mentioned previously.
Providing end-to-end transactional
integrity between the browser
and the application (server) is therefore important, as it will allow
work involving both the
browser and the server to be atomic. However, current techniques based
on cgi-scripts cannot provide end-to-end guarantees [1]. As illustrated
in figure 1 the user selects a URL which references a cgi-script on a
Web server (message 1), which then performs the transaction and returns
a response to the browser (message 2) after
the transaction has completed [2]. Returning the message during the
transaction is incorrect since it may not be able to commit the changes.
Figure
1: transactions through cgi-scripts
In a failure free environment, this mechanism works
well. However, in the presence of failures it is possible for message 2
to be lost between the server and the browser, resulting in work at the
server not being atomic with respect to any browser related work.
With the advent of Java it is possible to empower
browsers so that they can fully participate within transactional
applications. To be widely applicable any such transaction system must
be standards compliant. The most widely accepted standard for
distributed transactions is the Object Transaction Service (OTS) [3].
However, rather than require a browser to incorporate a full OTS
implementation for all applications, we are interested in the case where
it would be considered either undesirable or impractical, e.g., because
of the overhead and security implications involved in incorporating the
required CORBA services within a browser. In addition, we expect that
the majority of an application’s resources, particularly in the area
of electronic commerce, will continue to reside within protected
domains.
2.
End-to-end transactional guarantees
Our aim was to allow the construction of
transactional applications with end-to-end guarantees without requiring
(substantial) support from the browser; the level of browser support
will depend upon the application, but should be minimal, e.g., simply
writing a cookie to the user’s disk. Figure 2 shows a lightweight
transactional bank account: apart from necessary persistence mechanisms,
the transaction and concurrency control services are all managed for the
browser by the server; the cookie object still participates within any
transactions involving the bank, and therefore the transaction manager
guarantees that its state changes are transactional.
Figure
2: lightweight transactional browser
In the following sections we shall describe how we
have provided this lightweight transactional mechanism using the OTS.
2.1
Browser resources
The OTS specification allows transactional objects
to delegate their ACID responsibilities to recoverable objects (those
which support the Resource
interface defined in the CosTransactions
module); through this technique it is possible to encapsulate
non-transactional resources within OTS Resources,
such that they assume the responsibility of ensuring the object is
transactional. Therefore, the solution we chose to the problem of
providing lightweight end-to-end transactional guarantees is through the
use of Resource
implementations which “wrap” the non-transactional browser. These Resources
are registered with, and driven by, the transaction manager at the
server, and work with the browser-side application to make it
transactional. This allows the browser portion of the application to be
extremely simple and lightweight.
Although this browser Resource
will typically be specific to the application for which it was created,
the functional requirements all Resources
must fulfil will be the same:
·
prepare: the Resource transmits to
the browser application the identity of the transaction it has been
registered with, which can be used to replay the transaction in the
event of a failure. If the Resource passes any
results back to the browser application (e.g., a cookie) it should not
use them at this stage, since the transaction has not terminated. If the
browser does not respond to this message, the server-side Resource
may assume it has failed and cause the transaction to abort (by
returning VoteRollback from
prepare.) If the transaction aborts, all
of the other work performed within its scope will be undone.
·
commit: the
application is guaranteed that all of the work performed within the
transaction will be made durable, despite failures. Crash recovery
mechanisms can be relied upon to complete partial transactions arising
due to failures. Therefore, the Resource can make any
“state changes” permanent, e.g., by instructing the browser to store
the cookie, or display/decrypt the purchased document. If the Resource
does not reply to the commit request then the transaction manager will
assume it has crashed and crash recovery will complete the transaction.
·
rollback: the Resource transmits the
reason for the rollback to the browser, e.g., insufficient funds within
the user’s account.
This technique provides a lightweight means for Web
applications to gain end-to-end transactional integrity. The transaction
coordinator, and the majority of the transaction infrastructure, reside
within the server, while the browser application remains relatively
simple, needing only to be able to make any “changes” permanent in
the case of a commit, or to undo them if the action aborts.
3.
Overview of an OTS implementation
We have designed and implemented OTSArjuna, a C++ transaction toolkit which complies with the OTS
specification (including supporting subtransactions), and which provides
programmers with the ability to implement Web applications with
end-to-end transactional guarantees [4]. This OTS implementation is
extremely portable, and runs on over 7 different ORBs. However, in the
on-line example to be described later we have used Orbix 2.2MT from
Iona.
Rather than require programmers to make use of the
low-level OTS, concurrency control and persistence APIs, OTSArjuna
provides a high-level API for building transactional applications and
frameworks [5]. This API automates the activities concerned with
participating within an OTS transaction, such as creating and
registering appropriate Resource
implementations, propagating locks etc. The architecture of the system
is shown in figure 3.
Figure
3: OTSArjuna structure
The OTSArjuna model for building transactional
applications exploits object-oriented techniques to present programmers
with a toolkit of classes from which application classes can inherit to
obtain desired properties, such as persistence and concurrency control.
Each class is concerned with a single functionality, and these classes
form a hierarchy, part of which is shown in figure 4. By inheriting from
LockManager,
user classes are automatically transactional, with LockManager
and StateManager being
responsible for guaranteeing the ACID properties (isolation and
durability respecitvely). Apart from specifying the scopes of
transactions, and setting appropriate locks within objects, the
application programmer does not have any other responsibilities: the
system guarantees that appropriate Resource objects are
registered with transactions, and that in the event of failures crash
recovery mechanisms are invoked automatically.
Figure
4: OTSArjuna class hierarchy
4.
Bank cashpoint example
To illustrate the technique of incorporating
browsers into transactions, we have implemented a bank account example,
which is available on-line (http://arjuna.ncl.ac.uk/). Consider the case
of an on-line bank which allows users to inspect, remove, and insert
money from their accounts; the bank runs on an ORB which resides on a
Web server. When money is removed it is converted into digital
cash tokens which are stored and manipulated by the browser, and
contain the token’s current “cash” balance; we shall assume that
these tokens can be presented to other Web-commerce applications as
payment for services. Insertion of money to an account is simply the
reverse, whereby a token is consumed, and the user’s account is
credited by the amount left within the token. The bank requires to make
the delivery of the cash token to the user’s browser and the debiting of the user’s account atomic. Failures must not
result in inconsistencies at the browser or the bank.
Figure 5 shows the browser portion of the
application, consisting of a Java applet which displays a graphical
representation of the bank; user accounts are accessed via a PIN. To
guarantee consistency in the presence of failures, each operation is
performed within the scope of a transaction; the transaction manager
runs at the bank’s Web server. Although the operation to inspect an
account is transactional, it only reads the state of the account and
therefore there is no actual requirement for end-to-end transactional
integrity: if a failure occurs, the user can simply re-issue the
request. However, both withdrawing and inserting money require stronger
transactional guarantees: each operation must atomically modify the
account and either deposit or
consume a digital cash token to/from the browser.
During the prepare phase of each transaction the
applet will display the unique transaction identifier for the operation.
During the commit phase, the bank server will return the digital cash
token to the browser and the applet can then use this to animate (e.g.,
money appears to be dispensed). In a real system the token would need to
be stored by the browser. If the transaction aborts, an error message
will be displayed giving the reason, e.g., insufficient funds.
Figure
5: Bank applet.
4.1
Server application classes
To enable the bank to service multiple clients
concurrently, each account is a separate transactional object, responsible
for its own persistence and concurrency control. Therefore, concurrent
modifications to different bank accounts will not interfere. Using
OTSArjuna, each bank account is an instance of the Account
class, which is derived from LockManager:
enum Outcome { DONE, NOTDONE, INSUFFICIENT_FUNDS, ACCOUNT_ERROR, LOCKED };
class Account : public LockManager
{
public:
Account ();
virtual ~Account ();
void insert (int amount);
void withdraw (int amount);
void inspect (int&
amount);
private:
int amount;
};
We shall first
show the implementation of the withdraw
method without browser participation. We will then describe the necessary
browser Resource for end-to-end
transactional guarantees, and return to the withdraw
operation to show how it requires modification in order to incorporate
this Resource.
The withdraw
method first starts a new transaction and then attempts to obtain a lock
on the account object (by calling the setlock method
of the LockManager class). Because the
withdraw
operation will modify the state of the account, it tries to obtain an
exclusive (write) lock. If the operation is performed successfully (e.g.,
there is sufficient funds in the account), the transaction is committed,
otherwise it is rolled back. (Note: for simplicity the code fragments
shown do not perform all necessary error checking.)
void
Account::withdraw (int money)
{
Outcome result =
NOTDONE;
OTS::get_current().begin();
if (setlock(new
Lock(WRITE)) == GRANTED)
{
/*
*
Check whether the user has sufficient
*
money to withdraw.
*/
if
(amount >= money)
{
amount = amount - money;
result = DONE;
}
else
result = INSUFFICIENT_FUNDS;
}
else
result
= LOCKED;
if (result ==
DONE)
{
OTS::get_current().commit(TRUE);
}
else
OTS::get_current().rollback();
}
In order to
involve the browser such that it can receive the cookie when the user’s
account is finally debited, we need to create an instance of a specific Resource
which will encapsulate the browser for the duration of the transaction. The
signature of the BrowserResource is
shown below.
class BrowserResource
: public CosTransactions::ResourceBOAImpl
{
public:
BrowserResource (CosTransactions::otid_t
tran, int outcome, CashToken& amount);
virtual ~BrowserResource ();
CosTransactions::Vote prepare
();
void rollback ();
void commit ();
void forget ();
void commit_one_phase ();
private:
Boolean saveState ();
Boolean restoreState ();
void removeState ();
int opOutcome;
CashToken cashAmount;
CosTransactions::otid_t tid;
Browser applet;
// our handle on the browser side of the application.
};
When created,
each BrowserResource
is given the identity of the transaction it has been registered with, so
that it can transmit it to the browser during the prepare phase; this can
be used in the event of a failure. The Resource
also receives the outcome of the operation and the cash token to send back
to the browser. The browser applet is represented to the resource by an
instance of the Browser class;
this is simply a traditional client proxy for a corresponding object
within the browser.
To make the
browser transactional, the BrowserResource must keep a
durable record of its progress; this may then be used by crash recovery in
the event of a failure. For example, consider the implementation of the prepare
method:
CosTransactions::Vote
BrowserResource::prepare ()
{
if (!saveState())
return
CosTransactions::VoteRollback;
// invoke operation on
browser
if (applet->prepare(tid))
return
CosTransactions::VoteCommit;
else
{
// Failure -
discard Resource’s state
removeState();
return
CosTransactions::VoteRollback;
}
}
Before the BrowserResource
sends the transaction’s identity it calls saveState
which will permanent sufficient information to allow its role in the
transaction to be replayed in the event of a failure. If saveState
fails, the transaction must abort. If the subsequent invocation on the
browser proxy fails, the resource assumes that the browser has failed,
removes any state it previously saved, and forces the transaction to
rollback. If no errors occur during prepare,
the resource informs the transaction coordinator that it is ready to
commit. During the commit phase, the Resource sends the
token to the browser and awaits an acknowledgement. If no acknowledgement
is received, the Resource relies upon
crash recovery mechanisms to complete its work.
void BrowserResource::commit ()
{
// browser acknowledged
receipt
if (applet->commit(cashAmount))
{
removeState();
}
else
{
// Some failure
has occurred (browser, network).
// Crash
recovery will have to deal with this.
}
}
In the event of a
rollback the resource will send an error message to the browser indicating
the reason for the failure.
void
BrowserResource::rollback ()
{
removeState();
/*
* Send
reason for failure. If this fails, the
* browser
will try again later, and be told
* the
transaction rolled back.
*/
applet->rollback(reason);
}
If a failure occurs during the transaction, the user
can re-issue the request by supplying the transaction identifier. The
crash recovery mechanisms at the server will determine the transaction
outcome and complete the work. If no record of the transaction can be
found it can be assumed that the transaction aborted.
Having considered
the implementation of the BrowserResource, we can return to the withdraw
operation. We wish to incorporate the browser within the transaction before
it terminates, such that the browser will be informed of the outcome
regardless of whether the transaction subsequently commits or rolls back.
Therefore, after performing withdrawal we obtain a reference to the
current transaction’s Coordinator,
and register a new instance of the BrowserResource
with it. The transaction then either commits or aborts, depending upon
whether the withdrawal was successful, and drives the BrowserResource
to either deliver a token or an error message, respectively. If a failure
occurs, the user’s account will not be debited and
neither will the browser obtain a cash token.
void
Account::withdraw (int money)
{
Outcome result = NOTDONE;
OTS::get_current().begin();
if (setlock(new Lock(WRITE),
0) == GRANTED)
{
if (amount >=
money)
{
amount
= amount - money;
result
= DONE;
}
else
result
= INSUFFICIENT_FUNDS;
}
else
result = LOCKED;
/*
* Now involve the
browser within the transaction.
*/
Control_ptr cont =
OTS::get_current().get_control();
Coordinator_ptr coord =
cont->get_coordinator();
BrowserResource res =
new BrowserResource(OTS::get_current(),
result, money);
coord->register_resource(res);
if (result == DONE)
{
// automatically
aborts if it cannot commit
OTS::get_current().commit(TRUE);
}
else
{
// undo all work
performed
OTS::get_current().rollback();
}
}
Acknowledgements
The work reported here has been supported in part by
a grant from UK Engineering and Physical Sciences Research Council (grant
no. GR/L 73708).
References
[1]
T. Sanfilippo and D. Weisman, “Applications of the Secure Web
Technology in Transaction Processing Systems”, The Open Group Research
Institute, November 1996.
[2]
“Transarc DE-Light Web Client Technical Description”, Transarc
Corporation, February 1996.
[3]
“CORBAservices: Common Object Services Specification”, OMG
Document Number 95-3-31, March 1995.
[4]
M. C. Little and S. K. Shrivastava, “Java Transactions for the
Internet”, Proceedings of the 4th Usenix Conference on
Object-Oriented Technologies and Systems, Santa Fe, New Mexico, April
1998.
[5]
M. C. Little and S. K. Shrivastava, “Distributed Transactions in
Java”, Proceedings of the 7th International Workshop on High
Performance Transaction Systems, September 1997, pp. 151-155.
|