[jbpm-dev] Help with ht problem? (Re: BZ 853019)

Marco Rietveld mrietvel at redhat.com
Thu Sep 13 03:57:04 EDT 2012


Hi Maciej,

Just so were on the same page, (and for clarification to others reading 
along), this is what we're talking about (I think  :) ):

1. Changing the TaskServiceSession so that instantiation starts a 
transaction and disposal ends the transaction. (Currently, tx's in 
human-task are started at various different points depending on the 
operation requested).
2. Changing the LocalTaskService so that a TaskServiceSession is 
instantiated and disposed with every operation.


The main reason to do 2 is because otherwise, programs that are already 
written that use the LocalTaskService might break. At this point, users 
currently using the LocalTaskService expect that the transaction 
(whether it's a local or JTA tx) will be ended by the LocalTaskService 
at the end of an operation.

If we only do 1 (change tx behaviour) but not 2, then a tx will be 
opened when the LocalTaskService is initiated and a tx will only be 
closed when the LocalTaskService is closed. (All the tx logic inbetween 
will not fire, because the tx mgr will see that there's an active tx and 
not do anything to modify the status of the active tx.).

Except, for JTA tx's -- and probably also for Spring tx's -- this isn't 
true. Something else that the user is doing could then end those tx's, 
and that would break the LocalTaskService instance (which expects to be 
able to close a tx when it disposes -- but can't, because the user 
already has. ) True, in this situation everything would work (because of 
the inner tx logic) until the LocalTaskService was disposed.

Besides the technical consideration above, there's also the fact that 
users now expect the behaviour of the LocalTaskService to be 
transactional. That means that if they're using the LocalTaskService, 
and an exception is thrown halfway through, that the things that have 
been already done using the LocalTaskService will .. well, be done.

If we don't do 2 (ensure similar tx behaviour), then the following 
situation can occur, and users will definitely be angry about this:

1. User initializes LocalTaskService
2. User starts process (where by 5 tasks are created).
3. User completes task 1 (of 5) via LocalTaskService
4. User completes task 2 (of 5) via LocalTaskService
5. Exception is thrown by something, and we exit the stack.

If we don't do ensure similar tx behavior, then a. none of the 5 tasks 
will have been saved and b. 2 of the 5 tasks (which won't even exist) 
won't have the status "Completed".


--------------------

On another note, I'm realizing that what I'm proposing above is not 
something we can do anyways.

The problem, of course, comes back to the fact that the API/DTO object 
is our entity. That means that if we go through with the 1,2 (tx by 
init/dispose and new TaskServiceSession per op), then we can have the 
following:

1. LocalTaskService initiated, etc..
2. User calls LocalTaskService and gets a Task object back.
    - which means: a. entitymanager opened, b. tx opened, c. retrieve 
task d. tx closed e. em closed.
3. User does something else with LocalTaskService
   - which means.. (see above)
4. User tries to access something in the Task object -- but something in 
a (lazily-loaded) collection that of course hasn't been loaded yet.
5. Proxy instance of collection element tries to retrieve the element 
using the em.. that was closed in 2e.
6. "Boom!" as they say, or in other words, exception and User doesn't 
understand wtf is going on.

So it looks like we're  back to my original Option 2 or 3:
2. Run through option tree in order to force loading
3. tx around session.write().

I'm favoring option 3, mostly because it's the least work and probably 
the most robust. Obviously, neither option involves changing the 
LocalTaskService.


Thanks,
Marco


13-09-12 09:15, Maciej Swiderski:
> Marco, why we need to do that? Can't we just use it as is, meaning 
> that several operations will be included in same transaction, like 
> start, complete for example? Will this break on query level or ...
> I am not sure how often it is used like that - two task service 
> operations in single task service session?
>
> I can see that in some cases beneficial (like all or nothing) and in 
> some cases not really welcome (inserting users/groups - one fails roll 
> backs all others).
>
> Thanks
> Maciej
>
> On 12.09.2012 23:56, Marco Rietveld wrote:
>> Maciej,
>>
>> I was thinking about that -- but doing that breaks the 
>> LocalTaskService (or otherwise, we have to rewrite LocalTaskService 
>> so that it opens a new TaskServiceSession for every operation, just 
>> the way the TaskServerHandler handles that).
>>
>> Actually, the more I think about that, the better it sounds. It might 
>> impact the performance of LocalTaskService slightly, but it will be 
>> worth it, I think.
>>
>> Thanks,
>> Marco
>>
>> 12-09-12 17:16, Maciej Swiderski:
>>> Marco, another way could be to ensure transaction is started when 
>>> taskservicesession is created and closed (committed/rolledback) when 
>>> taskservicesession is disposed, I did that for a fix on 
>>> https://issues.jboss.org/browse/JBPM-3763 which is on postgresql and 
>>> worked fine. So that way we ensure that session.write is in 
>>> transaction as well. Of course not tested all possible cases but 
>>> worked for main ones.
>>>
>>> Wdyt?
>>>
>>> Maciej
>>>
>>> On 12.09.2012 12:22, Marco Rietveld wrote:
>>>> Hi Maciej and Mauricio,
>>>>
>>>> I'm struggling to find a good solution for a problem and was hoping 
>>>> to get your advice about the following.
>>>>
>>>>
>>>> The human-task service uses it's entities as DTO's, namely the Task 
>>>> class/instances.
>>>>
>>>> However, we use Hibernate, which uses lazy-loading, which means 
>>>> that Hibernate substitutes proxy instances in collections until the 
>>>> actual collection elements are needed.
>>>>
>>>> With Hibernate 3, we miraculously were able to avoid any large 
>>>> problems. However, testing with EAP 6 has uncovered situations, 
>>>> primarily with postgresql, in which this strategy (entity as DTO) 
>>>> just won't work.
>>>>
>>>> The problem is that even if all the "persistence" work is done in 
>>>> one tx, the collections are still lazily-loaded. That means if a 
>>>> task service operation has to return a Task instance, that the 
>>>> serialization of the Task object (when it's being sent) triggers 
>>>> the loading of entities. Due to postgresql's Large Object facility, 
>>>> this means that there needs to be a transaction around this action. 
>>>> Because we don't surround the session.write(resultsCmnd); operation 
>>>> with a tx, we get an exception.
>>>>
>>>> (To tell the truth, I don't understand why this worked with 
>>>> Hibernate 3.. )
>>>>
>>>> As I've been writing this, I've come up with a couple of solutions:
>>>>
>>>> 1. Turn off lazy-loading for all entities.
>>>> 2. Force the loading of all relevant entities by going through the 
>>>> object tree (task.getPeopleAssignments().size(), etc.. )
>>>> 3. Put a transaction around session.write(resultsCmnd);
>>>>
>>>> Option 1 has a big impact on performance, especially if we start 
>>>> talking about high-volumes.
>>>> Option 2 has a slightly larger impact on performance  but Option 3 
>>>> seems a little bit ugly to me.
>>>>
>>>>
>>>> Are there any options I missed? Any advice or comments?
>>>>
>>>> Thanks,
>>>> Marco
>>>>
>>>> PS. This is (IMHO) one of the reasons we need to rewrite 
>>>> human-task. I've been working on a proposal/POC, but the important 
>>>> thing is that certain problems that we have now aren't also present 
>>>> in the rewritten version.
>>>>
>>>
>>
>>
>


-- 
jBPM/Drools developer
Utrecht, the Netherlands



More information about the jbpm-dev mailing list