On 01/20/2011 04:24 AM, Vincent Massol wrote:
> 
> On Jan 20, 2011, at 10:12 AM, Denis Gervalle wrote:
> 
>> On Thu, Jan 20, 2011 at 09:23, Vincent Massol <[email protected]> wrote:
>>
>>> Hi Caleb,
>>>
>>> On Jan 19, 2011, at 7:54 PM, Caleb James DeLisle wrote:
>>>
>>>>
>>>>
>>>> On 01/19/2011 12:13 PM, Vincent Massol wrote:
>>>>> Hi Caleb,
>>>>>
>>>>> I see you're excited, that's good! :)
>>>>>
>>>>> Some general comments:
>>>>> * This looks more like a design for a transaction module than for a
>>> persistence engine. I don't see anything related to persistence in your
>>> proposal below. Your proposal could work on stuff others than storage,
>>> right?
>>>> Yes, this proposal only covers the transaction sub-module of the
>>> persistence engine. The so far
>>>> un-proposed modules include xwiki-store-serialization,
>>> xwiki-store-filesystem and a legacy
>>>> attachment storage module: xwiki-store-filesystem-attachments.
>>>
>>> Ok I was misled by the title of your mail "Introduce a new persistence
>>> engine". I guess it could/should have been "Introduce a generic transaction
>>> API independent of the underlying storage implementation", right?

"Introduce a generic transaction API independent of the underlying storage 
implementation"
so boring compared to "The XWiki Persistence Engine" ;)
Indeed you are correct but this proposal is for the foundation of the engine.

>>>
>>> So if we focus purely on the transaction part here are some questions:
>>>
>>> * Why don't we use existing standards such as JTA/JTS? See
>>> http://en.wikipedia.org/wiki/Java_Transaction_API
The original answer was that I defined my requirements and wrote rather than 
looking for something.
Now looking at the JTA information, I still can't tell if it meets the 
requirements which
TransactionRunnable was designed to meet.

1. Can multiple jobs be chained together inside of a transaction?
  Can each job be made atomic? Can they be tested without mocking?
2. Are try/catch completely handled?
  I notice a lot of transact.begin() and transact.commit() in the code.
3. Is is possible to run a job outside of the proper type of transaction?
  IE: Can a "save document in JCR" job accidentally be run in a Hibernate 
transaction?
4. Can JTA run on any storage platform? Hibernate, JCR, raw JDBC, Filesystem, 
Memcached, etc.

>>> * If we were to define our own API, would we be able to implement it using
>>> JTA, ie is it a higher level transaction API than JTA?
TransactionRunnable is designed to run on top of absolutely anything.

