You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-user@db.apache.org by Trejkaz <tr...@trypticon.org> on 2013/10/01 11:57:53 UTC

Thread-safe shutdown while other threads might try to reopen

Hi all.

We have an application where there are multiple different contexts and
each is isolated from the others (like a webapp). Individual users
close and open databases at will. They're opening their own database
instance rather than sharing an instance directly, in an attempt to
improve the isolation.

When all users have disconnected from the database, the admin expects
to be able to move the database files aside, so we have to shut the
database down when nobody is connected to it.

To try and achieve that, we keep our own static reference count. This
is done somewhat like this (only the real version uses ConcurrentMap,
so there is a lot more boilerplate):

    public class Database {
        private static final Map<String, Integer> connectionCountMap =
            new HashMap<String, Integer>();
        private static final Object connectionCountLock = new Object();

        private final String path;
        private final Connection conn;

        public Database(String path) throws SQLException {
            this.path = path;
            conn = DriverManager.getConnection("jdbc:derby:" + path);
        }

        public void close() throws SQLException {
            conn.close();
            decConnectionCount(path);
        }

        public void incConnectionCount(String path) {
            Integer count = connectionCountMap.get(path);
            if (count == null) {
                count = 1;
            }
            connectionCountMap.put(path, count + 1);
        }

        public void decConnectionCount(String path) {
            Integer count = connectionCountMap.get(path);
            int countAfterDec = count - 1;
            if (countAfterDec > 0) {
                connectionCountMap.put(path, countAfterDec);
            } else {
                connectionCountMap.remove(path);
                try {
                    DriverManager.getConnection("jdbc:derby:" + path +
                         ";shutdown=true");
                } catch (SQLException e) { /* expected */ }
            }
        }
    }

What we are finding is that if multiple threads are accessing the same
database like this, sometimes one of them will be in the middle of
opening a connection while another thread is shutting down the
database. I ended up debugging this into Derby and found that the
thread opening the database would get a null database reference. Derby
would then throw an exception saying the database not found.

So the end result is that despite the database files existing on disk,
Derby would report that the database was not found, which is wrong in
itself...

Anyway, setting the incorrect exception message aside, it seems like
Derby is thread-hostile in this regard. The docs do say only to
shutdown when you know the JVM won't open the database again. The
problem with this is that you never know that, so you can never shut
it down--so the files never get unlocked.

I know I can just slap a synchronized block around these two methods
to make it bulletproof, but there are two problems with this:
  (1) synchronized is slow and Derby's shutdown is not fast at all...
  (2) I don't know what other apps might be open in the same JVM at the same.

So I wonder if anyone with experience in this sort of thing has any
good tips on how to get around the problem.

Also, we're on Derby 10.8, so if anything has been improved in this
regard in the later releases, that would be good to know - it might
just be enough impetus to switch.

TX

Re: Thread-safe shutdown while other threads might try to reopen

Posted by Knut Anders Hatlen <kn...@oracle.com>.
"Dag H. Wanvik" <da...@oracle.com> writes:

> Hi,
>
> I don't believe this situation has changed since 10.8. I agree this
> situation could be improved, so I suggest filing an improvement
> request in JIRA. It would also help if you could supply a self
> contained repro (not strictly necessary, as I believe the description
> is clear enough, but it might just increase the chance of some
> developer looking at it...)

There's already a JIRA issue tracking such an improvement:
https://issues.apache.org/jira/browse/DERBY-4447

Work on the issue seems to have stalled, though.

>
> Thanks,
> Dag
>
> On 01. okt. 2013 11:57, Trejkaz wrote:
>> Hi all.
>>
>> We have an application where there are multiple different contexts and
>> each is isolated from the others (like a webapp). Individual users
>> close and open databases at will. They're opening their own database
>> instance rather than sharing an instance directly, in an attempt to
>> improve the isolation.
>>
>> When all users have disconnected from the database, the admin expects
>> to be able to move the database files aside, so we have to shut the
>> database down when nobody is connected to it.
>>
>> To try and achieve that, we keep our own static reference count. This
>> is done somewhat like this (only the real version uses ConcurrentMap,
>> so there is a lot more boilerplate):
>>
>>      public class Database {
>>          private static final Map<String, Integer> connectionCountMap =
>>              new HashMap<String, Integer>();
>>          private static final Object connectionCountLock = new Object();
>>
>>          private final String path;
>>          private final Connection conn;
>>
>>          public Database(String path) throws SQLException {
>>              this.path = path;
>>              conn = DriverManager.getConnection("jdbc:derby:" + path);
>>          }
>>
>>          public void close() throws SQLException {
>>              conn.close();
>>              decConnectionCount(path);
>>          }
>>
>>          public void incConnectionCount(String path) {
>>              Integer count = connectionCountMap.get(path);
>>              if (count == null) {
>>                  count = 1;
>>              }
>>              connectionCountMap.put(path, count + 1);
>>          }
>>
>>          public void decConnectionCount(String path) {
>>              Integer count = connectionCountMap.get(path);
>>              int countAfterDec = count - 1;
>>              if (countAfterDec > 0) {
>>                  connectionCountMap.put(path, countAfterDec);
>>              } else {
>>                  connectionCountMap.remove(path);
>>                  try {
>>                      DriverManager.getConnection("jdbc:derby:" + path +
>>                           ";shutdown=true");
>>                  } catch (SQLException e) { /* expected */ }
>>              }
>>          }
>>      }
>>
>> What we are finding is that if multiple threads are accessing the same
>> database like this, sometimes one of them will be in the middle of
>> opening a connection while another thread is shutting down the
>> database. I ended up debugging this into Derby and found that the
>> thread opening the database would get a null database reference. Derby
>> would then throw an exception saying the database not found.
>>
>> So the end result is that despite the database files existing on disk,
>> Derby would report that the database was not found, which is wrong in
>> itself...
>>
>> Anyway, setting the incorrect exception message aside, it seems like
>> Derby is thread-hostile in this regard. The docs do say only to
>> shutdown when you know the JVM won't open the database again. The
>> problem with this is that you never know that, so you can never shut
>> it down--so the files never get unlocked.
>>
>> I know I can just slap a synchronized block around these two methods
>> to make it bulletproof, but there are two problems with this:
>>    (1) synchronized is slow and Derby's shutdown is not fast at all...
>>    (2) I don't know what other apps might be open in the same JVM at the same.
>>
>> So I wonder if anyone with experience in this sort of thing has any
>> good tips on how to get around the problem.
>>
>> Also, we're on Derby 10.8, so if anything has been improved in this
>> regard in the later releases, that would be good to know - it might
>> just be enough impetus to switch.
>>
>> TX


