cancel
Showing results for 
Search instead for 
Did you mean: 

Activiti TaskQuery query = processEngine.getTaskService().createTaskQuery() returns stale results

alin
Champ in-the-making
Champ in-the-making
Hi,

I have searched through the forum and ran across similar issues but none specific to what I am seeing.

My scenario is pretty simple.

// I query the task I want to update.
Task task = taskService.createTaskQuery().taskId(id).singleResult();

// I update the task
task.setAssignee("new assignee");

// I save the task
taskService.saveTask(task);

// I then read the task again,
Task task = taskService.createTaskQuery().taskId(id).singleResult();

// ## here is where I notice that the task.getAssignee() does not return the "new assignee" that I have just saved
// I create a do-while loop here to keep querying until I see the task has the same assignee I have saved..
1) Question here, how do we know when the task that we have sent of to be saved is "complete"? If I read it immediately, is it expected not to be done? (I am using an H2 database here for activity)

// Now after the do-while in a later part of the process I attempt to query for the task with "new assignee" as the assigned user
TaskQuery query = taskService.createTaskQuery(); 
query.taskAssignee("new assignee");
List<Task> list = query.list();

// When I look at the list here I see that the Task objects that are returned are the old objects Task instance that were previous to saving the task and confirming the change with the do-while loop. IF I wait and try the same query later I get the correct date I expect.

2) How is this possible? I do not have caching turned on. I am curious how other people handle the asynchronous calls that are taking place? Any suggestions or tips would be appreciated.
7 REPLIES 7

alin
Champ in-the-making
Champ in-the-making
An update to my initial request. Edit I have tested this with a mysql database as well in unit testing and can't see any timing issues.

I have taken the advice that is given on a number of these posts to create a unit test to see if I can demonstrate the behavior with that but I have been unsuccessful. Smiley Sad Ideally it would be nice to wait/block until saveTask() is complete, I wouldn't have to implement a do-while loop to make sure that what I sent in via .saveTask(task) was actually saved by reading and verifying it.

Below is my unit test that shows what I was trying to do in my code but works in unit testing.
<java>
public class MyUnitTest {

@Rule
public ActivitiRule activitiRule = new ActivitiRule();

@Test
@Deployment(resources = {"org/activiti/test/my-process.bpmn20.xml"})
public void testImmediateReadAfterSaveTask() {
  ProcessInstance processInstance =
                         activitiRule.getRuntimeService().startProcessInstanceByKey("my-process");
  assertNotNull(processInstance);

  // get the single task
  Task task = activitiRule.getTaskService().createTaskQuery().singleResult();
  String id = task.getId();
  // verify that initially there is no assignee
  assertEquals(null, task.getAssignee());

  // set the assignee to "assignee"
  task.setAssignee("assignee");
  activitiRule.getTaskService().saveTask(task);

  // Retrieve the task associated with that id and verify that save task
                // indeed saved the assignee this is done IMMEDIATELY, on running
                // system it does not return an assignee
  Task t = activitiRule.getTaskService().createTaskQuery().taskId(id).singleResult();
  assertEquals("assignee", t.getAssignee());

  // The problem I have here is that when I query the task I just saved hasn't
                // been updated so I had to result to a do-while like so, which is ugly
  // do {
  //   t = activitiRule.getTaskService()
                //                            .createTaskQuery()
                //                            .taskId(id)
                //                            .singleResult();
  // } while (t.getAssignee() == null
                //                       || t.getAssignee().equalsIgnoreCase("assingee"));

}

@Test
@Deployment(resources = {"org/activiti/test/my-process.bpmn20.xml"})
public void queryForTaskWithAssignee() {
  // then later I try to retrieve using a query to get all the
                // tasks assigned to my assignee

  // I know I don't need the .list() because I am using only 1 task,
                // but in my case I was expecting more than 1
  List<Task> tasks = activitiRule.getTaskService()
                                                       .createTaskQuery()
                                                       .taskAssignee("assingee")
                                                       .list();
  for (Task qTask : tasks) {
   // Again in my system this does not pass,
                        // assignee is set to null, which is before the .saveTask(task) call
   assertEquals("assignee", qTask.getAssignee());
  }

}

}

martin_grofcik
Confirmed Champ
Confirmed Champ
Hi Alin,

