Magma Transactions - MagmaCommitConflictError

Bart Gauquie bart.gauquie at gmail.com
Thu Dec 10 19:59:47 UTC 2009


Hi Chris,

That makes perfectly sense to me.

If I want to check that user 2 does not update a stale (which was updated by
user 1) version; I should use a sort of pessimistic locking scheme, as
described on the wiki. For instance:

<code>
testTriggerAVersionConflictByUsingVersionLogic
|firstSession secondSession mockObjectRefByFirstSession
mockObjectRefBySecondSession
versionBeforeSessionBeginOfObjectReffedBySecondSession|
[firstSession :=
(MagmaRemoteLocation
host: 'localhost'
port: self databasePort) newSession.
firstSession connectAs: 'firstSessionUser'.
firstSession commit: [
firstSession root
at: 'mockObject'
put: (SomeMockDomainObject new aField: 'aField content'; yourself)].

secondSession :=
(MagmaRemoteLocation
host: 'localhost'
port: self databasePort) newSession.
secondSession connectAs: 'secondSessionUser'.

mockObjectRefByFirstSession := firstSession root at: 'mockObject'. "User 1
is loading the object"
mockObjectRefBySecondSession := secondSession root at: 'mockObject'. "User 2
is loading the same version of the object as User 1"

firstSession begin. "1. User 1 begins a transaction."
mockObjectRefByFirstSession aField: 'aField updated by user 1'. "3. User 1
changes Object A."
mockObjectRefByFirstSession incrementVersion.
firstSession commit. "4. User 1 commits his transaction."

versionBeforeSessionBeginOfObjectReffedBySecondSession :=
mockObjectRefBySecondSession version. "retrieve version of object right
before
beginning transaction (which does a refresh of the data of the object) - so
also updates the version field, thats why we have to
get the version right before beginning the transaction"
self assert: 0 equals:
versionBeforeSessionBeginOfObjectReffedBySecondSession.
secondSession begin. "2. User 2 begins a transaction."
self assert: 1 equals: mockObjectRefBySecondSession version. "new version
silently got merged in"
"since versions are not equal => user 2 has been updating a stale version of
object; so abort and do not commit any changes;
and signal a ConcurrentModificationException to the client".
mockObjectRefBySecondSession aField: 'aField updated by user 2'. "5. User 2
changes Object A."
secondSession abort.

self assert: 'aField updated by user 1' equals: ((firstSession root at:
'mockObject') aField).
self assert: 'aField updated by user 1' equals: ((secondSession root at:
'mockObject') aField).
self assert: 'aField updated by user 1' equals:
(mockObjectRefBySecondSession aField).

] ensure: [[firstSession commit: [
firstSession root removeAll].
firstSession disconnect]
ensure:
[secondSession disconnect]]

</code>

Are there any options in Magma provided to simulate such behavior
automatically ?

Thanks again.

Kind Regards,

Bart

On Thu, Dec 10, 2009 at 8:44 PM, Chris Muller <asqueaker at gmail.com> wrote:

> Hi, the should: raise: MagmaCommitConflictError in
>
> #testTriggerACommitConflictUsersCommitAfterEachOtherButWithTheSameVersionOfObjectInitiallyLoaded
> :
>
>                self should: [secondSession commit. "6.7. User 2 commits his
> transaction"]
>                        raise: MagmaCommitConflictError.
>
> fails because secondSession crossed a transaction boundary (via its
> #begin) after firstSession's commit.  Second session would see the
> results of firstSession's commit, and therefore there is no conflict.
> A commit-conflict would have occurred if secondSession had sent #begin
> BEFORE firstSession's commit..
>
> Regards,
>   Chris
>
>
> On Tue, Dec 8, 2009 at 2:18 PM, Bart Gauquie <bart.gauquie at gmail.com>
> wrote:
> > Dear all,
> >
> > I've been trying out the MagmaCommitConflictError in Magma. I'm
> experiencing
> > following which I do not understand.
> > I've attached a changeset with a test case reproducing the behaviour I do
> > not understand.
> > If I follow the steps outlined at:
> > http://wiki.squeak.org/squeak/2636 a MagmaCommitConflictError is thrown.
> > (see testTriggerACommitConflictSequenceAsDescribedOnWiki and a little
> > modification on that:
> > testTriggerACommitConflictLoadObjectBeforeStartingATransaction)
> > If I however execute
> >
> testTriggerACommitConflictUsersCommitAfterEachOtherButWithTheSameVersionOfObjectInitiallyLoaded
> >
> > |firstSession secondSession mockObjectRefByFirstSession
> > mockObjectRefBySecondSession|
> > [firstSession :=
> > (MagmaRemoteLocation
> > host: 'localhost'
> > port: self databasePort) newSession.
> > firstSession connectAs: 'firstSessionUser'.
> > firstSession commit: [
> > firstSession root
> > at: 'mockObject'
> > put: (SomeMockDomainObject new aField: 'aField content'; yourself)].
> >
> > secondSession :=
> > (MagmaRemoteLocation
> > host: 'localhost'
> > port: self databasePort) newSession.
> > secondSession connectAs: 'secondSessionUser'.
> >
> > mockObjectRefByFirstSession := firstSession root at: 'mockObject'. "User
> 1
> > is loading the object"
> > mockObjectRefBySecondSession := secondSession root at: 'mockObject'.
> "User 2
> > is loading the same version of the object as User 1"
> >
> > firstSession begin. "1. User 1 begins a transaction."
> > mockObjectRefByFirstSession aField: 'aField updated by user 1'. "3. User
> 1
> > changes Object A."
> > firstSession commit. "4. User 1 commits his transaction."
> >
> > secondSession begin. "2. User 2 begins a transaction."
> > mockObjectRefBySecondSession aField: 'aField updated by user 2'. "5. User
> 2
> > changes Object A."
> >
> > self should: [secondSession commit. "6.7. User 2 commits his
> transaction"]
> > raise: MagmaCommitConflictError.
> >
> > firstSession refresh.
> > self assert: 'aField updated by user 1' equals: ((firstSession root at:
> > 'mockObject') aField).
> > self assert: 'aField updated by user 1' equals: ((secondSession root at:
> > 'mockObject') aField).
> > self assert: 'aField updated by user 1' equals:
> > (mockObjectRefBySecondSession aField).
> >
> > ] ensure: [[firstSession commit: [
> > firstSession root removeAll].
> > firstSession disconnect]
> > ensure:
> > [secondSession disconnect]]
> >
> > It failes and I dont understand why. The test is what you get in a
> typical
> > web application. User 1 has a session on the database. User 2 has another
> > session. Both opened an item. Both are editing it and then first user1
> > commits his changes. User 2 wants to commit his changes but did not see
> the
> > updated data of User 1. The test failes at :
> > self should: [secondSession commit. "6.7. User 2 commits his
> transaction"]
> > raise: MagmaCommitConflictError.
> > => MagmaCommitConflictError is not thrown. Which is something I do not
> > understand. User 2 is trying to commit a change on an object some other
> > session already changed. So User 2 is trying to update a stale version.
> Is
> > there a reason why no MagmaCommitConflictError is thrown? Furthermore, if
> > you proceed on the failing MagmaCommitConflictError; refresh the
> > firstSession; i notice that the changes made by Session2 effectively got
> > committed; which surprises me even more. Because that means that the
> changes
> > made by User 1 got overridden by the changes of User 2 without User 2
> even
> > knowing.
> > If I read the information on the wiki, this behaviour is correct since
> Magma
> > will only detect if during a transaction (after begin has been called)
> some
> > other session has committed changes on the object being changed. Off
> course
> > this is not the kind of behaviour you have if you're developing web
> > applications since there, the transactions are very short.
> > Is there a way to detect if an object was updated by another transaction,
> > but not within my own transaction; so that stale updates do not happen. I
> > suppose there is some version on a persisted object you can check and if
> the
> > last committed version of an object is higher than the version in the
> > current session, this also means a magmacommitconflicterror? But I havent
> > found any info about that yet?
> > Thanks for any help.
> > Kind Regards,
> > Bart
> > --
> > imagination is more important than knowledge - Albert Einstein
> > Logic will get you from A to B. Imagination will take you everywhere -
> > Albert Einstein
> > Learn from yesterday, live for today, hope for tomorrow. The important
> thing
> > is not to stop questioning. - Albert Einstein
> > The true sign of intelligence is not knowledge but imagination. - Albert
> > Einstein
> > Gravitation is not responsible for people falling in love. - Albert
> Einstein
> >
> > _______________________________________________
> > Magma mailing list
> > Magma at lists.squeakfoundation.org
> > http://lists.squeakfoundation.org/mailman/listinfo/magma
> >
> >
>



-- 
imagination is more important than knowledge - Albert Einstein
Logic will get you from A to B. Imagination will take you everywhere -
Albert Einstein
Learn from yesterday, live for today, hope for tomorrow. The important thing
is not to stop questioning. - Albert Einstein
The true sign of intelligence is not knowledge but imagination. - Albert
Einstein
Gravitation is not responsible for people falling in love. - Albert Einstein
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.squeakfoundation.org/pipermail/magma/attachments/20091210/8372d5e6/attachment-0001.htm


More information about the Magma mailing list