>>> * Imagine we decide to use JCR as the storage implementation, how would
>>> this transaction API integrate with it knowing that JCR integrates with JTA?
>>> (http://www.day.com/specs/jcr/2.0/21_Transactions.html)
Yes, anything. Also I notice in the example code given at that page, they are 
using the same (anti)
pattern. begin, do stuff, commit. That code can't be incorporated into a larger 
transaction or else
it would be rolled back when begin() is called. This is the kind of code which 
begs to be made into
a TransactionRunnable.

>>>
>>>> Anything which requires transactions could use the TransactionRunnable
>>> although I'm at a loss to
>>>> think of anything other than storage which would require transactions.
>>>>
>>>>> * I was expecting to see some Store/Storage/Persistence interfaces with
>>> proposed APIs and explanation on how they could be implemented both with
>>> Hibernate and JCR for example. And the relationship with the proposed new
>>> Model defined.
>>>> I don't like to propose an interface until I have tried to implement it.
>>> Also I do not like to
>>>> propose an implementation until I have tried to use it. At this point
>>> it's far enough off that I
>>>> would rather wait than propose APIs blind.
>>>>
>>>> My experience with attachment store has shown that what we want is a set
>>> of functions which provide
>>>> TransactionRunnables to do various things:
>>>> aka:
>>>> TransactionRunnable<T> getDocumentSaveTransactionRunnable(XWikiDocument
>>> toSave);
>>>> In a hibernate implementation it would return
>>> TransactionRunnable<HibernateTransaction> and in a JCR
>>>> it would return TransactionRunnable<JCRTransaction>.
>>>>
>>>> We cannot have APIs like this until TransactionRunnable is agreed upon
>>> these will return instances
>>>> of it.
>>>>
>>>>> * I was also expecting a strategy defined to migrate users from the
>>> current implementation of the storage to the new one
>>>> IMO we should change the persistence engine and implement the same
>>> schema, once the persistence
>>>> engine is rebuilt, then we can consider modifying the schema. The schema
>>> is a specification, it may
>>>> not be perfect but it is something to comply with. It is important to me
>>> that a we prove that a new
>>>> persistence engine is able comply with existing specifications before we
>>> start designing new ones
>>>> around it.
>>>
>>> I don't quite agree with this since it assumes this schema is universal
>>> which it definitely is not. It's only a schema that works with RDBMS. It
>>> wouldn't work with an ODBMS or a file system implementation.
>>>
>>
>> The proposal of Caleb is not RDBMS related at all. He first use it for the
>> filesystem persistence of attachement.
> 
> I was responding to Caleb's point about keeping the schema...

>From my perspective we need to rewrite the engine so it's not a mess, get the 
>new engine working
well and with all the kinks worked out, then when each job is in a 
TransactionRunnable, we can
easily swap them out for new ones one at a time. To redesign the schema now 
means we need to
interact with the old XWikiHibernateStore code in order to do migration, that's 
not something I'd
like to do.

> 
>>> Also if there's one thing we shouldn't care about it's the schema. It's
>>> supposed to be opaque for the user and the user has to use the storage API
>>> to access it (and never go directly to the DB). In other words we should be
>>> clear that the schema is not part of the API since that would prevent any
>>> modification of it. I don't think we need this barrier.

"be clear that the schema is not part of the API" yes which is why I want to 
change one at a time.
If we design a new API and a new schema at the same time, we risk making an API 
with limitations and
not noticing it because we make a schema which hides those limitations.

>>>
>>> Conclusion:
>>>
>>> We're doing something really difficult here, which is defining a
>>> transaction API without defining the storage implementation we want to use
>>> and thus without defining how this transaction API would integrate with it.
>>> Right now the most common (if not the only one!) known and standard
>>> transaction API in the java world is JTA and most if not all known storage
>>> implementation support it. Thus if we really want a transaction API separate
>>> of the storage implementation I'd be in favor of looking at JTA and see
>>> whether it would fit our needs.
>>
>>
>>> WDYT?
>>>
>>
>> Proposal of Caleb is over anything, including JTA, it is absolutely not a
>> concurrent of JTA. What he proposed is to helps managing transactional
>> processing properly without having to write boring try/finally code and
>> ensuring we will commit or rollback and we do not mix incompatible
>> "sub"-transactions.
> 
> BTW there are other ways of doing this. In JEE servers it's done with 
> annotations (no "boring" try/finally as you say :)). It's the 
> container-managed part of JTA.

Boring but more importantly unsafe.

> 
> Normally transactions should be demarcated at the level of the services so 
> that several calls to the DB can be in the same Tx for example. At least 
> that's what I used to do in my J2EE days... 
> 
>> I would like to see it extended to support transmission
>> of datas between these "sub"-transaction. In regards to a DBMS, there will
>> be a single transactions and what is proposed by Caleb helps allowing it to
>> be customarily subdivide in smaller steps without having to take care of the
>> overall process of committing/rollbacking and collecting errors. You
>> concentrate on the work that should be done, and the rest is taken care by
>> your TransactionRunnables.
>> This is very interesting in my opinion, but this is not yet mature enough to
>> manage all situations.
> 
> Question: Since this looks generic, isn't there frameworks out there that do 
> this? If not, why?]

There is...   Now  ;)

Caleb

> 
> Thanks
> -Vincent
> 
>> Denis
>>
>>
>>>
>>> Thanks
>>> -Vincent
>>>
>>>>>
>>>>> I noticed some discussions between Denis and you on IRC about all this.
>>> Does you latest findings change the proposal below?
>>>> Everything proposed still holds true but I did add 2 new features.
>>>>
>>>> 1. There is a way for a TransactionRunnable<DBTransaction> to be passed
>>> an instance of DBTransaction
>>>> using a new method called getContext().
>>>>
>>>> 2. There is a new class which serves what I believe is an edge use case.
>>>> Suppose you want to define a TransactionRunnable (we will call it
>>> YourTransactionRunnable) which
>>>> must run inside of a DBTransaction but it must _also_ run after an
>>> instance of MyTransactionRunnable.
>>>> You can make MyTransactionRunnable a
>>> "ProvidingTransactionRunnable<DBTransaction, MyInterface>" and
>>>> then MyTransactionRunnable must run inside of a DBTransaction and we
>>> define YourTransactionRunnable
>>>> as a TransactionRunnable<MyInterface>. This also allows
>>> MyTransactionRunnable to share information
>>>> since YourTransactionRunnable.getContext() will provide an implementation
>>> of MyInterface. Of course
>>>> this feature must be used with care as it provides the tools to write
>>> horrible constructs but IMO it
>>>> is the type of feature which when you need it, there is no other way
>>> around.
>>>>
>>>> Caleb
>>>>
>>>>>
>>>>> Thanks
>>>>> -Vincent
>>>>>
>>>>> On Jan 10, 2011, at 2: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!
>>>>>>
>>>>>> 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?
>>>>>>
>>>>>> 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

Reply via email to