Re: Concurrent use of isValid()

Поиск
Список
Период
Сортировка
От Dave Cramer
Тема Re: Concurrent use of isValid()
Дата
Msg-id CADK3HHKBphOM9dmjJXyygyVZ8LgWmc88E7xOZfB+4H3Jtkf5QQ@mail.gmail.com
обсуждение исходный текст
Ответ на Concurrent use of isValid()  (Hannes Erven <hannes@erven.at>)
Список pgsql-jdbc



On Mon, Mar 10, 2014 at 8:10 PM, Hannes Erven <hannes@erven.at> wrote:
Hi folks,


in one of my applications, I recently decided to use Connection.isValid() to check and reestablish, if necessary, a connection to the DB. This application is a Eclipse RCP desktop app, built on top of Hibernate, and pools all its read-only workloads (data views that mostly refresh one single record at a time) through a single Connection object per JVM. These connections are again routed through a pgbouncer statement pool to reach the backends.

According to http://jdbc.postgresql.org/documentation/92/thread.html , pgjdbc is thread-safe, and hence a Connection object can be used concurrently between multiple threads.


Interesting piece of documentation. I wouldn't exactly call that thread safe other than the notion that it will block instead of running multiple threads through the connection.

 
In my code, I recently introduced an interesting bug as follows:

- whenever a new read-only Hibernate session needs to be created, check whether there already is a connection established. If there is, issue Connection.isValid(SHORT_TIMEOUT) and reestablish if necessary, else use the existing connection.

- sometimes, random queries (mostly ones that load larger sets of data) would throw an Exception like this:
Caused by: org.postgresql.util.PSQLException: FEHLER: storniere Anfrage wegen Benutzeraufforderung
(Query was canceled due to user's request)
org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2161)
org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1890)
org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255)
org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:559)
org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:417)
org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:302)
org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:208)


The reason for that is that one thread is already executing the data-loading query when another thread calls isValid() with a timeout that is shorter than the time it takes the already-processing-query to complete. Hence, isValid() must wait, cannot complete within its limit and fails, *canceling the original query*.

Interestingly, the Exception is logged on the server and also on the client in relation to the longer-running query. At least with my setup, the correlation to isValid() could not be made through the Exception traces, but required extensive digging through the code.


I now understand what Kris Jurka meant by "isValid is broken"  ( http://postgresql.1045698.n5.nabble.com/Connection-isValid-int-timeout-implementation-td5711754.html )... and this probably isn't by any means a fault in pgjdbc.


So, I'd ask you for suggestions on
- should the pgjdbc concurrency documentation be improved to suggest calling isValid() on an concurrently active connection might cancel the current command

certainly the documentation should be modified. I don't think we test it well for concurrency
 
- should isValid() be modified so it doesn't cancel previously running commands

- should isValid() be modified so it behaves more like
 st = createStatement();
 st.setStatementTimeout(X);
 st.execute("SELECT 1");
 (e.g. if the connection is in-use, wait for it to become free and only start the timeout when the SELECT 1 command starts?)


This last suggestion probably makes the most sense.  


- is there any sensible way to combine isValid() and concurrent queries, especially ones that might take longer that the isValid() timeout?


- is it reasonable to share a single per-JVM connection over all threads or is it better practice to let all the data views hit pgbouncer?
The data views bulk load the data to be displayed on initialization, but then just refresh one single record at a time.
(I'm expecting about 30 clients with 10 different data views each, so now I have 30 connections hitting pgbouncer, but I could also change it to 300 connections to pgbouncer.)



For the moment, my fix is to pass a timeout to isValid() that is way longer than any of the bulk data load queries should take, and I'm thinking about removing isValid() altogether.

The isValid() calls were introduced because at one point the connections to pgbouncer were cut due to an accidentally stopped service and then all app instances had to be restarted manually, instead of just automatically reconnecting.


Thanks for the detailed analysis

Dave 

Thanks for any suggestions and insights,
best regards

        -hannes


--
Sent via pgsql-jdbc mailing list (pgsql-jdbc@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-jdbc

В списке pgsql-jdbc по дате отправления:

Предыдущее
От: Dave Cramer
Дата:
Сообщение: Re: Missing tag REL9_3_1101 for latest jdbc release
Следующее
От: Daryl Foster
Дата:
Сообщение: Cannot insert to 'path' field using EclipseLink