Re: Thread-safe shutdown while other threads might try to reopen

Posted by "Dag H. Wanvik" <da...@oracle.com>.
Hi,

I don't believe this situation has changed since 10.8. I agree this 
situation could be improved, so I suggest filing an improvement request 
in JIRA. It would also help if you could supply a self contained repro 
(not strictly necessary, as I believe the description is clear enough, 
but it might just increase the chance of some developer looking at it...)

Thanks,
Dag

On 01. okt. 2013 11:57, Trejkaz wrote:
> Hi all.
>
> We have an application where there are multiple different contexts and
> each is isolated from the others (like a webapp). Individual users
> close and open databases at will. They're opening their own database
> instance rather than sharing an instance directly, in an attempt to
> improve the isolation.
>
> When all users have disconnected from the database, the admin expects
> to be able to move the database files aside, so we have to shut the
> database down when nobody is connected to it.
>
> To try and achieve that, we keep our own static reference count. This
> is done somewhat like this (only the real version uses ConcurrentMap,
> so there is a lot more boilerplate):
>
>      public class Database {
>          private static final Map<String, Integer> connectionCountMap =
>              new HashMap<String, Integer>();
>          private static final Object connectionCountLock = new Object();
>
>          private final String path;
>          private final Connection conn;
>
>          public Database(String path) throws SQLException {
>              this.path = path;
>              conn = DriverManager.getConnection("jdbc:derby:" + path);
>          }
>
>          public void close() throws SQLException {
>              conn.close();
>              decConnectionCount(path);
>          }
>
>          public void incConnectionCount(String path) {
>              Integer count = connectionCountMap.get(path);
>              if (count == null) {
>                  count = 1;
>              }
>              connectionCountMap.put(path, count + 1);
>          }
>
>          public void decConnectionCount(String path) {
>              Integer count = connectionCountMap.get(path);
>              int countAfterDec = count - 1;
>              if (countAfterDec > 0) {
>                  connectionCountMap.put(path, countAfterDec);
>              } else {
>                  connectionCountMap.remove(path);
>                  try {
>                      DriverManager.getConnection("jdbc:derby:" + path +
>                           ";shutdown=true");
>                  } catch (SQLException e) { /* expected */ }
>              }
>          }
>      }
>
> What we are finding is that if multiple threads are accessing the same
> database like this, sometimes one of them will be in the middle of
> opening a connection while another thread is shutting down the
> database. I ended up debugging this into Derby and found that the
> thread opening the database would get a null database reference. Derby
> would then throw an exception saying the database not found.
>
> So the end result is that despite the database files existing on disk,
> Derby would report that the database was not found, which is wrong in
> itself...
>
> Anyway, setting the incorrect exception message aside, it seems like
> Derby is thread-hostile in this regard. The docs do say only to
> shutdown when you know the JVM won't open the database again. The
> problem with this is that you never know that, so you can never shut
> it down--so the files never get unlocked.
>
> I know I can just slap a synchronized block around these two methods
> to make it bulletproof, but there are two problems with this:
>    (1) synchronized is slow and Derby's shutdown is not fast at all...
>    (2) I don't know what other apps might be open in the same JVM at the same.
>
> So I wonder if anyone with experience in this sort of thing has any
> good tips on how to get around the problem.
>
> Also, we're on Derby 10.8, so if anything has been improved in this
> regard in the later releases, that would be good to know - it might
> just be enough impetus to switch.
>
> TX


Re: Thread-safe shutdown while other threads might try to reopen

Posted by Bryan Pendleton <bp...@gmail.com>.
> I know I can just slap a synchronized block around these two methods
> to make it bulletproof, but there are two problems with this:
>    (1) synchronized is slow and Derby's shutdown is not fast at all...
>    (2) I don't know what other apps might be open in the same JVM at the same.

I'm not sure you can avoid the performance cost, given the
needs of your application.

I'm always more comfortable getting the correct behavior first,
then dealing with the implications once it's reliably running.

I think you may have to do slightly more than just make two
methods synchronized; it isn't exactly clear to me which two
methods you mean. I might try:

  - make the Database class have two public methods "Open" and "Close".
  - in Open, call DriverManager.getConnection and increment your
    in-use count
  - in Close, close the connection, decrement your in-use count,
    and shutdown the database when it goes to zero
  - make Open and Close synchronized

Regarding other apps in the same JVM, is that truly a concern?

I'm not sure how your app is packaged, but it seems unlikely to me
that some other arbitrary body of Java code is going to try to
open one of your own databases. How would that code even know what
connection URL to use?

thanks,

bryan