Request for opinions :)

Posts   
 
    
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 16-Mar-2004 14:21:56   

Ok. Recursive saves are great, however very complex behind the scenes. As you've probably recognized in the past few weeks, some issues have been plagueing the recursive save logic, however it seems to be working fine now.

Below are the 2 issues which are still in the recursive save logic. One is a little thing to fix, the other one is a possible nasty one as a fix will probably break not so well constructed applications.

1) Saving an entity collection recursively will not report the correct amount of entities saved. This is not that hard to fix 2) When a save somewhere deep in the hierarchy fails, during a recursive save, the complete transaction isn't aborted, the save just failed.

Every entity saved in a transaction is internally 'backupped', that is: all fields are backupped, and if a transaction fails, the original fields are restored with the backup. This is done to avoid refetches of data being kept in memory after the transaction is terminated and rolled back.

This means that a transaction in fact is an atomic save: if an error occurs, nothing is changed in the database nor is any refetching of already saved data messing up the data internally.

And now for the question simple_smile . If a save of an entity reports 'false', which means 0 rows were affected by the save (thus the entity in the database was removed or a concurrencypredicate limited the scope of the update) should this be a terminating factor for the complete transaction? In other words: IF an entity save action fails, should it throw an exception? (Which will roll back the transaction).

My own opinion: yes, a failed save should throw an exception during a recursive save. (not during a regular save). This is based on the analogy that an error also throws an exception terminating the complete transaction.

Why is this an important thing to discuss? Well, if you've constructed your application in a way that you use recursive saves just to save some entities, thus not using concurrency predicates, you probably will not catch the exception. This can make working applications become unusable.

This doesn't occur a lot though. If you look at it like: you've altered an entity. You want to save it. In a situation where you didn't use a concurrency predicate factory, this only fails when the entity is deleted by another thread. Is it then good that this update's failure goes unnoticed? No, I don't think so. That's why I think the exception is good.

However, as it might affect you all, my question to you is: should this exception be added or not?

TIA simple_smile

Frans Bouma | Lead developer LLBLGen Pro
wojo
User
Posts: 69
Joined: 10-Mar-2004
# Posted on: 16-Mar-2004 17:39:07   

IMHO, yes.

The reasons you stated pretty much sum it up. They address my concurrency predicate factory issue I emailed you about and also the case where the row has been deleted before a call to SaveEntity, which I believe should also be a "fatal" error.

jeffreygg
User
Posts: 805
Joined: 26-Oct-2003
# Posted on: 16-Mar-2004 18:48:14   

If you choose to save multiple entities as a unit, it should fail as a unit. However, perhaps you can provide a switch that overrides that default behavior to force the graph to save in spite of a fail. It's going to be hard to figure out what failed and compensate for it in code, and in cases like this (which probably should be handled 100% of the time) you'll just need to add logic to save each entity in the graph individually anyway...

Jeff...

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 17-Mar-2004 09:48:54   

Well a switch has to have a default value. simple_smile And that's the problem simple_smile If I add a flag property AbortOnFail to the DataAccessAdapter class and the Transaction class, should that flag be true as default or false? If I say: true, I can leave out the flag. If I say, false, a lot of people have to set the flag each time they're going to save. simple_smile

Currently I think that I should NOT throw an exception if the save is not recursive. Save returns false anyway in that case, so throwing an exception is double

In all other cases I should throw a concurrency exception. The reasons for those are: - if no concurrency predicate was specified, the save tried to update a deleted entity. This is bad and the developer should be notified. - if a concurrency predicate was specified, the save failed, due to the reason mentioned above or because of the concurrency predicate. Either way, it's a failure.

So existing code will not be affected, if there is never a chance of updating a deleted entity. I'll add the exception code to an update of the hotfix of the runtime libs, later today.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 17-Mar-2004 16:26:12   

I've uploaded a new hotfix for the runtime libs which sports this functionality now. It throws an ORMConcurrencyException (which is new) when a save fails AND the save is recursive.

Frans Bouma | Lead developer LLBLGen Pro
Banane avatar
Banane
User
Posts: 67
Joined: 01-Sep-2004
# Posted on: 09-Feb-2005 19:48:20   

Hi,

when there is a concurrency problem I want to know and raise an error... So, is there a way to know why the save() returned false? (not recursive save)

Is there a property I can check to know that save returns false because of a concurrency problem!

tx

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 09-Feb-2005 20:30:01   

Banane wrote:

Hi,

when there is a concurrency problem I want to know and raise an error... So, is there a way to know why the save() returned false? (not recursive save) Is there a property I can check to know that save returns false because of a concurrency problem!

A save which fails will report 0 as the amount of rows affected. There is no reason given by teh Database engine so I can't determine what the reason was. The only way to check it is to do a fetch after the failed save. If the fetch succeeds, the save failed due to a concurrency error. If the fetch fails, the save failed due to the lack of an updatable record. The runtime code doesn't have any more information than '0' as the amount of rows affected.

Frans Bouma | Lead developer LLBLGen Pro
Banane avatar
Banane
User
Posts: 67
Joined: 01-Sep-2004
# Posted on: 09-Feb-2005 20:51:56   

Make sense tx a lot wink