Handling transaction rollbacks with Grails persistence events

606 views
Skip to first unread message

Sitati Kituyi

unread,
Feb 4, 2016, 8:42:59 AM2/4/16
to Grails Dev Discuss
Hi all,

I've got a scenario where I want to detect all CRUD operations on some domains in my grails app (we're on 2.4.3), and automatically invoke some business logic. After researching my options, I've found and tested two general approaches that both work to an extent:
- Using afterInsert, afterUpdate and afterDelete in the relevant domains
- Using an AbstractPersistenceEventListener, filtering for the relevant classes in the onPersistenceEvent method

The problem I now face is that in transactional blocks, both these approaches result in the event handler being invoked immediately after a domain.save() is invoked. If a RuntimeException occurs later in the transaction, the transaction is rolled back and the domain is not persisted, but this business logic will have already been invoked. I'm testing with code that looks like this:

MyDomain.withNewTransaction {
   
new MyDomain(name:'Jeopardy').save() // << Event is thrown here
    sleep
(20000) // << I added this just so that I could assert through the front-end that the business logic has indeed been invoked before the transaction ended
   
throw new Exception("but it fails!") // << this causes rollback, so the MyDomain instance is not saved
}



The ideal solution I'm hoping to find is a setup that allows for a 1:1 match between the invocation of the handler and db level inserts: whether in a transactional context or not, only raise the event when an insert/update is actually committed at database level. Failing this, I'd settle for some way to detect these rollbacks and call some handler, knowing which entities were affected by the rollback (in other words, an 'undo' method for the business logic that was invoked).

Any ideas? Thanks!

Robert Oschwald

unread,
Feb 4, 2016, 9:02:58 AM2/4/16
to grails-de...@googlegroups.com
Maybe the audit-logging plugin is an option, with „handlersOnly = true“ setting if needed.

Robert






--
You received this message because you are subscribed to the Google Groups "Grails Dev Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to grails-dev-disc...@googlegroups.com.
To post to this group, send email to grails-de...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/grails-dev-discuss/ca5b8426-faca-4bf4-a821-3759fe6eebee%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Arvind Sachdeva

unread,
Feb 4, 2016, 9:20:36 AM2/4/16
to grails-de...@googlegroups.com

If your business logic involves updating Database, then it should be fine. But if it involves archiving files, sending mails or raising audit events etc then either you need to have an explicit rollback(for example files can be un-archived in case of rollback, and a followup audit event can be raised) for the business logic, or you need to queue the tasks in afterUpdate (and related functions) and invoke them at commit. For example mails should be sent only after all DB operations has been committed.

 

It depends on what is the business logic that gets executed at each db commit. If anybody can suggest a better approach please do because AFAIK business logic executed prior to commit needs to be manually handled in case of rollback.

 

Regards

Arvind Sachdeva.

--

Sitati Kituyi

unread,
Feb 4, 2016, 9:30:15 AM2/4/16
to Grails Dev Discuss, arvinds...@hotmail.com
Thanks for the quick replies Robert & Arvind.

I'll set up a quick test app using the audit-logging plugin to see if it behaves as desired with the code snippet I gave above. If it does, I'll have a look at source, maybe I can find something in their approach that shows how they handle this.

Arvind, I agree with the rules you described. Our business logic is actually not db related, but it's a safe operation that can be rolled back, similar to your example of un-archiving files. What I don't know is how to detect transaction rollback in order to run this 'undo' operation. Is there an event I can listen to for that? Alternatively, you also suggested queueing things, and only executing when the transaction is committed. Again, how do I detect this, is there an event from which I can get the list of domains that were committed?

I'm ok with either approach (queue and execute on "transactionSuccess" event, or execute immediately and roll back on "transactionRollback" event), but I can't find any docs about either of those events being available.

To unsubscribe from this group and stop receiving emails from it, send an email to grails-dev-discuss+unsub...@googlegroups.com.
To post to this group, send email to grails-d...@googlegroups.com.

Sitati Kituyi

unread,
Feb 4, 2016, 9:57:18 AM2/4/16
to Grails Dev Discuss
Had a look at the audit-logging plugin source, it's using the same AbstractPersitenceEventListener approach that I've already tested: https://github.com/robertoschwald/grails-audit-logging-plugin/blob/1.x_maintenance/grails-audit-logging-plugin/src/groovy/org/codehaus/groovy/grails/plugins/orm/auditable/AuditLogListener.groovy

This will not work - the event is thrown on save() instead of at the transaction boundary.

There are open issues in the repo about the plugin's behaviour on rolled-back transactions: https://github.com/robertoschwald/grails-audit-logging-plugin/issues/88

Unfortunately that plugin won't help here.

To simplify the question (for anyone skimming through this): is there a way to detect transaction commit or rollback as an event?

Robert Oschwald

unread,
Feb 4, 2016, 10:17:47 AM2/4/16
to grails-de...@googlegroups.com
With Hibernate, an EmptyInterceptor would work, as you got access to the Transaction in afterTransactionCompletion(), but I do not know a GORM way to get the same thing in an ORM agnostic way.
> --
> You received this message because you are subscribed to the Google Groups "Grails Dev Discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to grails-dev-disc...@googlegroups.com.
> To post to this group, send email to grails-de...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/grails-dev-discuss/206a425f-5b0c-479f-8373-e5134ea02cc7%40googlegroups.com.

Sitati Kituyi

unread,
Feb 9, 2016, 7:34:11 AM2/9/16
to Grails Dev Discuss
This looks a promising direction, thanks Robert.

I can see from the Transaction javadoc that we'll be able to tell whether the transaction rolled back or was successful, but is there an API that lets me see the affected domain instances in the event of a rollback/commit?
> To unsubscribe from this group and stop receiving emails from it, send an email to grails-dev-discuss+unsub...@googlegroups.com.

Jimmy Mbiyu

unread,
Feb 26, 2016, 4:36:59 AM2/26/16
to Grails Dev Discuss
Hello,

You can achieve your goal by using Hibernate Events as detailed here https://grails.github.io/grails-doc/latest/guide/GORM.html#eventsAutoTimestamping

In light of this Hibernate ticket, https://hibernate.atlassian.net/browse/HHH-1582, make sure you use Hibernate 4. You can install the Hibernate 4 plugin as detailed in the Grails 2.4.3 release notes if you are not already using Hibernate 4 - https://grails.org/wiki/2.4.3%20Release%20Notes. The Events API has changed so the classes/interfaces to look out for are in the org.hibernate.event.spi package.

Regards,
Ndung'u.  
Reply all
Reply to author
Forward
0 new messages