-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 10 posts ] 
Author Message
 Post subject: Sharing a single connection for all sessions
PostPosted: Sat Oct 09, 2004 12:37 am 
Regular
Regular

Joined: Tue Sep 28, 2004 5:18 pm
Posts: 55
Location: Switzerland
I'm attempting to retrofit Hibernate into our Database persisted Desktop application, with a Swing GUI.

I've been running into problems in a couple areas, particularly Connection and Session management. In the interest of keeping this post focused, I'll focus on what seems to be my stickiest problem: Sharing a single connection.

Our database server (FirstSQL) runs within the same virtual machine as the application, and only allows a single connection at any given time. Attempts to spawn further connections give exceptions.

Within a single thread, this isn't an issue. However, since this is a Swing application, there are a few threads being used, sometimes for background db access, sometimes to allow an interactive progress bar to be displayed, but there are cases where two threads may both be performing database access concurrently.

Since Sessions aren't threadsafe, I'm using the ThreadLocal Session pattern.

I'm also using the C3PO Connection proxy to allow preparedstatement caching.

Additionally, some legacy code needs direct access to the connection, since there are enough classes that hibernate must be eased in slowly while keeping the app functional.

Currently I'm passing the Connection to the SessionFactory.openSession() to prevent a second thread from freezing when it attempts to obtain a connection from the Session.

This seems like it would work but here's the bizarre problem I get, seemingly impossible:

The following code executes without error:



Code:
conn.prepareStatement("select * from book");
        assert !conn.isClosed();

        SwingUtilities.invokeAndWait(
                new Runnable() {
                    public void run() {
                        try {

                            assert !conn.isClosed();
                            conn.prepareStatement("select * from book");

                        } catch (Exception e) {
                            throw new DAOException(e);
                        }
                    }
                });



but the following code, differing only by the addition of the first three lines, throws an AssertionError

Code:

URL url = ClassLoader.getSystemResource("sb/data/dao/hibernate/hibernate.cfg.xml");
Configuration configuration = new Configuration().configure(url);
configuration.buildSessionFactory();

conn.prepareStatement("select * from book");
        assert !conn.isClosed();

        SwingUtilities.invokeAndWait(
                new Runnable() {
                    public void run() {
                        try {

                            assert !conn.isClosed();  <-- ASSERTION ERROR HERE!!!
                            conn.prepareStatement("select * from book");

                        } catch (Exception e) {
                            throw new DAOException(e);
                        }
                    }
                });


How is this possible? Note that everything works fine if the SessionFactory is not built. I've even made sure (stepping through hibernate source) that the SessionFactory is not using a C3PO connection. I can think of no reason why the conn would close itself just because it is within an invokeAndWait() method, and particularly not ONLY when i have also built a sessionfactory. It took me a full day to nail down this failure case from a much more complex scenario, and my frustration is nearing the point where i throw up my hands and going back to hand-coded persistence, which I dread.

I would pass this off as a java or c3po bug if it weren't for the fact that this only happens when the session factory is built, somehow it causes the connection to self destruct later on down the line.

HELP! I really want to use hibernate but this problem has me stumped after nearly three days of trying to make threads behave while sharing a single connection.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Oct 09, 2004 6:03 pm 
Regular
Regular

Joined: Tue Sep 28, 2004 5:18 pm
Posts: 55
Location: Switzerland
Trying a different tack. Rather than using ThreadLocal Sessions and trying to get them all to share a connection, I'm now just synchronizing access to a single session. I hope this is the correct approach, since even though there are multiple threads, they are created only in exceptional circumstances, and are used only because the requirements of the Swing asynchronous model mandate their use. This simplifies connection handling, and just leaves the problem of assuring that threads don't contain any logic that might collide.

I would be grateful for any comments on the advisability of this approach or suggestions for alternatives (there must be other people out there using hibernate with Swing...)


Top
 Profile  
 
 Post subject:
PostPosted: Sun Oct 10, 2004 1:48 pm 
Hibernate Team
Hibernate Team

Joined: Tue Sep 09, 2003 2:10 pm
Posts: 3246
Location: Passau, Germany
Synchronizing on one session is okay - but be aware that this may lead to data staleness issues (no Problem if there is only one client accessing the database). If you would do it pure JDBC, you would have to synchronize on the database connection, too - just the same with Hibernate.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 13, 2004 12:54 pm 
Regular
Regular

Joined: Tue Sep 28, 2004 5:18 pm
Posts: 55
Location: Switzerland
Thanks Michael. Things seem to be working pretty well so far.

While (re)implementing the Dao, a related question occurred to me. I have the following code:

Code:
public UserMaster select(Integer userkey) throws DAOException {
        final UserMaster user;
        final String qrystr = "from UserMaster where userKey = :userKey";
        final Query query = HibernateUtil.getSession().createQuery(qrystr);
        try {
            query.setParameter("userKey", userkey);
            user = (UserMaster) query.list().get(0);
        } catch (HibernateException e) {
            throw new DAOException(e);
        }
        return user;
    }


