One of the very nice features of GemStone is that you don’t have to do anything special to take advantage of persistence. With other smalltalk dialects (Squeak, VW, VA, and Dolphin) objects that are referenced by the global Smalltalk will be ‘kept alive’ in the image. With GemStone, any objects referenced by a persistent root (I’ll explain why I didn’t say ‘Smalltalk’ here in a later post) will be ‘kept alive’ and ‘automatically’ saved in the data base.
In a generic GemStone application you do have to explicily manage transaction boundaries using the following methods:
- System class>>beginTransaction
- System class>>commitTransaction
- System class>>abortTransaction.
For the GemStone port of Seaside, we have embedded the transaction boundaries in the Seaside framework, so you don’t have to change your application at all – a beginTransaction is performed when the request comes in from the http server and a commitTransaction is performed right before the response is shipped back out to the http server.
This means that while your Seaside application is responding to the request, you have an up-to-date ‘view’ of the object graph and any changes that you make to persistent objects will be committed to the data base, without any special coding on your part.
[Update 3/17/2008]
As you scale your application you will eventually be adding more Seaside vm’s to serve your pages and when you do, you will then need to worry about transaction conflicts. I’ll cover transaction conflict avoidance in a later post.
With recent modifications to the GemStone port of Seaside, it isn’t absolutely neccessary to avoid transaction conflicts. For most applications a Simple Persistence model is perfectly adequate.
11 comments
Comments feed for this article
May 7, 2007 at 12:08 pm
Boris Popov
Cool, keep the posts coming, I’m personally very curious what the pattern for handling commit failures on the web might look like.
May 8, 2007 at 12:19 am
Ian Prince
Hi, this seems to mirror the way transactions are handled in Zope+ZODB, which I found to be very useful in rapid prototyping.
However, with a Seaside application I would imagine database transactions frequently spanning more than one HTTP request-response. How can this be handled?
Keep up the cool work.
Ian.
May 8, 2007 at 8:24 am
Dale Henrichs
Good question, Ian.
I suppose that I would like to distinguish between ‘database transactions’, ‘transient session state’, and ‘persistent session state’ when working in GemStone/Seaside.
You get ‘transient session state’ in Seaside when running in Squeak (for example). In fact, much of the Seaside framework is designed to manage ‘transient session state’.
You get ‘persistent session state’ in GemStone/Seaside when you explicitly reference some ‘session state’ from a persistent root. If you play with the Gemstone/S sushi application you will notice that the cart is persistent – you can log in and out of the sushi site, and the contents of your cart is available at each login. The WAStoreCart is simply associated with a WACustomer (instances of which are stored in a class variable in WAStoreRepository).
You get your classic ‘database transaction’ when you finally decide to ‘Checkout’ and have pressed ‘Bill Me’ button. The HTTP request-response path leading to this point can very easily utilize ‘transient’ and ‘persistent state’ as required by the particular application.
There is no ‘RDB mapping cost’ for persisting objects in Gemstone/S, so you can be a little more liberal in deciding what parts of your Seaside application need/could/should be persisted.
May 8, 2007 at 2:53 pm
David Siegel
Hi, Dale! Seriously cool stuff.
Is GemStone/Seaside available for download yet? I couldn’t find a link on seaside.gemstone.com.
I think Ian was asking about rolling back transactions that span HTTP requests.
Say there’s a UI that permits many page views and edits, at the end of which the user can hit ‘Save’ or ‘Cancel’. As far as I know, transaction boundaries at HTTP requests don’t help you. You’d either have to (a) save changes off to the side, moving them to the persistent objects only when the user hits ‘Save’ or (b) have some application specific way to restore previous state when the user hits ‘Cancel’.
Both approaches have serious drawbacks:
(a) since your changes aren’t part of the persistent graph, you can’t send normal GemStone messages to your modified objects.
(b) other users can see partially changed objects (after some, but not all of the HTTP requests in the ‘unit of work’ have occurred).
You might get away with (b) for simple applications where every data element stands alone.
You might get away with (a) for applications that need little object behavior.
The sushi store has characteristics of both (a) and (b).
For more complex applications, you’d really want a way to accumulate changes to the persistent graph off to the side, as part of a user’s transient session state, and swap them in when one of his HTTP requests arrives, and swap them out when it completes.
May 9, 2007 at 7:53 am
Dale Henrichs
Hey David!
You can sign up for the Beta program on seaside.gemstone.com. The Beta should start within the next month or so.
You know, as I think about your suggestion, it occurs to me that in Seaside the method WASession>>registerObjectForBacktracking: allows one to arrange for an object to be restored to its ‘original’ state when using the Back button. Perhaps a handful of methods could be added to WASession for accumulating changes and scheduling the swapping as the WASession is activated/deactivated:
WASession>>registerObjectForForwardTracking:
WASession>>commitObjectForForwardTracking:
WASession>>rollbackObjectForForwardTracking:
WASession>>registerObjectForForwardTracking: would schedule a restore of saved state when the WASession is activated, save existing state and restore original state when the WASession is deactivated.
WASession>>commitObjectForForwardTracking: would leave the state of the object alone and remove it from the registry, effectively committing the transaction with the accumulated set of changes.
WASession>>rollbackObjectForForwardTracking: would restore the original state to the object and remove it from the registry, effectively rolling back the transaction and dropping the accumulated changes on the floor.
If I’m not mistaken, the problem you describe exists whenever one attempts to share an object graph between disparate users whether the object graph is persistent or not.
I’d be interested in what some of the Seaside folks think of this suggestion. I think something like this is doable in both GemStone and Squeak.
June 29, 2007 at 10:56 am
Unlimited GemStone VMs in every Garage? ….and a Stone in every Pot « (gem)Stone Soup
[…] 2007 in Seaside, Gemstone About a week ago, one of our early beta users pointed out that the single Seaside VM limit (plus a Maintenance VM) in the Web Edition is not practical. Since each http request coming into […]
November 13, 2007 at 3:15 pm
Bill
Will there be a solution to David Siegel’s question implemented in the general release? Dale, perhaps your WASession methods? Might it not be better to allow just a begin / end transaction instead, or does the current page-level implementation preclude that?
Thanks.
November 15, 2007 at 10:38 am
Dale Henrichs
Bill, we won’t have any general solutions for the upcoming release … getting the tools in shape is consuming a lot of our time.
We are taking the commit/page approach because it is very scalable and it is very straightforward to use.
It is indeed possible to use Seaside and manage your transactions explicitly, however, this approach is not very scalable. In GemStone, you would basically have to allocate a single vm per session to allow for leaving a transaction “open” across multiple requests. For specialized applications this may indeed be the best approach.
As I mentioned in another comment, most of the current solutions to this type of problem involve copying of one form or another and have their own advantages and disadvantages. Using an OR mapping layer involves making a local copy of the data from records in the relational data base.
Getting the copy right, is usually an application specific issue. Don’t forget that there are multiple of ways of making a copy.
One technique that I’m a fan of is to record all changes to objects as change records – there is at least one customer of ours that uses this approach. With change records it is possible to apply/undo chages at will … So doing a long running transaction with change records is simply keeping track of a set of change records that are applied when the long running transaction is completed…
December 3, 2007 at 11:39 am
Bill
> We are taking the commit/page approach because it is very scalable
> and it is very straightforward to use….
> a beginTransaction is performed when the request comes in from the
> http server and a commitTransaction is performed right before the
> response is shipped back out to the http server.
Hopefully this is for full page requests only, and not for all requests such as arbitrarily small Ajax round trips?
December 3, 2007 at 12:38 pm
Dale Henrichs
Bill, it is a ‘commit’ per request. Now if during the arbitrarily small Ajax roundtrips there is no change to persistent state, then the commit is free.
Keep in mind that our commit processing is VERY fast (no OR mapping involved). For example, it is much faster to get a request from GemStone (with a commit) than it is to get a request from Squeak (with no commit).
We have customers who are doing thousands of commits per second, so there _is_ an upper limit — but we have plans for future releases of GemStone where we should be able to bump up the limit quite a bit…
October 30, 2008 at 5:05 pm
GemStone 101: #allInstances, #become: and friends « (gem)Stone Soup
[…] have the most intuitive explanantion and the stack dumped into the object log was rooted in the standard commit that occurs right before the HTTP response is returned to the browser – not much […]