Using Spring Events in Grails
Hi all,
I have published a new blog post Using Spring Events in Grails.
Spring Events are basically used to perform events asynchronously.
In my use case I need to save hundreds of records in the database. So I use Spring Events to save records asynchronously.
There is a plugin available in Grails for using Spring Events which save us from XML configurations and also provides a publishEvent method which can be called from a domain class, a controller or a service in the application.
Spring Events work around two classes
1. A class that extends ApplicationEvent
2. A class that implements ApplicationListener
and publishEvent method is used to raise event that will be listen by a listener.
Let’s start by taking an example.
Author
[java]
package com.demo.domain
class Author {
String name
Integer age
}
[/java]
AuthorSaveEvent
[java]
package com.demo.events
import org.springframework.context.ApplicationEvent
class AuthorSaveEvent extends ApplicationEvent {
AuthorSaveEvent(){
super([])
}
}
[/java]
EventListnerService
[java]
package com.demo.service
import com.demo.domain.Author
import com.demo.events.AuthorSaveEvent
import grails.transaction.Transactional
import org.springframework.context.ApplicationListener
@Transactional
class EventListnerService implements ApplicationListener {
public void onApplicationEvent(AuthorSaveEvent event){
(1..10000).each {
new Author(name:"Madhav", age:23).save()
}
}
}
[/java]
UtilController
[java]
package com.demo.controllers
import com.demo.domain.Author
import com.demo.events.AuthorSaveEvent
class UtilController {
def withSpringEvent() {
AuthorSaveEvent authorSaveEvent = new AuthorSaveEvent()
publishEvent(authorSaveEvent)
render "Records saving has been Intailized"
}
def withOutSpringEvent() {
(1..10000).each {
new Author(name: ‘Madhav’, age: 23).save()
}
render "All Records has been saved"
}
}
[/java]
Let us talk about the flow of the application:
1. When we hit the action of a controller then it will raise an event using publishEvent method.
2. Then the event will be listened by onApplicationEvent method of a class which is implementing the ApplicationListener interface.
3. Now the block of code in onApplicationEvent method will be executed on a separate thread.
1. withOutSpringEvent – it will take 6 minutes 20 seconds approx
2. withSpringEvent – it will take 18 seconds approx
Hope this helps !!
– Madhav Khanna
madhav.khanna@intelligrape.com
@Vivek, I totally agree with you on this. Running the same code withOutSpringEvent with a Thread.start() for each event is even much faster than implementing the spring event mechanism. I will say the only mistake in withSpringEvent code is that each save operation is not done asynchronously. Like @Roni says, that would have produced better results, or even 10 operations at a time, would have been better
I just did some bench marking using a sample application built along similar lines.
The performance of DB persistence with/without events was quite comparable. (A difference of 1 second at max between the two.)
I’d believe that the claims made in this post about the performance are false. 🙂
Ah yes! Would be much more interesting if we had generated 10000 events. 🙂
@Roni:
I’d have agreed to that if we were generating 10 separate events that’d then run in 10(or whatever number) different threads. From what I see, we generate one event which is handled in the onApplicationEvent method (I’m sure it doesn’t spawn threads for any logic that’s inside the method.)
This means that we still effectively have one thread that’s doing all the lifting. Please correct me if I’m missing something here.
@Vivek This has more to do with how many concurrent writes are happening on the DB. Async processing will obviously improve performance timings since there would be lesser waiting time for the shared resource. In this case, there might be 10 records being written out by 10 threads rather than the 10 records being written out by a single thread.
Given the fact that Hibernate creates a session if one does not exist, I’m inclined towards that conclusion until the pool does not get exhausted. Correct me if I’m wrong.
I’m not sure if using a background thread is going to improve persistence times as against while using a foreground thread. If yes, I’d be interested in knowing how that happens behind the scenes.
Additionally, I’d also like to know the performance if it ran in the same thread inside a service method as opposed to a method inside a controller. By that, I mean calling authorService.populateDB() or some such from within the withoutSpringEvent action.
The time mentioned in this is the DB persistence time
Does this really improve the DB persistence time?
As far as I could see, it’s just that the user gets the response quicker.