6 Replies Latest reply on Nov 14, 2009 11:46 PM by saraswati.santanu

    takeTask() race condition

      I have an app that lets users take a task with the following sequence.

      1. taskList = taskService.findGroupTasks(user);
      2. Task task = taskList.get(0);
      3. taskService.takeTask(task.getId(), user);

      I'm finding a race condition if multiple users get the same task at step 2 at the same time. The first user to execute step 3 wins and the second gets the following exception:

      Nov 13, 2009 12:42:32 PM org.hibernate.event.def.AbstractFlushingEventListener performExecutions
      SEVERE: Could not synchronize database state with session
      org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [org.jbpm.pvm.internal.task.TaskImpl#300945]
      at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1792)
      at
      ...
      org.jbpm.pvm.internal.svc.TaskServiceImpl.takeTask(TaskServiceImpl.java:178)

      Is there a better way to do this? Or do I just need catch StaleObjectStateException and try again for the second user?


        • 1. Re: takeTask() race condition

          We have found same error in load testing of our application. However we noticed the data used was very less compared to vusers and duration of load test. But nothing to worry, need to have user retry it.

          • 2. Re: takeTask() race condition
            saraswati.santanu

            Actually your assumption that the first user who executes step 3 wins is not always correct. May be most of the time it happens that way for your case.

            It has to be that the first of the two treads which does a hibernateSession.flush() will win. This is a desired behaviour from hibernate, and this is a very important feature.

            I think the solution you thought of catching StaleObjectStateException is fair enough. Make sure that your "try" boundary is the smallest possible so that you do not catch StaleObjectStateException happening because of some other reason.

            • 3. Re: takeTask() race condition
              kukeltje

              I think eventually jBPM should be able to cope with this situation and return a more batural error like "task already taken by ...."

              • 4. Re: takeTask() race condition
                sebastian.s

                Maybe you should create a JIRA issue asking the developers to investigate this.

                • 5. Re: takeTask() race condition
                  kukeltje

                  no, discuss this on the dev forum first

                  • 6. Re: takeTask() race condition
                    saraswati.santanu

                    This is a nice feature to have, specially if you need to show something on the UI.

                    But, this will be very difficult to achieve as well. With FlushMode.Auto, hibernate can do a flush at any point it thinks suitable. So when you call "takeTask" method, hibernate may not complain at all, but at some later point it, when it tries to clear the action queue, then it will complain. So you never know when this exception comes.

                    One strategy for this might be is to have an exception translator at the DBSession level. But that will be almost as generic as Hibernate exception translator. So whether the Task is taken by somebody or something else happened to the task will be difficult to decide.

                    But the flip side of this is, a generic exception is caught to give some message which is more context specific then re-thrown - this will also pollute the exception stack trace a bit. As a developer, who is using Jbpm API, I wont like that to happen. I personally would prefer to deal in StaleObjectStateException and inspect what is stale and why. Custom exception on top of that may make finding to root cause a bit difficult for a developer.