2 Replies Latest reply on Mar 20, 2006 10:25 AM by jesse_sweetland

    Long Transactions

    jesse_sweetland

      jBPM version: 3.1
      App server: JBoss 4.0.3

      I was looking at the Hibernate mapping files and none of the associations that I looked at include "lock" in the cascade. So, for example, if I call Session.lock on a TaskInstance, only that TaskInstance is reassociated with the Hibernate session. If that TaskInstance has associations that are initialized under another Hibernate session (using the session-per-request-with-detached-objects pattern) then they are not reassociated with the new session unless I explicitly lock each associated object.

      The problem I am having is that in one request (session1) I traverse the graph back up to the root token to construct a "canonical name" for a token (process/state/sub-process/super-state/state/task). This initializes the token's parent tokens, the tokens' respective nodes and process instances, the process instances' process definitions and super-tokens, and so forth on up to the root token. Then, in another request (session2), I signal the token, which apparently traverses the tree back up to the root trying to load events at each node. The problem is that all of those objects were loaded in and are still associated with session1, which is now closed, resulting in a LazyInitializationException. To fix this, I have two options:

      1. Call session.refresh() instead of Session.lock() on the token I want to signal and start over
      2. Traverse the tree all the way back up to the root and lock each object

      The first solution seems reasonable, except for the note in the Hibernate 3.1 JavaDoc indicating that it is inadvisable to use the Session.refresh method to implement long transactions. I suspect this is because one might unintentionally lose changes in associated objects since they are discarded, or perhaps the concern has more to do with performance (issuing all of those queries again). The second solution is just a messy hack that could instead be accomplished automatically in Hibernate with a cascading lock operation on all of the associations.

      It is understandable that the associations do not cascade lock operations, especially since it's dealing with hierarchical/recursive data (as there is a possibility of NonUniqueObjectExceptions). But has any thought gone into long transactions and how best to handle them in jBPM? What is the preferred method?

      Thanks,

      - Jesse

        • 1. Re: Long Transactions
          koen.aers

          Jesse,

          Why exactly would you need long transactions? And what do you exactly mean with it? Can you give an example?
          Normally the transactions in jBPM are not long at all, they only serve to calculate the new state of the process instance after giving one or another token the signal to move on. To make these transactions long running, you would have to develop a node or an action that invokes long running operations. But you have to realize that, as the calculations of the new state are executed in the client thread, this would freeze your client application. The workaround for this is to use asynchronicity, but then (at least for jBPM) you don't have long running transactions anymore.

          Regards,
          Koen

          • 2. Re: Long Transactions
            jesse_sweetland

            The transactions I'm referring to are not jBPM transactions per se, they are transactions for the client application I am writing that happen to operate on jBPM objects. For example:

            Request 1: User retreives a list of Tasks
            Request 2: User clicks on a task and views information for that task
            Request 3: User clicks on the "Action" tab to enter a comment and choose a transition
            Request 4: User enters a comment, chooses a transaction, and clicks "Finish"

            The task is loaded in request 1 and cached as a part of a list in a session-scoped managed bean. The user clicks on the task, which sets the "selectedTask" property of the session-scoped managed bean equal to the task instance that was clicked on. The user then proceeds to navigate a serious of read-only views that operate on "selectedTask" task instance (#{sessionBean.selectedTask.name}, for example). Finally, in Request 4, the user choosed to do something with the task, but since there are many associated entities loaded in previous requests I get a LazyInitializationException.

            I gather based on your response that the preferred behavior would be to store the task instance ID in the session bean and load a fresh copy on each request, making each request a short transaction (most read-only with some being updates). That would certainly work, but it's not very efficient, especially if associations have to be lazily initialized on every request (task instance variables, for instance).

            What I've ended up doing is using the cached TaskInstance for all of my read-only operations, and calling Session.refresh() on the TaskInstance before performing updates, effectively making the update transactions short transaction (read-modify-commit in a single request). A cascading lock operation would be nice, so at the end I could simply do version checks (or use LockMode.NONE and rely on optimistic locking). Session.refresh() is a little dangerous because it will read the state from the database without a version check and may result in some of the user's changes being lost without notification. Session.update() isn't an option because there are many-to-one associations that would have to cascade, and it is not reasonable to expect update operations to cascade in those cases.

            - Jesse