Обсуждение: keeping Connection alive

Поиск
Список
Период
Сортировка

keeping Connection alive

От
Andreas Brandl
Дата:
Hi,

I'm using a PostgreSQL as backend for a Java application, which may idle for quite a while (i.e. hours) and now and
thenissue a query on the backend. 

The problem is the following exception I get when connection is apparently broken:

-- snip --
org.postgresql.util.PSQLException: An I/O error occured while sending to the backend.
-- snip --

What is best practice to prevent the connection from breaking, i.e. keeping the Conection alive?

I'm using an instance of PGPoolingDataSource and would expect to retrieve a _valid_ Connection when calling
DataSource.getConnection()(which I don't get). I even did call PGPoolingDataSource.setTcpKeepAlive(true) - same result. 

My first approach was to use a scheduled task and regularly check the Connection for validity ("ping") - unfortunately
Connection.isValid(inttimeout) has not been implemented yet. 

My current workaround is a scheduled task issueing a "SELECT 1" on a regularly basis. Which I feel is not the best
solution,though. 

I read of using a connection pool like dbcp, which I'd like to avoid: I don't need connection pooling at all, just need
tohave a valid Connection even after hours of no-operation (a reconnect is ok, so no performance issue here). 

So, what's the best practice using the postgresql jdbc driver?

I'm using PostgreSQL 8.3.8 and postgresql-8.4-701.jdbc4.jar for JDBC.

Thanks alot!

Best regards,
Andreas


Re: keeping Connection alive

От
"F. Jovan Jester"
Дата:
On Dec 12, 2009, at 6:43 PM, Andreas Brandl wrote:
> I'm using a PostgreSQL as backend for a Java application, which may idle for quite a while (i.e. hours) and now and
thenissue a query on the backend. 

Is this a standard Java application or is it running in a Java EE or servlet container?

> -- snip --
> org.postgresql.util.PSQLException: An I/O error occured while sending to the backend.
> -- snip --
>
> What is best practice to prevent the connection from breaking, i.e. keeping the Conection alive?
>
> I'm using an instance of PGPoolingDataSource and would expect to retrieve a _valid_ Connection when calling
DataSource.getConnection()(which I don't get). I even did call PGPoolingDataSource.setTcpKeepAlive(true) - same result. 

If you're not using an application server, and/or don't need pooling, perhaps it would be better to use the regular
jdbcapi something like this: 
Class.forName("org.postgresql.Driver");
Connection conn = DriverManager.getConnection(jdbcConnUrl, username, password);
// then conn.createStatement , etc.

Then you can call conn.close() and create a new connection when necessary.

> My first approach was to use a scheduled task and regularly check the Connection for validity ("ping") -
unfortunatelyConnection.isValid(int timeout) has not been implemented yet. 
> My current workaround is a scheduled task issueing a "SELECT 1" on a regularly basis. Which I feel is not the best
solution,though. 
>
> I read of using a connection pool like dbcp, which I'd like to avoid: I don't need connection pooling at all, just
needto have a valid Connection even after hours of no-operation (a reconnect is ok, so no performance issue here). 

I've only had issues with pooled connections when I've misconfigured a setting in the app server (it should manage the
connectionsand automatically close and reconnect every so often) or when I've had an application bug that was not
releasingconnections because of unintended long running transactions. 

Hope that helps.

-Jovan Jester


> I'm using PostgreSQL 8.3.8 and postgresql-8.4-701.jdbc4.jar for JDBC.
>
> Thanks alot!
>
> Best regards,
> Andreas
>


Re: keeping Connection alive

От
Andreas
Дата:
Hi Jovan,

thanks for your reply!

> On Dec 12, 2009, at 6:43 PM, Andreas Brandl wrote:
> > I'm using a PostgreSQL as backend for a Java application, which may
> idle for quite a while (i.e. hours) and now and then issue a query on
> the backend.
>
> Is this a standard Java application or is it running in a Java EE or
> servlet container?

This is a standard Java application.

> If you're not using an application server, and/or don't need pooling,
> perhaps it would be better to use the regular jdbc api something like
> this:
> Class.forName("org.postgresql.Driver");
> Connection conn = DriverManager.getConnection(jdbcConnUrl, username,
> password);
> // then conn.createStatement , etc.
>
> Then you can call conn.close() and create a new connection when
> necessary.

I did work with a pure Connection before, until I realized things go wrong when application is idle for long time.

In either case, the problem is how to detect whether a Connection is valid or not. As far as I know there is no native
mechanismfor checking the state. So one approach may be to implement a DataSource which does check (i.e. "SELECT 1")
validitybefore returning the Connection (and reconnect if its not valid, i.e. throwing a SQLException?). 

Is there any better way to achieve this? After all this might mean a lot of overhead (checking every time
DataSource.getConnection()is called)... 

Thanks,
Andreas

Re: keeping Connection alive

От
Craig Ringer
Дата:
On 13/12/2009 9:37 AM, Andreas wrote:
> Hi Jovan,
>
> thanks for your reply!
>
>> On Dec 12, 2009, at 6:43 PM, Andreas Brandl wrote:
>>> I'm using a PostgreSQL as backend for a Java application, which may
>> idle for quite a while (i.e. hours) and now and then issue a query on
>> the backend.
>>
>> Is this a standard Java application or is it running in a Java EE or
>> servlet container?
>
> This is a standard Java application.

Is its connection to the server diect via a local Ethernet segment? Or
is it going through routers - possibly routers doing NAT, such as most
home user modem/routers?

NAT is the most common cause of connection drop-outs. You can use TCP
keepalives to address this. See the archives and documentation.

(Hmm, we need a FAQ entry for this - added to my todo)

> I did work with a pure Connection before, until I realized things go wrong when application is idle for long time.
>
> In either case, the problem is how to detect whether a Connection is valid or not.

Don't. Even if the connection is ok when you test it, it might not be
when you then try to use it. Instead of trying to ensure that the
connection is perfect, make your application tolerant of failure. Batch
things into transactions, catch SQLExceptions, and re-try operations
that fail.

After all, database operations may fail due to serialization errors (in
SERIALIZABLE mode), locking deadlocks if you make mistakes with lock
ordering, etc. While you want to try to avoid these things, you should
also handle them in the app if they come up.

If you try to ensure that your connection is ok and then rely on it
staying OK, you *will* have problems. A user might lose wifi/cellular
coverage, a network might drop out, etc. You're setting yourself up for
a race condition you will inevitably lose some of the time.

> As far as I know there is no native mechanism for checking the state.

Trying something and catching the SQLException if it fails. That
reflects the design of the JDBC interface and of transactional databases
in general that you should try to do something and cope if it fails,
rather than ensuring it cannot fail.

 > So one approach may be to implement a DataSource which does check
(i.e. "SELECT 1") validity before returning the Connection (and
reconnect if its not valid, i.e. throwing a SQLException?).

Nononono....

Have the DataSource _consumer_ deal with it.


int retries = MAX_RETRIES;
do {
   try {
     try {
       Connection conn = myprovider.getConnection();
       // do work
       break;
     } finally {
       try {
         stmt.close();
       } catch (SQLException e) {
         // log to complain about statement close failure
       }
     }
   } catch (SQLException e) {
     myProvider.invalidateConnection(conn, e);
     retries--;
   }
} while (retries > 0);


... where "invalidateConnection(Connection, Throwable)" tells the
provider/pool that the connection is broken.

If you're using Connection directly, you'll just close() one you think
is broken and set the connection var to null so you know you need to
create a new one next time around.

In practice, you'll also generally test e.getSQLSTate() to see if the
exception might be a transient once tha doesn't imply the connection is
broken, and re-try with the same connection if it is. There's no point
dropping and re-creating the connection if it's a deadlock,
serialization failure, or the like after all.

Here's some code I use to check for SQLStates that may mean an operation
is worth retrying:

    private static final List<String> transientStates = Arrays.asList(
             "08",   // Connection exceptions - refused, broken, etc
             "53",   // Insufficient resources - disk full, etc
             "57P0", // Db server shutdown/restart
             "40001",// Serialization failure
             "40P01"// Deadlock detected
             );

    /**
      * Attempt to figure out whether a given database error could be
      * transient
      * and might be worth a retry. Detects things like network timeouts,
      * transaction deadlocks, serialization failures, connection drops,
      * etc.
      *
      * @param e Exception thrown by persistence provider
      * @return True if the error might be transient
      */
     public static boolean isDbErrorTransient(Throwable e) {
         final SQLException se = getSQLException(e);
         if (se == null)
             return false;
         final String sqlState = se.getSQLState();
         for (String s : transientStates) {
             Logger.getLogger(TheNameClass.class).warn(
                "Got possibly transient SQL error " + sqlState, e);
             if (sqlState.startsWith(s))
                 return true;
         }
         return false;
     }


--
Craig Ringer

Re: keeping Connection alive

От
Craig Ringer
Дата:
On 13/12/2009 10:52 AM, Craig Ringer wrote:

> int retries = MAX_RETRIES;
> do {
> try {
> try {
> Connection conn = myprovider.getConnection();
 > Statement stmt = conn.createStatement();
> // do work
> break;
> } finally {
> try {
> stmt.close();
> } catch (SQLException e) {
> // log to complain about statement close failure
> }
> }
> } catch (SQLException e) {
> myProvider.invalidateConnection(conn, e);
> retries--;
> }
> } while (retries > 0);

Oh, also: if that seems ghastly and rather undesirable to repeat all
over the place - good.

You should probably wrap your database work up in an implementation of
an interface that you can pass to a database work executor. Your
interface has a single method that does the actual work with the
connection, plus hooks called for success, failure, etc.

You pass a (probably anonymous inner class) instance of the interface to
a database worker that's responsible for the ugly error handling and
connection management, and let it take care of giving it a connection
and running it. The database worker can take care of the nasty bits,
leaving the rest of your code free to just handle real failures.

You don't have to bother to catch SQLExceptions thrown within your work
code, since it should all be running in a single transaction so it'll
get automatically rolled back when the exception is thrown. Instead, you
can let the exception bubble up for the database worker to handle. It'll
decide whether to retry, to give up and call your failure hook, etc.

As a bonus, if you specify that the on success and on failure methods
must be run on the EDT but require the database work code to be safe to
run on any thread, you can trivially move your database work off the EDT
and into a background worker thread.

--
Craig Ringer

Re: keeping Connection alive

От
Andreas
Дата:
Hi Craig,

thanks a lot for your detailed answer! I learned a lot and hopefully are going to do a better design next time. :)

> Is its connection to the server diect via a local Ethernet segment? Or
> is it going through routers - possibly routers doing NAT, such as most
> home user modem/routers?

Neither local nor NAT/home routers.

>  > So one approach may be to implement a DataSource which does check
> (i.e. "SELECT 1") validity before returning the Connection (and
> reconnect if its not valid, i.e. throwing a SQLException?).
>
> Nononono....
>
> Have the DataSource _consumer_ deal with it.
>
>
> int retries = MAX_RETRIES;
> do {
>    try {
>      try {
>        Connection conn = myprovider.getConnection();
>        // do work
>        break;
>      } finally {
>        try {
>          stmt.close();
>        } catch (SQLException e) {
>          // log to complain about statement close failure
>        }
>      }
>    } catch (SQLException e) {
>      myProvider.invalidateConnection(conn, e);
>      retries--;
>    }
> } while (retries > 0);
>
>
> ... where "invalidateConnection(Connection, Throwable)" tells the
> provider/pool that the connection is broken.

I'm not sure how to do this, because AFAIK a DataSource implementation does not have a method for invalidating the
Connection.How to do this when implementing against standard interfaces? 

Thanks again for your detailed answer!

Best regards,
Andreas

Re: keeping Connection alive

От
Craig Ringer
Дата:
On 14/12/2009 10:53 PM, Andreas wrote:

>> ... where "invalidateConnection(Connection, Throwable)" tells the
>> provider/pool that the connection is broken.
>
> I'm not sure how to do this, because AFAIK a DataSource implementation does not have a method for invalidating the
Connection.How to do this when implementing against standard interfaces? 

If you're using JNDI to get a DataSource and calling getConnection(...)
on it to obtain a JDBC connection, then there's no connection pooling
going on. You close your connectoins when you're done with them. So
there's no need to tell the DataSource the connection is broken; you
just close it and request a new one from the DataSource.

I don't currently use JNDI in my own app (though it looks like I
should), but I'm pretty sure that's how to handle it.

