2011-08-25 12 views
5

Estoy tratando de crear un tipo específico de configuración de procesamiento de fondo en una aplicación de grial.Llamando a Hibernate en hilos de fondo en grises

  • Existe una tamaño fijo piscina hilo sólo durante la duración del lote de puestos de trabajo
  • Un sola sesión es mantenida por cada hilo
  • Cada tarea se ejecuta en un transacción separada

Estoy tratando de comenzar el trabajo de la siguiente manera:

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 crea roscas que tienen una sesión de hibernación unidos durante la duración de la rosca.

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() 
     } 
    } 
    } 
} 

Parece que funciona, sin embargo, obtendré el siguiente error la primera vez que ejecuto el trabajo por lotes. (Trabajos posteriores se ejecutan sin incidentes)

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 ... 

He intentado sustituir el persitanceInterceptor con MyDomainClass.withNewSession {}, sin efecto.

Parece que los métodos GORM no se están inyectando en mis clases de dominio.

¿Alguien puede ver lo que estoy haciendo mal, y por qué ejecutar nuevamente el trabajo por lotes le permite tener éxito?

@fixitagain Para completar el trabajo toma esta forma:

doSomeWork = { id -> 
    MyDomainClass a = MyDomainClass.findById (id) 
    a.value = lotsOfWork() 
    a.save() 
} 

Creo que la falta ahorra es una cortina de humo, como he tratado de envolver el funcionamiento en una transacción, y luego sale un error que dice " DomainClass.withTransaction (Closure) 'no está definido.

Parece que puede haber una condición de carrera donde el primer trabajo no se ejecuta, pero todos los trabajos siguientes se ejecutan correctamente después de (algo?) ha terminado de iniciarse.

+0

¿En qué punto del ciclo de vida de Grails está iniciando sus trabajos? Parece que está ocurriendo antes de que los dominios se hayan decorado con sus métodos dinámicos, etc. –

+0

Los trabajos se desencadenan a partir de una acción de controlador (por ahora), por lo que es bastante después de que los dominios se hayan decorado. Debo señalar que si llamo a todos los trabajos desde el hilo principal, no ocurre ningún problema. – Akusete

+0

bueno, para mí GORM está "adjuntado" ya que está proponiendo guardar (Mapa) como una posible solución. ¿Puedes poner la línea donde estás usando guardar? BTW flush es falso de manera predeterminada, lo que significa que se vaciará al final de la sesión de Hibernate ... – fixitagain

Respuesta

1

En lugar de tratar de crear su propio hilo, puede ser aconsejable usar el complemento ejecutor para Grails. Inyecta la sesión de hibernación necesaria en los hilos que creas, también es configurable con respecto al ejecutor que utiliza, el número de hilos, etc. Lo uso en producción con trabajos de cuarzo y otros escenarios, y funciona muy bien.

Grails Executor Plugin Si tiene reservas para usarlo, puede echar un vistazo a su código antes de escribir su propia estrategia de enhebrado.

+0

He echado un vistazo al plugin ejecutor, también usa persistenceInterceptor para crear una sesión en nuevos subprocesos. Por el momento, probablemente estoy más interesado en asegurarme de que entiendo completamente cómo funciona Grails/Hibernate antes de usar un complemento. Pero gracias por mencionarlo. – Akusete

+0

Después de un segundo vistazo al plugin ejecutor, siempre creará una nueva sesión por trabajo. Lo cual tiene sentido en general, pero no para la tarea específica que estoy buscando. Sin embargo, estoy tratando de usar exactamente el mismo mecanismo para crear una sesión como complemento. – Akusete

+0

¿Por qué no utilizas el complemento Quartz? http://grails.org/plugin/quartz – felipenasc

0

No puedo distinguir entre el código o la convención de nomenclatura, pero ¿está seguro de que está llamando a guardar en una instancia de la clase de dominio?

+0

Supuse que era una 'peculiaridad' con el mensaje de error, llamo desafiantemente a save() en una instancia de clase de dominio. Además, me pregunto si ese era el problema, no explicaría por qué el trabajo terminaría con éxito cuando lo ejecutara por segunda vez. – Akusete

+0

antes de llamar a a.save(), ¿a = a.merge() ayuda? – bluesman

0

La excepción de método faltante implica que está llamando a guardar en la clase, no en la instancia.

editar: GORM ya ha aplicado los métodos adicionales, como puede ver en los nombres de los métodos de solución sugeridos.

Cuestiones relacionadas