Grails StaleObjectException with Dynamic Finder (Hibernate)

245 Views Asked by At

I have a batch job that consistently throws stale object exceptions on a dynamic finder. Ideally I would not be running an ORM solution for this job, but I have no choice. The exception occurs in FormulaTagService, which is being called from FormulaBatchPopulatorService. This application is being run on two servers using one database. One server simply performs batch processing.

My questions are: a) Why would a simple select statement, resulting in a domain object instance where no changes are being made to the object during a given transaction would eventually be persisted in a session, resulting in a stale object exception? b) Is it possible that the sorting being done on formula.tags is being persisted at the end of the transaction, thus causing a staleobjectexception if someone else is modifying the formula on a different server?

Note, I did change the service to be read-only, and I am still get the stale object exception. Any help would be greatly appreciated.

Formula Tag Service

@Cacheable("formulaJob")
      def getFormulaByTeacherTagsOrDefaultBatchJob(Long evaluationTemplateId, List teacherTags) {
          Long formulaByTagsId = existsFormulaWithSameTagsBatchJob(evaluationTemplateId, teacherTags)

          if (DefaultFormulaForEvaluationTemplate.get(evaluationTemplateId) == null && formulaByTagsId ==    
             null) {
               return null;
          }

        Long defaultFormulaId = DefaultFormulaForEvaluationTemplate.get(evaluationTemplateId).formulaId
        return formulaByTagsId ?: defaultFormulaId
    }

    def existsFormulaWithSameTagsBatchJob(Long evaluationTemplateId, List tags){
       // LINE BELOW THROWING STALE OBJECT EXCEPTIONS
        def formulas = Formula.findAllByExtEvaluationTemplateIdAndIsActive(evaluationTemplateId, true)

        for (Formula formula: formulas) {
            def formulaTags = formula.tags
            if (existsTagMatchIgnoringBlankTags(tags, formulaTags)) {
                def id = formula.id
                formula.discard()
                return id
            }
        }
    }

    @CacheEvict(value='formulaJob', allEntries=true)
    def resetTags(){
    }

    def existsTagMatchIgnoringBlankTags(List tagsToCompare, List tagsExisting) {
          if (!tagsToCompare || !tagsExisting) {
              return false
           } 
           else {
               return tagsToCompare?.sort() == tagsExisting?.sort()
            }
    }

FormulaBatchPopulatorService Snippet

   //Doing this below to improve performance of batch processing
         if(index%250==0){
             cleanUpGorm()
             formulaTagService.resetTags()  //cache-evict in formulatagservice
         }
    
         def cleanUpGorm(){
            def session = sessionFactory.currentSession
            session.flush()
            session.clear()
            propertyInstanceMap.get().clear()
        }
1

There are 1 best solutions below

0
On

I believe your answer is correct:

b) Is it possible that the sorting being done on formula.tags is being persisted at the end of the transaction, thus causing a staleobjectexception if someone else is modifying the formula on a different server?

If you sort the tags and it's a list, I believe Groovy does this in place i.e. sorts the original list and returns it. This list would then be persisted at one of the following times:

  1. end of the transaction
  2. end of request
  3. the next time a DB query is executed in the current session (e.g. findBy will flush session)

It's persisted as the index field has likely changed so is considered dirty by GORM / hibernate.

I have a similar issue, resulting in the same problem. I came across this while verifying it to see if others faced it.

Not sure what's going on with the read only part! Is your service transactional?