2012-12-06 8 views
5

GORM funziona bene fuori dalla scatola purché non ci siano lotti con più di 10.000 oggetti. Senza ottimizzazione affronterai i problemi outOfMemory.Grails sospensione sessione in batch

La soluzione più comune è quella di svuotare() e chiara() la sessione ogni n (EGN = 500) oggetti:

Session session = sessionFactory.currentSession 
Transaction tx = session.beginTransaction(); 
def propertyInstanceMap = org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin.PROPERTY_INSTANCE_MAP 

Date yesterday = new Date() - 1 

Criteria c = session.createCriteria(Foo.class) 
c.add(Restrictions.lt('lastUpdated',yesterday)) 
ScrollableResults rawObjects = c.scroll(ScrollMode.FORWARD_ONLY) 

int count=0; 
while (rawObjects.next()) { 
    def rawOject = rawObjects.get(0); 

    fooService.doSomething() 

    int batchSize = 500 
    if (++count % batchSize == 0) { 
     //flush a batch of updates and release memory: 
     try{ 
      session.flush(); 
     }catch(Exception e){ 
      log.error(session) 
      log.error(" error: " + e.message) 
      throw e 
     } 
     session.clear(); 
     propertyInstanceMap.get().clear() 
    } 
} 

session.flush() 
session.clear() 
tx.commit() 

Ma ci sono alcuni problemi che non posso risolvere:

  1. Se utilizzo currentSession, il controller non riesce a causa della sessione è vuota
  2. Se utilizzo sessionFactory.openSession(), la sessione corrente viene comunque utilizzata all'interno di FooService. Per causa posso usare la notazione session.save (oggetto). Ma questo significa che devo modificare fooService.doSomething() e duplicare il codice per un'operazione singola (notazione di graal comuni come fooObject.save()) e operazione batch (session.save (fooObject()) .. notation).
  3. Se utilizzo Foo.withSession {session->} o Foo.withNewSession {session->}, gli oggetti di Foo Class vengono cancellati da session.clear() come previsto. Tutti gli altri oggetti non vengono cancellati(), ciò che porta alla perdita di memoria.
  4. Di causa posso usare sfratto (oggetto) per cancellare manualmente la sessione. Ma è quasi impossibile ottenere tutti gli oggetti rilevanti, a causa dell'autofetching delle assunzioni.

Quindi non ho idea di come risolvere i miei problemi senza rendere il FooService.doSomething() più complesso. Sto cercando qualcosa come withSession {} per tutti i domini. O per salvare la sessione all'inizio (Session tmp = currentSession) e fare qualcosa come sessionFactory.setCurrentSession (tmp). Entrambi non esiste!

Qualsiasi idea è benvenuta!

+2

Questo sembra un lavoro che dovrebbe essere svolto interamente in un metodo di servizio. Se all'interno del metodo di servizio, si utilizza 'currentSession', il controller funziona ancora? – doelleri

+3

Sono d'accordo con @doelleri. I servizi sono il posto giusto per farlo. Inoltre, ricorda che per impostazione predefinita sono transazionali, se vuoi gestire manualmente lo stato, usa 'Domain.withTransaction' e imposta' statical transaction = false' o lascia che il servizio si occupi del commit/rollback. –

+0

Il codice che ho postato lì è già all'interno di un metodo di servizio. Sì, potrei usare il contesto di transazione del metodo di servizio, ma non risolverà il mio problema. @doelleri - La risposta alla tua domanda è: il controller frena, in modo che l'utente non possa fare più nulla nell'applicazione tranne che chiudere il browser.(vedi problema 1) – Waldemar

risposta

0

Un approccio modificato per quello che stai facendo sarebbe:

  1. loop sopra la vostra intera collezione (rawObjects) e salvare un elenco di tutti gli ID per tali oggetti.
  2. Loop sull'elenco di id. Ad ogni iterazione, cerca solo quel singolo oggetto, dal suo id.

Quindi utilizzare la stessa pulizia periodica della cache di sessione come si fa ora.

A proposito, someone else has suggested an approach similar to yours. Ma si noti che il codice in questo collegamento non è corretto; le righe che cancellano la sessione dovrebbero essere all'interno dell'istruzione if, proprio come nella tua soluzione.