javamultithreadinghibernatedining-philosopher

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


Solution

  • 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!