简体   繁体   中英

Handling connection pool exhaustion and avoiding deadlock in Hibernate/C3P0

My old application following chain to query my database: Spring Tx -> Hibernate -> C3P0 . Now I need to implement new features based on the existing architecture.

I normally enter a transactional context by using either the @Transactional annotation or manually invoking the PlatformTransactionManager .

Sometimes, to perform asynchronous and big data operations, I open a stateless session using the SessionFactory API. We never had any additional problem as our thread pool is well controlled

For the first time, my requirement is to perform several DB operations in parallel to speed up performance. I have a doubt about it, because I am extremely careful about multithreaded operations.

For each entity in the database, I can perform a reconciliation operation on a separate thread. But each reconciliation operation uses a pair of connections for each of the two threads it spawns. So there are basically 4 connections for each thread.

Multithreading class teaches students that in order to prevent a deadlock (eating philosophers issue), resources shall be acquired in a transactional way: once you have acquired a fork, if you cannot acquire the second in a reasonable time, release the first and try again.

My question is simple. Given the SessionFactory API, how can I write code that won't wait indefinitely for 4 connections from c3p0 if the pool is full? I mean I need 4 StatelessSession s only if there is room for 4, otherwise I can wait and retry.

As far as I know, the SessionFactory API is blocking and does not allow to set a watchdog

Currently the idea is to use the plain old workaround.

In general, in Java world, if an API does not offer a watchdog method for acquiring a resource, you can delegate that to a Future , which offers get(int timeout, TimeUnit timeUnit) API to constrain the execution.

Step 1: watchdog timer for resource acquire

So basically, how do I get a stateless session within the bounds of a timeout?

private Future<StatelessSession> getStatelessSession(SessionFactory sessionFactory)
{
    return asyncTaskExecutor.submit(new Callable<StatelessSession>()
    {

        @Override
        public StatelessSession call() throws Exception
        {
            return sessionFactory.openStatelessSession();
        }
    });
}

try {
    StatelessSession session = getStatelessSession(sessionFactory).get(3000,TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
    // go to step 2
}

Step 2: eat the way a philosopher would do

As I said, my problem looks basically like the dining philosopher, except that we don't just have two forks but spoons and knives to take for a total of 4.

StatelessSession session1, session2, session3, session4;
for (int i=0; i<MAX_ATTEMPTS;i++) {
    try {
        session1 = tryGetSessionOrBoooooom();
    } catch(TimeoutException ex) {
        continue;
    }
    try {
        session2 = tryGetSessionOrBoooooom();
    } catch(TimeoutException ex) {
        session1.close();
        continue;
    }
    try {
        session3 = tryGetSessionOrBoooooom();
    } catch(TimeoutException ex) {
        session2.close();
        session1.close();
        continue;
    }
    try {
        session4 = tryGetSessionOrBoooooom();
    } catch(TimeoutException ex) {
        session3.close();
        session2.close();
        session1.close();
        continue;
    }
}

What happens if you go beyond MAX_ATTEMPTS is up to you, developer!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM