May 20,2000 -

More free code from father@bigattichouse.com!

Anyway, so here I am exploring transactions.  If you have downloaded my toadbase components you'll realize I've implimented transactions there.. where any action you take, to insert, update or delete is sort of "staged" under a transactionid you provide.. this allows you to even create your own personal transaction log, to ensure that what you are doing is atomic and removed from everyone else.. no bumping into each other.

Here the idea is similar to what I think MTS was shooting for, but the VB programmers don't grasp (at least not in the fortune 500 company I was involved with!)  That you can create an object that can "do" something in a transaction, and be controlled, rolled back, etc from outside that transaction.

A quick primer: 

Imagine you work in a bank (its always banks isn't it!) and you have to transfer funds from Account#1, to Account #2 .. Ok.. so if you just go and delete $50 from #1 and go put it in #2, sounds easy, right? OK... What happens if you deduct $50 from #1 right as your 4 year old asks "What does this button do?" and turns off the bank's computer... now #1 is out $50 and #2 never got it.. poof, money went where?  So what you need is a way to know that EVERYONE is ABSOLUTELY ready to go, but not at the point of no return.. then you hit them with a "COMMIT!", and everything is cool.  So, when I spill coffee on the computer, the $50 was "earmarked" for moving, but hadn't gone, and $50 was ready to go into #2.. The computer quit AND... drumroll .. it never happened.. the transaction never completed so it gets "forgotten", or better yet, there is a log of all transactions about to occur, and I can recover to the exact moment the computer quit!


Ok.. now to MTS, MTS works this way and has a nice thing that controls transactions. Unfortunately MTS gets used as a nice way to make Threadsafe components for VB. It happens, someone takes a minor feature and makes it "The Feature"...  The idea was that you build a component, set it up so that it can participate in a transaction, and it can be rolled back if necessary.. so your "Deposit" Component can participate nicely with the "WithDrawal" component... all you write is the ONE component at a time.

So.. WHy not use MTS and Delphi... I don't know, probably the same reason I wrote the PSP stuff and found that I could create MemoryMapped files for fast threadfriendly storage!...  I am a nerd, thats why.

So ANYWAY, I came up with TTransactable.  It is a nice ancestor that allows you to create an object that can be part of a larger transaction (TComponentTransaction). You can take two components (like Withdrawal and Deposit Components), add them to a TComponentTransaction and have them all play together when you tell them to Execute.  If any errors occur then ALL the components rollback.  Heres the cool part. TComponentTransaction is ALSO a TTransactable, so you can make huge chains or trees of transactions...

My idea is very similar to an MTS transaction, except that they put years into it, and I put about 8 hours.. so take my code lightly, I'll try and help out if you are trying something more robust with it, but I would'nt run a bank on it just yet ;)  

I also included TADOtransaction, which allows you to create a SQL statement and a database connection that can participate in a transaction.  Sample is provided, I recommend walking through with F7 to watch how it works. It would also be a good idea to try making an "Accounts" table in a database and trying to make TADOComponents that deduct or credit accounts and try the example above.. the ADO objects begin transactions when you tell them, and don't commit until explicitly told.

The biggest limit here is that there is the possibility of failure during the brief time between each component's commission...Say that all of my ADO SQL calls have gone through and I tell the ComponentTransaction to commit, there are <20 ms there while I look through the list of objects and tellthem to commit, once committed they are permanent...   MTS uses a UDP-type broadcast or windows messaging to say "GO!".. so that time period is pretty short.. they also will rollback an object if it sits in the "ready" state too long (the "I've executed and am ready to commit!").. and will roll them back. 

But I guess there are risks with everything... and you end up chasing your tail with "how do I get all of these transactions to know when to commit..". At worst you could add a Post verification and have specific code to UNDO a record.. delete the last inserted record, etc.. but how do you know that everyone UNDID their work.. see it just keeps going.  You can hope you have the uncertainty down to a "well.. we can kinda trust that".. 
But having some security is better than none.


DISCLAIMER: the following is just an exploration of a thought, and hasn't been tried yet.
Now, My idea is to store transaction data, the Published fields, in Toadbase tables to create that Buffer mentioned before. create a Table and store the transaction particulars as XML created by QuickRTTI (more of my software available on my site), then add the deletion of that log AS A TRANSACTION COMPONENT... but have it clear the transaction on Rollback AND commit. This way you can recover the state of the components via QuickRTTI/XML and get the whole transaction back into play if something happened.  Turn off the machine in the middle of a transaction, and you can restore back to the last state. COOL huh! If I play my cards right I could build the function INTO a TComponentTransaction, and it would go ahead and do it ANYWAY, imagine having complete transaction recovery built into a component, all for the price of using T[something] = class(TTransactable)!


The big thing to remember is that you are implimenting the entire transaction:
PREPARE: What do you need to do before someone says for you to being a transaction.. like opening a database
BEGINTRANSACTION: Start your work, tell the database to begin a transaction, whatever..
EXECUTE: Do what you need to do, like execute a SQL statement. use a TRY..EXCEPT to call RollBack on a failure.
COMMIT: When things go well , your creator will call "COMMIT".. any failure, call roll back.
ROLLBACK: Your creator will call this, OR it will be called if someones' execution failed.

ADOTransaction demonstrates this pretty well... since databases have had this sort of this down for a while, ADO kinda fits the model pretty easy.. just remember that you are making A LOT of database connections if you use ADOTranaction, and you need to Pool Connections if allowed.

With my old Interbase 4.2 DB and a COmponentTransaction of 10 recors happens within a few seconds... 1000 records, committing, takes about 