Hi Caleb,

We have found your proposal really interesting, and we have stated to use it
against a true database (using JDBC) for one XWiki related project we have
in development today. I have had not much time myself to look at the
details, but here is comments/questions from my colleague Olivier. If we
plan to use it later for the xwiki-store, I would really appreciate your
comments.

In my understanding, for a database, I need to have a
> StartableTransactionRunnable that roughly:
>  * Open the connection in onPreRun
>  * Commit the transaction in onCommit
>  * Rollback the transaction in onRollback
>  * Close the connection in onComplete
>
> Then, I need to have classes extending TransactionRunnable and implementing
> the desired code in the onRun method.
>
> Have I correctly understood how this library should be used ?
> If I'm correct, how do the TransactionRunnable instances retrieve the
> connection held in the StartableTransactionRunnable ?
> Currently I have a code similar to this:
>
> DBStartableTransactionRunnable run = new DBStartableTransactionRunnable
> (dataSourceName);
> new InsertDataTransactionRunnable(run, data).runIn(run);
> run.start();
>
> In which I need to pass the DBStartableTransactionRunnable instance in
> order to be able to access the exposed Connection object which is
> instanciated in the onPreRun method
>
>
> My second interrogation concern transactions that return data. What's the
> best way to implement them ?
> Currently what I've come up with is something like:
>
> DBStartableTransactionRunnable run = new DBStartableTransactionRunnable
> (dataSourceName);
> GetDataTransactionRunnable t = new GetDataTransactionRunnable(run, dataID)
> t.runIn(run);
> run.start();
> MyData d = t.getResult();
>
>
> I have a third interrogation, but I'm still unsure about it's validity.
> It consist of data communication between transactions.
> Assume that running in the same StartableTransactionRunnable we have:
>  * TR1 fetch data A1
>  * TR2 update another data A2
>  * TR3 perform some operation involving A1
>
> Where TR means TransactionRunnable instance. How does TR3 has access to the
> data that is retrieved by TR1 ?


WDYT ?
Thanks,

Denis

On Tue, Jan 11, 2011 at 14:50, Caleb James DeLisle <[email protected]
> wrote:

