1

tl:dr; This is a bit involved of a problem, any advice is welcome, appreciate reading in advance :)

My coworkers and I have been struggling a bit with an odd behavior in our batch processing application. We recently upgraded it from Grails 1.3.7 to 2.1

The stacktrace is showing the following error:

Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: 
  Cannot insert the value NULL into column 'date_created', 
  table 'dev.dbo.notification_log'; column does not allow nulls. INSERT fails.
  ...
[quartzScheduler_Worker-1] [||] ERROR hibernate.AssertionFailure  - an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
org.hibernate.AssertionFailure: null id in com.virtuwell.domain.NotificationLog entry (don't flush the Session after an exception occurs)
    at org.quartz.core.QuartzScheduler.notifyJobListenersWasExecuted(QuartzScheduler.java:1891)
    at org.quartz.core.JobRunShell.notifyJobListenersComplete(JobRunShell.java:352)
    at org.quartz.core.JobRunShell.run(JobRunShell.java:223)
    at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:546)

[quartzScheduler_Worker-1] [||] ERROR listeners.SessionBinderJobListener  - Cannot flush Hibernate Sesssion, error will be ignored
org.hibernate.AssertionFailure: null id in com.virtuwell.domain.NotificationLog entry (don't flush the Session after an exception occurs)
    at org.quartz.core.QuartzScheduler.notifyJobListenersWasExecuted(QuartzScheduler.java:1891)
    at org.quartz.core.JobRunShell.notifyJobListenersComplete(JobRunShell.java:352)
    at org.quartz.core.JobRunShell.run(JobRunShell.java:223)
    at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:546)

Here is the code of that particular Domain Object (NotificationLog)

class NotificationLog implements Serializable{
    Date dateCreated
    Notification notification
    NotificationDeliveryState deliveryState
    String message

    static mapping = {
       message type: 'text'
    }
}

What's strange, however, is this error doesn't occur EVERY time that domain object is persisted, and we only have one place in the code that object is ever persisted, shown below:

class NotificationLogService {

    boolean transactional = true

    def logNotification(Notification notification, message, deliveryState) {

        def notificationLog = new NotificationLog(
                notification: notification,
                deliveryState: deliveryState,
                message:message
        )

        try{
            notificationLog.save(failOnError:true)
        } catch (Exception e) { // Failure to save a notificationLog should not rollback the calling transaction
            log.error "NotificationLog State:[$deliveryState] for notification:${notification?.id} did not save. Errors: ${notificationLog?.errors}, Message:$message", e
        }


    }
}

We've found a 'hack' of a workaround in the below SO question where we are no longer periodically seeing the error in the logs, by adding this to the Domain Object

static mapping = {
    autoTimestamp true
}

But this isn't the only domain we're seeing with the SAME periodic failure to save (thus, I need to add the mapping to other domains), and if this truly is necessary for dateCreated to function properly in Grails 2.1, I need to add it to a LOT more domains!

Worse, I can't reproduce it in a Unit or Integration test, its only happening on our running Dev and QA instances.

So, 2 Questions:

  1. Does anyone know why this error might be periodically occurring?

  2. If not, is there a way I can globally add this autoTimestamp true mapping to ALL of my project's domain objects (I've been unable to find documentation for how to add it at all, other than to set it to false)

Relevant SO Question: dateCreated, lastUpdated fields in Grails 2.0

Relevant Grails Maillist discussion http://grails.1312388.n4.nabble.com/dateCreated-lastUpdated-in-Grails-2-0-td4337894.html

Relevant Grails docs on autoTimestamp GORM properties http://grails.org/doc/latest/guide/GORM.html#eventsAutoTimestamping

Community
  • 1
  • 1
Will Buck
  • 1,500
  • 16
  • 25

2 Answers2

3

To answer both of the questions:

  1. EDIT Try flush: true while save otherwise autoTimestamp true is the last resort. I have not researched to find out the cause of this issue.

  2. You can set this property in Config.groovy to make it applicable for all domain classes.

    grails.gorm.default.mapping = {
        autoTimestamp true //or false based on your need
    }
    
dmahapatro
  • 49,365
  • 7
  • 88
  • 117
  • Thank you so much for the second answer! I'm not sure I understand your answer to the first question though, could you clarify what you mean by 'We know the behavior is perennial'? Is this something that happens every year in grails as a whole? Or did you mean 'periodical' or 'intermittent', as in I know it isn't consistent and therefore need to start with the 'Last Resort' solution and work from there? – Will Buck Apr 25 '13 at 02:30
  • 1
    Sorry for the misunderstanding. I mingled something else in my response. See my edit now. I can try to test the same if required. – dmahapatro Apr 25 '13 at 04:01
0

Have you tried to manually insert the date when creating a new NotificationLog? Like this:

class NotificationLogService {

boolean transactional = true

def logNotification(Notification notification, message, deliveryState) {

    def notificationLog = new NotificationLog(
            dateCreated: new Date(),
            notification: notification,
            deliveryState: deliveryState,
            message:message
    )

    try{
        notificationLog.save(failOnError:true)
    } catch (Exception e) { // Failure to save a notificationLog should not rollback the calling transaction
        log.error "NotificationLog State:[$deliveryState] for notification:${notification?.id} did not save. Errors: ${notificationLog?.errors}, Message:$message", e
    }


}

}

Matthew Leonard
  • 163
  • 2
  • 8
  • I have not tried that. I assume that it would work as well as the `mapping` addition I demonstrated, but there are two problems with that solution: 1) GORM should already be doing that for me, and does occasionally, and 2) If I were to fix my other failing domain saves, I'd need to find a LOT of places where those domains are persisted and apply this fix, which would be very tedious, and error-prone! Appreciate your suggestion though! – Will Buck Apr 24 '13 at 22:03