--
Craig Ringer

Re: keeping Connection alive

От
Craig Ringer
Дата:
On 14/12/2009 10:53 PM, Andreas wrote:

> I'm not sure how to do this, because AFAIK a DataSource implementation does not have a method for invalidating the
Connection.How to do this when implementing against standard interfaces? 

OK, after a little more reading it's clear that if you're using the
basic javax.sql.DataSource there's no pooling and you just close dead
connections and open new ones.

If you're using javax.sql.ConnectionPoolDataSource, you'll need to
inform the connection pool that the connection is dead. How to do so
depends on the connection pool you're using, so you'd need to provide
more information. In general, it seems to end up with the connection
pool calling close() on the PooledConnection associated with the problem
connection.

See:
   http://java.sun.com/javase/6/docs/api/javax/sql/PooledConnection.html

--
Craig Ringer

Re: keeping Connection alive

От
Andreas Brandl
Дата:
Hi Craig, folks,

I'm coming back to this topic, because I now do have an implementation which is more robust in respect to connection
failuresand the like and which follows Craig's idea. 

> > As far as I know there is no native mechanism for checking the
> state.
>
> Trying something and catching the SQLException if it fails. That
> reflects the design of the JDBC interface and of transactional
> databases
> in general that you should try to do something and cope if it fails,
> rather than ensuring it cannot fail.
>
>  > So one approach may be to implement a DataSource which does check
> (i.e. "SELECT 1") validity before returning the Connection (and
> reconnect if its not valid, i.e. throwing a SQLException?).
>
> Nononono....
>
> Have the DataSource _consumer_ deal with it.
>
> [...]
>
> ... where "invalidateConnection(Connection, Throwable)" tells the
> provider/pool that the connection is broken.
>
> [...]
>
> In practice, you'll also generally test e.getSQLSTate() to see if the
> exception might be a transient once tha doesn't imply the connection
> is
> broken, and re-try with the same connection if it is. There's no point
> dropping and re-creating the connection if it's a deadlock,
> serialization failure, or the like after all.
>
> Here's some code I use to check for SQLStates that may mean an
> operation
> is worth retrying:
>
>     private static final List<String> transientStates =
> Arrays.asList(
>              "08",   // Connection exceptions - refused, broken, etc
>              "53",   // Insufficient resources - disk full, etc
>              "57P0", // Db server shutdown/restart
>              "40001",// Serialization failure
>              "40P01"// Deadlock detected
>              );
>

