timing issue?

From: Bryan Lewis (brya..aine.rr.com)
Date: Tue Jul 20 2010 - 13:05:30 UTC

  • Next message: Andrew Willerding: "Memory Leak?"

    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 : Tue Jul 20 2010 - 13:06:13 UTC