[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Fwd: Re: Long-running transactions in Ozone?



Peter, please forgive me if I don't get the main points from emails as fast as
I should and/or if I my emails are not that clear as they should be sometimes.
(this thread seems to be a good example for this)

On Tue, 17 Apr 2001, Peter Schuller wrote:
> > The WizardStore tries to group objects that 'are used together' together in
> > clusters. So the chance is high that if you access/lock one object out of a
> > cluster then you will probably access/lock others from the same cluster too.
> 
> Ok, that's consistent with what I thought.
> 
> I'll quote out of order now because it relates to this:
> 
> > > Not to mention maintaining isolation in a case where write-thread A starts
> > > modifying cluster X, and when write-thread B (whose transaction started
> > > prior to write-thread A's) it has to see the old version of X (prior to A's
> > > modifications).
> > ozone uses pessimistic locking! Just one tx can WRITE an object at one given
> > point in time.
> 
> But entire clusters are locked at a time, right?
> 
> Anyways; my "problem" then is one of deadlock.
> 
> Suppose transaction A needs to access object
> X and Z recursively in order to complete. Supposed further that transaction B needs
> to access object Y, Z and X recursively in order to complete. Each object is in a
> separate cluster. Transaction A executes some update method on object X
> simultaneously as a method is being executed on Z by transaction B. This is
> allowed because they are in different clusters.
> 
> Transaction B proceeds to execute a method on object Z (again, recursively). A
> is now working with X and B with Z.
> 
> We now have two possiblities.
> 
> 1) A wants to recursively invoke a method on Z before B wants to recursively
>    invoke a method on X.
> 2) B wants to recursively invoke a method on X before A wants to recursively
>    invokte a method on Z.
>    
> In both cases we have a deadlock situation. A waits on B and B waits on A.
> None can complete without breaking transaction isolation.
> 
> A: X ---------------> Z ------> ... return
> B: Y -----> Z ------> X ------> ... return
> 
>                       ^
> 		      ^
> 	At this point X is locked by A and Z by B (or rather, their clusters
> 	are locked).
> 
> So what am I missing? If Ozone uses *only* pessimistic locking, and clusters
> are the units on which the locking occurrs (rather than the entire DB), how
> is this worked around?

You are of course right, ozone can run into deadlocks at each and every time.
Period. But this has nothing to do with the number of objects that are covered
by one lock - one object or 100 in case of cluster locking in WizardStore.

The only thing ozone can do is to detect deadlocks and resolve them. This
is done via a separate thread which permanently checks for deadlocks using a
slightly modified edge chasing algorithm. If one is detected it signals any of
the locked transactions to abort and restart. This works for transactions that
do not use explicit demarcation. Such transactions are started by just one
method call from the client. This method call transalated into a
org.ozoneDB.core.DbRemote.DbCommand and stored in the thread of the
transaction. Therefore it's no problem to abort and restart a deadlocked
transaction. This is done as long as all transaction have ended without
deadlock.

> 
> > This depends on the implementation of the optimistic locking you choose. Your
> > 'clone' version indeed is hard to implement (I did it for the first versions of
> > ozone). It would probably check at the end of a transaction if all READ locks
> > were aquired before all WRITE locks (2-phase).
> > 
> > I was thinking of an implementation that _immediately_ throws an exception,
> > when a object which I have optimistcly locked was changed by another party
> > (READ and WRITE are not 2-phase). For this implementation no clones are needed.
> > Just a way set/check timestamps.
> 
> Hmm. So basically, charge ahead and do changes, and if a read transaction
> detects a change, rollback the read transaction (in response to the exception)
> and try again? That would impose a one-write-at-a-time restriction though,
> would it not?
> 
> If that is a correct interpretation, then I suppose that would work well in
> most cases. However the absolut worst-case senario is that read transactions
> never complete because they keep getting rolled back due to writes.
> Particularly in the case of longer transactions.
> 
> Then again, I'm probably mis-understanding you. I have no prior experience
> with implementing databases (OO or otherwise).

Ok, my explanation wasn't clear enough. Here is my next try ;)

What we want to have is that a transaction should spawn several (method) calls
to the database (in several HTML pages for example). right? 