In the course of implementing I discovered, that in JDBC 4 there are subtypes of SQLException designed for
distinguishingtransient and non-transient failures. These are SQLTransientException, SQLNonTransientException and
SQLNonTransientConnectionException.Quite an old source is [1]. 

My understanding is that in general distinguishing by the type of SQLException is sufficient for this purpose. Though I
wonderif the postgresql-jdbc does implement this? 

Another question arising with implementing a more robust connection handling is the following. Suppose there is a
'WorkUnit'containing the concrete jdbc-code which gets executed in a single transaction which may get rolled back and
retriedsome more times. 

The execution of the 'WorkUnit' generates some Statement- and ResultSet-instances which have to be closed after
execution. 

My question is if it is advisable to do this closing of resources on a background worker thread. This would have the
advantageof returning faster after executing the relevant JDBC code because resources are closed on the separate
backgroundthread. 

I.e. in pseudo code:

void execute(WorkUnit unit) {

  do {
     unit.execute();
  } while (failed and retry-count is not reached};

  backgroundThread.close(unit); // a background thread will close the units ressources

}

Thanks a lot, I really appreciate this (and the other pgsql-*) mailinglist(s)!

Andreas

[1] http://www.artima.com/lejava/articles/jdbc_four3.html

Re: keeping Connection alive

