2

After reworking one service method in order to use multithreading I found out that if more than one user is trying to request page (and call service method) many times, the server begins throwing "Cannot get a connection, pool exhausted" exception. Let me provide an example of my service class.

class DocumentService {
   def convertToJSON() {
      docs.collect { doc ->
         taskExecutor.submit({
            Document.withNewSession {
                def json = convertDocumentToJSON(doc)
            }
         } as Callable)
      }.collect { it.get() }
   }

   def convertDocumentToJSON(doc){
      def json = [:]
      // ... fill json with data using doc and GORM requests
      evaluateStatus(json)
      return json
   }

   def evaluateStatus(json){
      //... some work using database through GORM
   }
}

I've been struggling with this issue for more than week and I can't find a solution. I don't understand well how Grails works with sessions, connections and transactions. My guess is following. When convertDocumentToJSON is called, it takes connection from pool (4 users, 25 threads per user = 100 connections) but then they need to call evaluateStatus method, which also tries to obtain connection from pool for its own needs. But pool is exhausted, and no one thread can release connection because all of them are waiting for evaluateStatus. So, there is deadlock. I tried to place SUPPORT propagation type for evaluateStatus, but I get "pooled connection has been closed" exception. Then I thought it might work if I move evaluateStatus call from convertDocumentToJSON and wrote such code

def convertToJSON(){
  docs.collect { doc ->
    taskExecutor.submit({
        Document.withNewSession {
            def json = convertDocumentToJSON(doc)
            evaluateStatus(json) // move call from convertDocumentToJSON
        }
      } as Callable)
  }.collect { it.get() }
}

but in this case I also faced the same error (exhausted pool). Please, give me advice what should I fix in my code

volea
  • 31
  • 5

1 Answers1

0

You need something to throttle the use of connections, try using GPars e.g.

import groovyx.gpars.GParsPool

GParsPool.withPool() {
    docs.collect {
        // do your stuff...
    }
}

There are XXXXParallel versions of the usual groovy collection methods e.g. collectParallel which you can play around with to assist with performance.

Mike W
  • 3,853
  • 2
  • 11
  • 18
  • Thanks. Yes, I tried to use GPars, but it works only with ForkJoinPool. I don't actually remember what problem was, but it didn't meet my expectations. And I'm really curious what's wrong with code above – volea May 29 '17 at 15:32
  • Is the taskExecutor a Java ExecutorService? If so how big is the pool it's created with? – Mike W May 29 '17 at 16:17
  • Yes, it's ExecutorService. Pool size is 30. Connection pool - 100 – volea May 29 '17 at 20:12
  • How long does it take for an exception to be thrown? It sounds like the pool is not waiting for long after all connections have been borrowed before throwing an exception, do you have maxWait defined in your dataSource config? The value should be define in ms. – Mike W May 29 '17 at 20:26
  • All threads stuck for 30 seconds and then server throws an exception. The message says that cannot obtain a connection for 30 seconds. I think it is a default value. My dataSource has no maxWait value defined. If I request page from one browser everything is ok and method takes less then second to complete. – volea May 30 '17 at 09:46