Something like:

	OptimisticTX otx = new OptimistcTX(...);
	try {
		obj.doSomethingPage1();

		// user input (maybe long)

		obj.doSomethingPage2();
	}
	catch (OptimisticException e) {
		// inform the user about the rollback
		// and update the GUI
	}

What I did not clearly stated is that I would keep the pessimistic locking
inside the method calls doSomething...() intact. They behave just like there is
no transaction around them. The only difference is that all objects that are
accessed inside otx for the first time (this is always a READ lock) are stored
together with their timestamps of the last change. This allows to detect
'dirty' changes that would corrupt the transaction isolation.

In other words, instead of (pessimisticly) locking the objects, we just give
us the chance to find out about corrupt transaction isolation.

BTW: it seems that you are searching for a way to let two transactions
READ/WRITE the same object at the same time without affecting each other.
IMO this is not possible. No matter what locks and or strategies you are using.
pessimistic, optimistic, clones, whatever - at the end all READ have to be
acquired before all WRITES in order to keep isolation intact. In ozone we
basically achieve this using this one-write-at-a-time restriction
(pessimistic). Another approach is to work on clones (seems that you prefer
this). But this does not fundamentally change the situation.

Back to the example. The problem with this approach is that my so called
OptimisticTransaction is no transaction because we cannot roll it back as a
whole. There I would change API to something like:

	ExternalDatabase db = ...
	
	db.mark();
	try {
		obj.doSomethingPage1();

		// user input (maybe long)

		obj.doSomethingPage2();
	}
	catch (OptimisticException e) {
		// inform the user about the rollback
		// and update the GUI
	}
	finally {
		db.unmark();
	}

This simulates the READ UNCOMMITED isolation level of RDBMSs. But
unfortunaltely if we detect the OptimisticException during doSomethingPage2()
then we cannot rollback changes that potentially were made during
doSomethingPage1(). I'm not sure here. Ist this really a problem? Can we solve
this? How?

> 
> > > Well I was mostly referring to implementing multiple-read and multiple-write
> > > transactions that do not affect each other (kind of like a SERIALIZABLE
> > > RDBMS transaction). 
> > I probably lost you here. We are talking about scalability, right? We want to
> > let as most threads/CPUs run as possible, right again? SERIALIZABLE gives the
> > worst scalability IMO. SERIALIZABLE isolation level on RDBMSs let you neither
> > WRITE nor READ (!) data at the same time from different transactions.
> 
> Well the way SERIALIZABLE isolation works in Postgres at least (I haven't
> actually read the SQL spec so I don't know if its required/correct) is that each
> transaction can proceed simultaneously. It's just that transaction X doent't
> see what transaction Y is doing, and vice versa.

Quote from 'Enterprise JavaBeans' (RM Haefel):

"SERIALIZABLE: The transaction has exclusive read and update privileges to data;
different transcarion can neither read not write the same data."

So, yes, this is the best isolation but also the most pessimistic and least
scalable one.

> 
> Essentially, each transaction (even with auto-commit) operates on a snapshot
> of the entire database. Changes then get "merged", assuming no conflicts.

It has to be a real clever DBMS that is able to "merge" changes without
conflicts. I don't beliefe that RDBMS do this. Anyway, while this is
theoretically possible for RDBMS (because semantic of SQL operations is well
known to the DBMS) this is totally impossible for ODBMSs (because semantic of
the data manipulation language Java is not known to the DBMS).

> 
> (That's just conceptual; obviously it's not implemented like that!)
It's implemented using exclusive locks -> worst scalability.


> 
> But yes, the point was scalability, and being able to do mulitple reads and
> writes simultaneously (that is, actually executing then simultaneously inside
> the database).
> 
> > Of course we have transaction isolation. ozone uses MROW locks (pessimistic
> > locking). This is something like SERIALIZABLE but let multiple readers
> > access an object.
> 
> Yes. My confusion was just due to my (possibly imagined?) deadlock senario.
> I assumed something special was being done when cluster boundaries were
> crossed.


Falko
-- 
______________________________________________________________________
Falko Braeutigam                              mailto:falko@smb-tec.com
SMB GmbH                                        http://www.smb-tec.com