Hi Juan,
Thank you for the code example, the answer is easy. I have put line numbers for reference.
1> aMagmaSession := MagmaSession 2> openLocal: 'C:\Squeak 3> 3.8\Squeak3.8-current-win-full\test'. 4> aMagmaSession connectAs: 'test'. 5> aMagmaSession begin. 6> self deny: (aMagmaSession root includesKey: 'xxxXxxx'). 7> aMagmaSession root at: 'xxxXxxx' put: 'hola'. 8> self assert: (aMagmaSession root includesKey: 'xxxXxxx'). 9> aMagmaSession abort. 10> self deny: (aMagmaSession root includesKey: 'xxxXxxx'). "this 11> assertion 12> sometimes fails" 13> aMagmaSession disconnect.
This is a wonderful example because it touches several "features" of Magma which are not obvious the first time.
The first is that the root is strong-cached in the session (preventing it from being GC'd) ONLY if you are inside a transaction. Normally, Magma never wants to strong-reference anything so the size of the cached model is strictly in the hands of the user program. However, it is so common to write:
mySession commit: [ mySession root at: 'hello' put: 'world' ]
that if Magma did not strong-cache the root while in a transaction, then it could be garbage-collected anytime after at:put: and before the commit finishes. The result is it looks like the commit sometimes does not work. So you would have to write:
| root | root := mySession root. mySession commit: [ root at: 'hello' put: 'world' ]
So, for convenience, Magma hard-references the root when in a transaction so it won't be GC'd so that scripts like the first one will work.
Now, in line 9 the transaction is aborted, so the strong-reference to the root is dropped, but still, Magma will check its weak cache when the root is asked for in line 10 because if its there its much faster than going to get it again from the server. Sometimes, a GC has occurred by that time and sometimes not. If it has, it will not find it in its weak cache and so re-retrieves from the server, the state of which is without the key 'xxxXxxx'.
But wait, there's more (I told you this was a good example!). Even notwithstanding the above, the #abort in line 9 will not do what you want unless you change a preference.
The preference (get this) is called #refreshPersistentObjectsEvenWhenChangedOnlyByMe.
aMagmaSession refreshPersistentObjectsEvenWhenChangedOnlyByMe: true
No joke, the default is false and that is why line 9 will not refresh the model unless you set it to true, because the root object was not changed by anyone else, it was changed only by you, so Magma is will not wipe out your "work".
The nature of some applications may want to have a dynamic view of the database but many other applications have requirements that insist the users work not be disturbed upon crossing a transaction boundary. Crossing a transaction boundary is a #begin, #commit, or #abort. The default to this preference is false because it is more conservative with the users content and also performs better.
#abort is not "undo", although by setting this preference to true it can be used for a very rudimentary, one-level undo. Instead, abort is meant for transaction control. There is considerable flexibility in Magma's transaction control by way of three different "commit strategies" which may be used depending on how you want the program to work.
For example, do you just want the user to work on the domain model (through the UI) and the program takes care of the transactions invisibly? Or, do you want to provide the user a "Save" button (commitAndBegin) that they can do incremental commits anytime? A "Refresh" button (abort) may be just the user wants to see the latest model while getting ready to "attach" their changes they worked on for two hours.
Other factors are how often the domain changes or whether there is a lot collaboration needed by multiple users on the same exact objects. To get the most out of Magma, all of these strategies should be considered and then decide which one works best. Here is a description of all three:
http://minnow.cc.gatech.edu/squeak/5605
Welcome to Magma, Chris