Note the query. The session returned from getSession() is a wrapper for Session that synchronizes access to all Session calls. In the case of createQuery, a Query object is returned, which in turn is used to perform a query. I'm wondering if I need to synchronize the access to the Query object too? I'm thinking probably not, since the query is only used to read data, not modify data, and I'm not too concerned about the possibility of reading dirty data in our app, since we don't have any complex competing processes.

However, in general principle does synchronizing access to the Session include synchronizing access to Query objects it produces? Transaction objects? Anything else?

thanks,
Thom


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 13, 2004 2:04 pm 
CGLIB Developer
CGLIB Developer

Joined: Thu Aug 28, 2003 1:44 pm
Posts: 1217
Location: Vilnius, Lithuania
Did you tried "maxConnections = 1" in pool configuration ?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 13, 2004 5:20 pm 
Regular
Regular

Joined: Tue Sep 28, 2004 5:18 pm
Posts: 55
Location: Switzerland
Yes, I have tried that. If I remember right, I ran into what seemed to be deadlock problems right away, though I haven't puzzled out why.


Let's see... Probably something to do with two threads both wanting access to a single connection through two different sessions. Since in some cases we have threads waiting on the completion of another thread via join(), if the waiting thread holds the connection, a deadlock occurs.

Your first reaction is probably "Sounds like bad thread design." Well, sort of. The problem is, Swing logic MUST execute in the Event dispatch thread. In order to implement an interactive progress bar, there is no alternative but to implement the monitored task in a separate thread, so the swing thread remains free to update the progress bar.

In some cases, the monitored task must make a change to the gui, via invokeLater() or invokeAndWait(). Particularly with invokeAndWait() deadlock is easy to achieve. Another potential deadlock is when such monitored tasks are chained, one waiting on completion of the other. For instance, there may be a monitor delete thread, followed by a monitored loading thread, each of which must execute in sequence, forcing use of the Thread.join() method.

A lengthy explanation but I think it demonstrates how having only a single connection available in a multi-threaded environment can quickly cause deadlock. It might be possible to avoid such situations by never having one thread wait on the completion of another without first closing it's session, but at the moment, the shared session seems like the simpler solution.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Oct 14, 2004 6:23 am 
CGLIB Developer
CGLIB Developer

Joined: Thu Aug 28, 2003 1:44 pm
Posts: 1217
Location: Vilnius, Lithuania
Yes, any blocking can cause deadlocks, it is not trivial to do this stuff in more high level too . Probably there is no easy and good solutions for single user dabase with multithreaded client, may be some "waitfor" graph with thread wrappers can help for deadlock detection.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Oct 14, 2004 11:49 pm 
Regular
Regular

Joined: Tue Sep 28, 2004 5:18 pm
Posts: 55
Location: Switzerland
I'm not sure I understand what you mean. Could you explain the 'waitfor' graph/Thread Wrapper idea in a little more detail? I'd certainly like to find a 'safe' solution to my problem, since I suspect the current solution is eventual going to lead to some difficult to diagnose bugs if we start coding more threads and as I convert more Daos to use hibernate...


Top
 Profile  
 
 Post subject:
PostPosted: Fri Oct 15, 2004 4:32 am 
CGLIB Developer
CGLIB Developer

Joined: Thu Aug 28, 2003 1:44 pm
Posts: 1217
Location: Vilnius, Lithuania
Idea is to use "custom" threads, you can not Override final methods, but you can use custom interface if you control thread creation in application. it is possible to build "waitFor" graph in blocking methods and to detect deadlock. Algorythm is trivial, if you have cycle in grapth then it means you have deadlock and you must abort caller:


Code:
class CustomThread {

Thread my;

CustomThread( MyThread my ){
    my.setCustom(this);
    this.my = my;
}

void join(){

  customThread = ((MyThread)Thread.currentThread()).getCustom();

  Graph.waitFor(customThread, this );
  //throws exeption if cycle is detected.

  my.join();

}

}


It looks very exotic, and it will not detect blocking in "synchronized" block anyway. Probably it is a bad idea, it can produce memory leaks in this graph. Byte code transformation looks like overenginiering to register threads in "waitFor" graph too.

It looks like 1.5 JVM can detect deadlocks, have you tried it ?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Oct 15, 2004 10:16 am 
Newbie

Joined: Sat Sep 20, 2003 1:09 pm
Posts: 8
tpischke wrote:
Yes, I have tried that. If I remember right, I ran into what seemed to be deadlock problems right away, though I haven't puzzled out why.


That shouldn't be to difficult when you can reproduce the deadlock.
Just press <CTRL>-<BREAK> (on windows) or <CTRL>-<\> (linux) in the console where you started your application. You will get a
stacktrace which helps you to identify the deadlock.

With jdk1.5 it is even simpler. Just fire up 'jconsole' (very nice
program!)

Kees.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 10 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.