>
>
> On 01/11/2011 06:03 AM, Marius Dumitru Florea wrote:
> > Hi Caleb,
> >
> > Sounds good. Great job! One comment below,
> >
> > On 01/10/2011 03:15 PM, Caleb James DeLisle wrote:
> >> Hi,
> >> I have been working hard on filesystem attachments and I found that
> synchronizing manual filesystem
> >> transactions with automatic database transactions was a difficult job
> and I needed a new tool to do
> >> it. So I wrote what I am proposing to be the next XWiki Persistence
> Engine.
> >>
> >> I'll start off with the fun part of the proposal, I have been calling it
> xwiki-store so far but I am
> >> so excited about the capabilities of this engine that I don't think it
> does it justice to name it
> >> "store" after the place on the corner with milk and eggs. I am proposing
> it be named "XWiki
> >> Persistence Engine", the directory will be renamed xwiki-persistence,
> the artifact name
> >> xwiki-core-persistence, and the package name org.xwiki.persistence.
> Persistence is an attribute of
> >> castles, mountains and redwood trees which I think is fitting for a
> conservatively designed storage
> >> engine.
> >>
> >> Now a little explanation of what I'm so excited about:
> >> The common and error prone way of saving things in the database is to
> open a transaction, enter a
> >> try clause, do something then commit. If we catch an exception, then we
> rollback.
> >> something like this:
> >>
> >> begin transaction;
> >> try {
> >>     do something;
> >>     do something else;
> >>     commit;
> >> } catch (Any exception which may occur) {
> >>     rollback;
> >> }
> >>
> >> There are 3 things which can go wrong. 1 we forget to begin the
> transaction, 2 we forget to commit
> >> and 3 we do not rollback properly. What makes things worse is often the
> database will "assume we
> >> meant to..." and things will work ok most of the time which makes things
> much worse because bugs
> >> will hide very well.
> >>
> >> My answer to this problem is a class called TransactionRunnable. It
> provides a set of 5 empty
> >> methods to override: onPreRun(), onRun(), onCommit(), onRollback(), and
> onComplete(). the exact
> >> circumstances under which each are called is documented in the javadoc
> comments here:
> >>
> http://svn.xwiki.org/svnroot/xwiki/contrib/sandbox/xwiki-store/xwiki-store-transaction/src/main/java/org/xwiki/store/TransactionRunnable.java
> >> I wrote TransactionRunnable twice, I wrote it, used it for attachments,
> then after having real
> >> experience as a user, I wrote it again.
> >>
> >> To repeat our original example with TransactionRunnable you might say
> this:
> >>
> >> public class DoSomethingTransactionRunnable extends TransactionRunnable
> >> {
> >>     public void onRun()
> >>     {
> >>       do something;
> >>       do something else;
> >>     }
> >> }
> >>
> >> Now we can use another TransactionRunnable which opens and closes the
> transaction for us.
> >>
> >> StartableTransactionRunnable transaction = new
> HibernateTransactionRunnable();
> >> new DoSomethingTransactionRunnable().runIn(transaction);
> >> transaction.start();
> >>
> >> the runIn() function allows us to run one TransactionRunnable inside of
> another. Supposing we wanted
> >> to reuse "do something else" in other places, we can make it a separate
> TransactionRunnable and use
> >> the runIn() function to hook it to our DoSomethingTransactionRunnable
> ie:
> >>
> >> public class DoSomethingTransactionRunnable extends TransactionRunnable
> >> {
> >>     public DoSomethingTransactionRunnable()
> >>     {
> >>       new DoSomethingElseTransactionRunnable().runIn(this);
> >>     }
> >> ..
> >>
> >> The only limitations on running TransactionRunnables inside of one
> another are they cannot run more
> >> than once and they cannot call themselves (this would be an infinite
> loop).
> >>
> >> This pattern makes each job which is done on storage easily isolated
> and, as I have so far
> >> experienced, trivial to test. However, it still leaves the possibility
> that we might forget that
> >> DoSomethingTransactionRunnable must be run inside of a hibernate
> transaction. I have devised a
> >> solution for this too. Using generics, I offered a means for the author
> of a TransactionRunnable to
> >> communicate to the compiler what other TransactionRunnable their
> runnable must be run in and without
> >> explicit casting or defining of an intermediary runnable, this
> requirement cannot be violated or
> >> else it wouldn't compile!
> >
> > Is this generic enough to allow us to easily test a TransactionRunnable
> > that has explicitly specified the runnable it must be run in?
> >
> > When you say "generics" I guess you are referring to:
> >
> > public class DoSomethingTransactionRunnable extends
> > TransactionRunnable<HibernateTransactionRunnable>
> > {
> >      ...
> > }
> >
> >  From what I understood HibernateTransactionRunnable must be a concrete
> > class so that you can instantiate it. Then it's not easy/elegant to mock
> it.
>
> Since runIn() takes a TransactionRunnable<? extends T> instead of T itself,
> there are 2 options:
> 1. Do as you say and then DoSomethingTransactionRunnable may be runIn() any
> TransactionRunnable<HibernateTransactionRunnable> so you need not mock
> HibernateTransactionRunnable,
> only define a TestTransactionRunnable which extends
> TransactionRunnable<HibernateTransactionRunnable>
> or as I prefer:
>
> 2. Create an empty interface called HibernateTransaction which exists for
> the sole purpose of being
> in the generic. Thus to define a
> StartableTransactionRunnable<HibernateTransaction> is to promise
> that you have indeed started and stopped the hibernate transaction in your
> runnable.
>
> I did spend the better part of a day trying to make sure that an evil
> StartableTransactionRunnable
> could not make promises that it couldn't keep but in the end I decided that
> this method was better.
>
> In regards to mocking, I have not found a need to mock anything since I can
> just instantiate an
> anonymous class extending TransactionRunnable, put my test logic in that,
> and run my tested runnable
> inside of it.
>
>
>
> Along a similar line, you might ask why TransactionRunnable itself is not
> an interface. I tried to
> do this and found that first, there were a number of private methods used
> for chaining which had to
> be made public and was less than pleasant although acceptable. Second,
> runIn() relies on access to a
> private field to attach the runnable to it's parent and I couldn't face the
> thought of a public
> unsafeAdd() method which said "please don't use this" in the javadoc
> comment. I could not think of
> any use case for another implementation of the base TransactionRunnable
> class and it made no sense
> to me to sacrifice the outward simplicity and stability of
> TransactionRunnable for something which I
> could not imagine any use for.
>
> Caleb
>
>
> >
> >>
> >> Finally we have the issue of starting the runnable. Who's to say I won't
> be tired one day and just
> >> write new DoSomethingTransactionRunnable().start() without opening a
> transaction first? If
> >> DoSomethingTransactionRunnable cannot be safely run outside of a
> transaction all it needs to do is
> >> not extend StartableTransactionRunnable and it won't have any start
> function.
> >>
> >> I have taken a multitude of very easy mistakes and given the author of a
> TransactionRunnable the
> >> tools to make it very hard for the user to make them. Also, since a
> TransactionRunnable has no
> >> reason to be very long (it can just branch off into another runnable)
> this will make testing and
> >> code review easy in the place where it is most important. This part of
> the code is entirely generic
> >> and has no dependence on hibernate or anything else.
> >>
> >> I propose we move:
> >> contrib/sandbox/xwiki-store/xwiki-store-transaction/
> >> to:
> >> platform/core/xwiki-persistence/xwiki-persistence-transaction
> >>
> >> And I will propose moving each additional piece in the coming days.
> >>
> >> WDYT?
> >
> > +1
> >
> > Thanks,
> > Marius
> >
> >>
> >> Caleb
> >>
> >> _______________________________________________
> >> devs mailing list
> >> [email protected]
> >> http://lists.xwiki.org/mailman/listinfo/devs
> > _______________________________________________
> > devs mailing list
> > [email protected]
> > http://lists.xwiki.org/mailman/listinfo/devs
> >
>
> _______________________________________________
> devs mailing list
> [email protected]
> http://lists.xwiki.org/mailman/listinfo/devs
>



-- 
Denis Gervalle
SOFTEC sa - CEO
eGuilde sarl - CTO
_______________________________________________
devs mailing list
[email protected]
http://lists.xwiki.org/mailman/listinfo/devs

Reply via email to