similar tests are passing in activiti source:
org.activiti.engine.test.api.task.TaskServiceTest#testSetAssignee*

Please have a look on them and compatre them with yours tests.

Regards
Martin

alin
Champ in-the-making
Champ in-the-making
They are passing for me as well in the unit test scenario like I said above. However in the application itself the same does scenario does not pass and I have to read to make sure the task has been saved. Do you know if there is anyway to verify that .saveTask() has actually persisted to the repo? A subsequent read call after a read should return the saved task state, right?

jbarrez
Star Contributor
Star Contributor
When saveTask() returns, the transaction is committed to the database. It sounds very, very odd what you're seeing.
What kind of transactions have you configured? What is the difference ins etup between your app environment and the unit test?

alin
Champ in-the-making
Champ in-the-making
One difference I am noticing is that I use the JtaProcessEngineConfiguration and the transaction manager is a GeronimoTransactionManager. The database that I am using now is MySQL. This is a bit different than what the unit test is doing. I am still investigating differences that might cause this behavior. As I said in my previous post I changed the unit test to use the MySQL database and that made no difference. I didn't try changing the configuration to use the transaction manager that I am using. Could this make a difference?

A task is queried first using a taskId. Then it is modified using .saveTask(), then later that same task is queried again.  Do these three transactions occur in the same transaction? Does that answer you question-"What kind of transactions have you configured?"

alin
Champ in-the-making
Champ in-the-making
Hi,

So I have been able to write a unit test that reproducing my issue that I am seeing. I didn't modify any of the settings that come packaged in your activiti unit test framework. I believe just running this unit test in your environment should reproduce the same issue.

I have attached a log file of the stacktrace I see from the unit test as well as all the debug info.

<code lang="java">
package org.activiti;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.test.ActivitiRule;
import org.activiti.engine.test.Deployment;
import org.junit.Rule;
import org.junit.Test;

import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;

public class MyUnitTest {

@Rule
public ActivitiRule activitiRule = new ActivitiRule();

@Test
@Deployment(resources = {"org/activiti/test/my-process.bpmn20.xml"})
public void testImmediateReadAfterSaveTask() {
  ProcessInstance processInstance =
    activitiRule.getRuntimeService().startProcessInstanceByKey("my-process");
  assertNotNull(processInstance);

  assertEquals("h2", activitiRule.getProcessEngine().getProcessEngineConfiguration().getDatabaseType());

  // get the single task
  Task task = activitiRule.getTaskService().createTaskQuery()
    .processInstanceId(processInstance.getId()).singleResult();
  String id = task.getId();
  // verify that initially there is no assignee
  assertEquals(null, task.getAssignee());

  QueryThread q = new QueryThread(id);
  // set the assignee to "assignee"
  task.setAssignee("assignee");
  activitiRule.getTaskService().saveTask(task);
                // QUESTION HERE: Shouldn't saveTaks() in the line above be complete by the time I call q.start()? Why is there
                // org.activiti.engine.ActivitiOptimisticLockingException: Task[id=7, name=Activiti is awesome!] was updated by another transaction concurrently
  q.start();

}

public class QueryThread extends Thread {

  protected String taskId;

  public QueryThread(String taskId) {
   this.taskId = taskId;
  }

  // Used to replicate REST call that would query the task and then assign it to someone else
  // and then save the task again
  public void run() {
   Task t = activitiRule.getTaskService().createTaskQuery().taskId(taskId).singleResult();
   assertEquals("assignee", t.getAssignee());
   t.setAssignee("someone-else");
   activitiRule.getTaskService().saveTask(t);
  }
}

}
</code>

The test DOES NOT fail every time, but if I run it 10 times I can get it to fail at least 1 time. If I connect to an external database(MySQL) I can get it to fail 9/10 times. What is causing the failure to occur if the saveTask() is atomic and the QueryThread is assigning it to someone else after it verifies that it has been modified? Can you help me understand which threads are fighting for modification?

Thanks in advanced.

jbarrez
Star Contributor
Star Contributor
Thanks for the code, it's clear now: it's not the saveTask that the thread is competing with, but the teardown of the test.

See the stacktrace, it mentions

" at org.activiti.engine.impl.test.TestHelper.annotationDeploymentTearDown(TestHelper.java:116)\
"

This is the logic that tears down the test afetr running it.

Do a q.join() to wait for the thread and you'll see the expected result.