От
Craig Ringer
Дата:
On 21/12/2009 10:24 AM, Andreas Brandl wrote:

> In the course of implementing I discovered, that in JDBC 4 there are subtypes of SQLException designed for
distinguishingtransient and non-transient failures. These are SQLTransientException, SQLNonTransientException and
SQLNonTransientConnectionException.Quite an old source is [1]. 

Hmm, I'd never noticed the subtypes of SQLException.

> My understanding is that in general distinguishing by the type of SQLException is sufficient for this purpose. Though
Iwonder if the postgresql-jdbc does implement this? 

It does not appear to - at least, those exception names don't appear
anywhere in the sources. It only seems to throw PSQLException, which is
a subtype of SQLException.

The full heirarchy is here, by the way:

   http://java.sun.com/javase/5/docs/api/java/sql/SQLException.html


Unfortunately, the exception descriptions don't seem to be as clear as
they could be regarding what exactly "transient" means.

SQLRecoverableException describes something the app can recover from by
at minimum re-connecting and re-trying. That's clear enough.

SQLTransactionRollbackException would be a good one to be able to use
for deadlock-rollback. It's similarly clear.

SQLTransientException is for when "a previoulsy failed operation might
be able to succeed when the operation is retried without any
intervention by application-level functionality." This isn't very clear.
Does that mean "retrying *this* *statement* may work, you don't have to
re-try the whole transaction" ? That's how I read it. If it refers to
just the one JDBC operation as it seems to, it doesn't really apply to Pg.

