Mark Lundquist wrote:
The application is a ticketing system. We have to reserve the tickets
in the database before we process the payment transaction so that we
don't oversell our tickets. So, reserving the tickets is
transactional (using Spring+Hibernate+MySQL), while the payment
processing is long but non-transactional. We have to know that we
will be able to issue the requested number of tickets, before we can
bill the customer's credit card.
There are other ways to handle this that I can think of, and they are
are all bad:
1) Process the payment first, with no guarantee of availability. Once
payment is secure, attempt to reserve the tickets. If the requested
number of tickets are no longer available, then run another
transaction to reverse the payment. I really don't like this idea...
2) Just do it all atomically within the transaction. This is bad for
two reasons. First of all, it has *really* poor concurrency
properties. Suppose you and I are both trying to purchase tickets at
the same time, and I win the race and make it into my transaction
first. Your transaction will then have to wait for as long as it
takes mine to complete — including the interaction with the payment
processor (Verisign) which takes a long time — before yours can even
get started, and then of course you will have to wait for your own
transaction to complete before you see any reply. The second reason
it is bad is that the design calls for a self-refreshing "please wait,
transaction in process yada yada" screen to be displayed while the
transaction is in process. This requires that the purchase processing
be done aysnchronously w.r.t. to the client request, and that is
impossible if it is all to be within a single database transaction,
due to lifetime issues with the Hibernate Session and Transaction
objects (specifically: the main thread would be closing the Hibernate
Session at the end of processing the HTTP request, while the secondary
thread is still in mid-transaction. A solution to that would be to
use the Hibernate "long session" pattern, but that's too invasive of a
change to make at this point. BTW you would not like that, you would
say it is "BAD BAD BAD"! :-) :-)
3) Use a delayed capture on the credit card, i.e. the first
transaction is an authorization that places a hold on the funds. Then
if the attempt to reserve tickets is successful, we follow up with the
capture transaction. If we weren't able to reserve the tickets after
all, then we don't. This is bad because for multiple reasons that I
don't have time to go into right now.
4) Periodically run a task that sweeps through the database,
"harvesting" tickets that were reserved but never committed.
Something like that *could* be implemented but endorses a bad design.
Well, once again I am most interested to hear just what it is that is
so bad about the design. You say "BAD, BAD, BAD" with no explanation,
so that might as well just be FUD, FUD, FUD!
Mark,
IIUC your system reserves the tickets and then charges the credit card.
If the payment isn't processed than you want to release the tickets.
What happens if your JVM happens to crash? Do you have a way of
completing (or rolling back) the transaction from another JVM?
The problem here is that you really do have a transaction that spans
both reserving the tickets and paying for them. It is understandable
that you don't want the "real" transaction to span all of that, but the
logical one does anyway.
Frankly, this is one case where I would look into using a workflow
engine to manage the full transaction. Most do a nice job of handling
all the issues involved and allowing you to specify rules and when to do
what and how. Unfortunately, most are oriented towards B2B stuff and
are heavily biased towards SOAP, which is probably unnecessary in your case.
Ralph
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]