Re: timing issue?

From: Andrus Adamchik (andru..bjectstyle.org)
Date: Wed Jul 21 2010 - 08:11:53 UTC

  • Next message: Andrus Adamchik: "Re: timing issue?"

    Hi Bryan,

    There can definitely be a small delay updating a peer context in the
    same JVM with the commit data. And this is probably what you are
    observing in the test (adding a delay gives all DataContexts a chance
    to digest the event). If the relationship hasn't been refreshed for an
    hour, this is caused by something else... My guess is that the Company
    object in a peer context had modifications of its own, so Cayenne
    bailed on merging the changes into it. There is a code in ObjectStore
    responsible for merging to-many:

    void processIndirectlyModifiedIDs(Collection indirectlyModifiedIDs) {
            Iterator indirectlyModifiedIt =
    indirectlyModifiedIDs.iterator();
            while (indirectlyModifiedIt.hasNext()) {
                ObjectId oid = (ObjectId) indirectlyModifiedIt.next();

                // access object map directly - the method should be
    called in a synchronized
                // context...
                final DataObject object = (DataObject) objectMap.get(oid);

                if (object == null
                        || object.getPersistenceState() !=
    PersistenceState.COMMITTED) {
                    continue;
                }
    ....
    }

    One possible way around it is to invalidate Company object after
    commit (then on next read it will refetch its relationship).

    Andrus

    On Jul 20, 2010, at 4:05 PM, Bryan Lewis wrote:

    > We released a new internal web app at the start of the month and we've
    > noticed some troublesome behavior. After the insertion or deletion
    > of an
    > object, a to-many relationship can occasionally get out of sync. For
    > example, we have a Company entity with a to-many relationship to
    > Task. After
    > the user adds a task and returns to the task list page, the new object
    > doesn't appear. Sometimes. I've been working around it by adding
    > explicit
    > refetches in the list pages.
    >
    > I've tried to boil it down to a reproducible test case. The code
    > below
    > simply adds and deletes a thousand objects and checks that the size
    > of the
    > list is correct in a different DataContext. One of the assertions
    > will fail
    > at some point in the test. I tried it with and without OSCache and
    > the
    > error happens with both.
    >
    > I wondered if it might be some kind of timing issue. If I added a
    > one-second pause whenever the lists disagreed, that was enough to
    > get them
    > back in sync.
    >
    > I'm not sure this completely explains the behavior we've been
    > seeing. We
    > haven't been hitting the database that hard, with only about a half-
    > dozen
    > users at any one time. Yesterday we had a case where one user's
    > insertion
    > didn't appear in another user's list almost an hour later. It's a
    > start for
    > discussion. It would be good news if I'm missing something.
    >
    >
    > private static final int cRuns = 1000;
    >
    > private Employee user;
    > private RDTaskType taskType;
    >
    > private org.apache.cayenne.access.DataContext createDataContext()
    > {
    > Configuration config = Configuration.getSharedConfiguration();
    > DataDomain domain = config.getDomain();
    > return domain.createDataContext();
    > }
    >
    > void onActionFromTestCaching()
    > {
    > DataContext dc = createDataContext();
    > DataContext dc2 = createDataContext();
    >
    > // Get a few objects needed to populate new tasks.
    > user = DataObjectUtils.objectForPK(dc, Employee.class, 312);
    > taskType = DataObjectUtils.objectForPK(dc, RDTaskType.class,
    > "CLIOR");
    >
    > // A Company is the source of the to-many relationship. Get the
    > same
    > // one in two DataContexts.
    > Company company = DataObjectUtils.objectForPK(dc, Company.class,
    > 5000);
    > Company company2 = (Company)
    > dc2.localObject(company.getObjectId(),
    > null);
    >
    > int nTasksStart = company.getTasks().size();
    >
    > for (int i = 0; i < cRuns; i++) {
    > int nTasks = company.getTasks().size();
    > assert nTasks == nTasksStart + i : nTasks;
    >
    > int nTasks2 = company2.getTasks().size();
    > if (nTasks2 != nTasks) {
    > debug("nTasks2 was " + nTasks2);
    > pause();
    > nTasks2 = company2.getTasks().size();
    > debug("nTasks2 now " + nTasks2);
    > }
    > assert nTasks2 == nTasks : "nTasks = " + nTasks + ",
    > nTasks2 = "
    > + nTasks2; // sometimes fails
    >
    > insertTask(company);
    > }
    >
    > List<Task> tasks = company.getTasks();
    > nTasksStart = tasks.size();
    >
    > for (int i = 0; i < cRuns; i++) {
    > int nTasks = company.getTasks().size();
    > assert nTasks == nTasksStart - i : nTasks;
    >
    > int nTasks2 = company2.getTasks().size();
    > if (nTasks2 != nTasks) {
    > debug("nTasks2 was " + nTasks2);
    > pause();
    > nTasks2 = company2.getTasks().size();
    > debug("nTasks2 now " + nTasks2);
    > }
    > assert nTasks2 == nTasks : "nTasks = " + nTasks + ",
    > nTasks2 = "
    > + nTasks2;
    >
    > deleteTask(tasks.get(nTasks - 1));
    > }
    > }
    >
    > private org.apache.cayenne.access.DataContext createDataContext()
    > {
    > Configuration config = Configuration.getSharedConfiguration();
    > DataDomain domain = config.getDomain();
    > return domain.createDataContext();
    > }
    >
    > private void insertTask(Company company)
    > {
    > Date now = new java.util.Date();
    > DataContext dc = (DataContext) company.getObjectContext();
    >
    > Task task = dc.newObject(Task.class);
    > task.setCreatedBy(user);
    > task.setLastModifiedBy(user);
    > task.setCreatedWhen(now);
    > task.setTargetDate(now);
    > task.setTimeWhen(now);
    > task.setCompany(company);
    > task.setAssignedTo(user);
    > user.addToTasks(task);
    > task.setType(taskType);
    >
    > try {
    > dc.commitChanges();
    > }
    > catch (CayenneRuntimeException e) {
    > e.printStackTrace();
    > }
    > }
    >
    > private void deleteTask(Task task)
    > {
    > DataContext dc = (DataContext) task.getObjectContext();
    > dc.deleteObject(task);
    >
    > try {
    > dc.commitChanges();
    > }
    > catch (CayenneRuntimeException e) {
    > e.printStackTrace();
    > }
    > }
    >
    > private long pauseMillis = 1000;
    >
    > private void pause()
    > {
    > if (pauseMillis > 0) {
    > debug("pausing...");
    > try {
    > Thread.sleep(pauseMillis);
    > }
    > catch (InterruptedException ex) {
    > ex.printStackTrace();
    > }
    > }
    > }



    This archive was generated by hypermail 2.0.0 : Wed Jul 21 2010 - 08:13:31 UTC