I am trying to create a specific type of background processing setup in a grails application.
I am trying to start the job as follows:
int poolSize = 10
ThreadFactory factory = new MyThreadFactory (Executors.defaultThreadFactory())
ExecutorService pool = Executors.newFixedThreadPool (poolSize, factory)
(1..100).each { i ->
pool.submit {
try {
MyDomainClass.withTransaction {
doSomeWork(i)
}
} catch (Exception e) {
log.error "error in job ${i}", e
}
}
}
MyThreadFactory creates threads which have a hibernate session attached for the duration of the thread.
class MyThreadFactory implements ThreadFactory {
ThreadFactory delegate
PersistenceContextInterceptor persistenceInterceptor
MyThreadFactory (ThreadFactory delegate) {
this.delegate = delegate
ApplicationContext applicationContext = ApplicationHolder.getApplication().getMainContext()
persistenceInterceptor = applicationContext.getBean("persistenceInterceptor");
}
Thread newThread (Runnable work) {
return delegate.newThread {
persistenceInterceptor.init()
try {
work.run()
} finally {
persistenceInterceptor.flush()
persistenceInterceptor.destroy()
}
}
}
}
It seems to work, however I will get the following error the first time I run the batch job. (Subsequent jobs run without incident)
groovy.lang.MissingMethodException: No signature of method: static MyDomainClass.save() is applicable for argument types: (java.util.LinkedHashMap) values: [[flush:false]]
Possible solutions: save(), save(java.util.Map), save(java.lang.Boolean), wait(), any(), wait(long)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at ...
I have tried replacing the persitanceInterceptor with MyDomainClass.withNewSession {}, with no effect.
It appears as though GORM methods are not being injected into my domain classes.
Can anyone see what I'm doing wrong, and why running the batch job again allows it to succeed?
@fixitagain For completeness the work takes this form:
doSomeWork = { id ->
MyDomainClass a = MyDomainClass.findById (id)
a.value = lotsOfWork()
a.save()
}
I believe the missing save is a red herring, as I tried wrapping the operating in a transaction, and then get an error saying 'DomainClass.withTransaction(Closure)' is not defined.
It looks like there might be a race condition where the first job fails to run, but all following jobs run successfully after (something?) has finished starting up.
Instead of trying to create your own thread, it might be advisable to use the executor plugin for Grails. It injects the necessary hibernate session to the threads you create, also it is configurable with respect to the executor it uses, number of threads etc. I use it in production with quartz jobs and other scenarios and it works just fine.
Grails Executor Plugin If you have reservations in using it, you can take a look at its code before you write your own threading strategy.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With