In that case, is there even a suitable class for the most important case
in Pg - "if you re-try the whole transaction this will probably succeed,
but re-trying this particular statement is pointless. You don't have to
reconnect." ?



Anyway: To implement the use of SQLTransientException etc might be
interesting given that the Pg driver supports JDBC2, JDBC3 _and_ JDBC4
drivers in one source package. It'd need an exception factory that was
part of the JDBC-version-specific code, which took an exception type
param as well as the usual msg, sqlstate and cause. The factory for
JDBC2 / JDBC3 would return a normal PSQLException, and the JDBC4 factory
would override it to return a PSQLException subclass derived from one of
the JDBC SQLException subtypes (for JDBC4).

The JDBC4 factory wouldn't even get built when building the JDBC2/3
drivers, so the exceptions being missing from older JDKs wouldn't matter.



> Another question arising with implementing a more robust connection handling is the following. Suppose there is a
'WorkUnit'containing the concrete jdbc-code which gets executed in a single transaction which may get rolled back and
retriedsome more times. 
>
> The execution of the 'WorkUnit' generates some Statement- and ResultSet-instances which have to be closed after
execution.
>
> My question is if it is advisable to do this closing of resources on a background worker thread. This would have the
advantageof returning faster after executing the relevant JDBC code because resources are closed on the separate
backgroundthread. 

Closing the statement and associated result set shouldn't take long. I'd
be reluctant to add the additional complexity, myself. Also, while the
PgJDBC driver _is_ thread safe, this still seems like it's asking for
threading issues. I'm no JDBC expert, though.

--
Craig Ringer

Re: keeping Connection alive

От
Craig Ringer
Дата:

Re: keeping Connection alive

От
John R Pierce
Дата:
Craig Ringer wrote:
> On 21/12/2009 11:05 AM, Craig Ringer wrote:
>
>> http://java.sun.com/javase/5/docs/api/java/sql/SQLException.html
>
> Er, sorry:
>
>   http://java.sun.com/javase/5/docs/api/java/sql/SQLException.html

same broken link, but this
http://java.sun.com/j2se/1.5.0/docs/api/java/sql/SQLException.html

seems to work for me.