Thread: Re: [BUGS] BUG #1347: Bulk Import stopps after a while ( 8.0.0.

Re: [BUGS] BUG #1347: Bulk Import stopps after a while ( 8.0.0.

From
Kris Jurka
Date:

On Mon, 13 Dec 2004, PostgreSQL Bugs List wrote:

>
> The following bug has been logged online:
>
> Bug reference:      1347
> PostgreSQL version: 8.0 Beta
> Operating system:   Windows XP
> Description:        Bulk Import  stopps after a while ( 8.0.0. RC1)
>
>  - I have written a java program to transfer  data from SQL Server 2000 to
> PosgresSQL 8.0.0 RC1 release. I am updating the data in batches.
> If my batch size is 1000/2000 records at a time.. This works fine.. And if I
> change this size to say 20,000, it does only finishes one loop.. and then
> stays idle. The CPU usage down to 10 % which was before 100 % while applying
> the first batch of 20, 000 records.
>
>
> The execution of program is halting just at
> int n [] = stmt.batchUpdate();
>

This may be a problem with the JDBC driver deadlocking as described in the
below code comment.  When originally written I asked Oliver about the
estimate of MAX_BUFFERED_QUERIES and he felt confident in that number.  It
would be good to know if lowering this number fixes your problem.

Kris Jurka

    // Deadlock avoidance:
    //
    // It's possible for the send and receive streams to get
    // "deadlocked" against each other since we do not have a separate
    // thread. The scenario is this: we have two streams:
    //
    //   driver -> TCP buffering -> server
    //   server -> TCP buffering -> driver
    //
    // The server behaviour is roughly:
    //  while true:
    //   read message
    //   execute message
    //   write results
    //
    // If the server -> driver stream has a full buffer, the write will
    // block.  If the driver is still writing when this happens, and the
    // driver -> server stream also fills up, we deadlock: the driver is
    // blocked on write() waiting for the server to read some more data,
    // and the server is blocked on write() waiting for the driver to read
    // some more data.
    //
    // To avoid this, we guess at how many queries we can send before the
    // server -> driver stream's buffer is full (MAX_BUFFERED_QUERIES).
    // This is the point where the server blocks on write and stops
    // reading data. If we reach this point, we force a Sync message and
    // read pending data from the server until ReadyForQuery,
    // then go back to writing more queries unless we saw an error.
    //
    // This is not 100% reliable -- it's only done in the batch-query case
    // and only at a reasonably high level (per query, not per message),
    // and it's only an estimate -- so it might break. To do it correctly
    // in all cases would seem to require a separate send or receive
    // thread as we can only do the Sync-and-read-results operation at
    // particular points, and also as we don't really know how much data
    // the server is sending.

    // Assume 64k server->client buffering and 250 bytes response per
    // query (conservative).
    private static final int MAX_BUFFERED_QUERIES = (64000 / 250);

Re: [BUGS] BUG #1347: Bulk Import stopps after a while (

From
Csaba Nagy
Date:
Hi all,

I just shoot in the dark, but using the non blocking IO facilities of
jdk1.4+ wouldn't solve this problem ? Of course this would be no more
compatible with older jdks... and not that I would have time to
contribute :-(

Cheers,
Csaba.


On Mon, 2004-12-13 at 08:46, Kris Jurka wrote:
> On Mon, 13 Dec 2004, PostgreSQL Bugs List wrote:
>
> >
> > The following bug has been logged online:
> >
> > Bug reference:      1347
> > PostgreSQL version: 8.0 Beta
> > Operating system:   Windows XP
> > Description:        Bulk Import  stopps after a while ( 8.0.0. RC1)
> >
> >  - I have written a java program to transfer  data from SQL Server 2000 to
> > PosgresSQL 8.0.0 RC1 release. I am updating the data in batches.
> > If my batch size is 1000/2000 records at a time.. This works fine.. And if I
> > change this size to say 20,000, it does only finishes one loop.. and then
> > stays idle. The CPU usage down to 10 % which was before 100 % while applying
> > the first batch of 20, 000 records.
> >
> >
> > The execution of program is halting just at
> > int n [] = stmt.batchUpdate();
> >
>
> This may be a problem with the JDBC driver deadlocking as described in the
> below code comment.  When originally written I asked Oliver about the
> estimate of MAX_BUFFERED_QUERIES and he felt confident in that number.  It
> would be good to know if lowering this number fixes your problem.
>
> Kris Jurka
>
>     // Deadlock avoidance:
>     //
>     // It's possible for the send and receive streams to get
>     // "deadlocked" against each other since we do not have a separate
>     // thread. The scenario is this: we have two streams:
>     //
>     //   driver -> TCP buffering -> server
>     //   server -> TCP buffering -> driver
>     //
>     // The server behaviour is roughly:
>     //  while true:
>     //   read message
>     //   execute message
>     //   write results
>     //
>     // If the server -> driver stream has a full buffer, the write will
>     // block.  If the driver is still writing when this happens, and the
>     // driver -> server stream also fills up, we deadlock: the driver is
>     // blocked on write() waiting for the server to read some more data,
>     // and the server is blocked on write() waiting for the driver to read
>     // some more data.
>     //
>     // To avoid this, we guess at how many queries we can send before the
>     // server -> driver stream's buffer is full (MAX_BUFFERED_QUERIES).
>     // This is the point where the server blocks on write and stops
>     // reading data. If we reach this point, we force a Sync message and
>     // read pending data from the server until ReadyForQuery,
>     // then go back to writing more queries unless we saw an error.
>     //
>     // This is not 100% reliable -- it's only done in the batch-query case
>     // and only at a reasonably high level (per query, not per message),
>     // and it's only an estimate -- so it might break. To do it correctly
>     // in all cases would seem to require a separate send or receive
>     // thread as we can only do the Sync-and-read-results operation at
>     // particular points, and also as we don't really know how much data
>     // the server is sending.
>
>     // Assume 64k server->client buffering and 250 bytes response per
>     // query (conservative).
>     private static final int MAX_BUFFERED_QUERIES = (64000 / 250);
>
> ---------------------------(end of broadcast)---------------------------
> TIP 8: explain analyze is your friend


Re: [BUGS] BUG #1347: Bulk Import stopps after a while (

From
Oliver Jowett
Date:
Csaba Nagy wrote:

> I just shoot in the dark, but using the non blocking IO facilities of
> jdk1.4+ wouldn't solve this problem ? Of course this would be no more
> compatible with older jdks...

Well, that's a big problem right there..

-O

Re: [BUGS] BUG #1347: Bulk Import stopps after a while ( 8.0.0.

From
Tom Lane
Date:
Kris Jurka <books@ejurka.com> writes:
>     // To avoid this, we guess at how many queries we can send before the
>     // server -> driver stream's buffer is full (MAX_BUFFERED_QUERIES).

It seems essentially impossible for the driver to do this reliably,
since it has no clue how much data any one query will return.

How about instead thinking in terms of not filling the driver->server
channel?  You have a good handle on how much data you have sent (or at
least you could keep track of that), and if you bound it to 64K or so
then you should be safe.  Perhaps the limit ought to be easily
configurable just in case, but at least you'd be measuring something
measurable.

            regards, tom lane

Re: [BUGS] BUG #1347: Bulk Import stopps after a while ( 8.0.0.

From
Oliver Jowett
Date:
Tom Lane wrote:
> Kris Jurka <books@ejurka.com> writes:
>
>>    // To avoid this, we guess at how many queries we can send before the
>>    // server -> driver stream's buffer is full (MAX_BUFFERED_QUERIES).
>
>
> It seems essentially impossible for the driver to do this reliably,
> since it has no clue how much data any one query will return.

Right, but I'm not convinced that this is the problem here as batch
execution in JDBC is only allowed to do non-result-set-returning
queries. The only case I can think of where this would break is if
something is causing lots of logging output to the client (triggers etc.).

> How about instead thinking in terms of not filling the driver->server
> channel?  You have a good handle on how much data you have sent (or at
> least you could keep track of that), and if you bound it to 64K or so
> then you should be safe.  Perhaps the limit ought to be easily
> configurable just in case, but at least you'd be measuring something
> measurable.

That's possibly a better idea but it does mean that we wouldn't be able
to batch inserts that contain lots of data. That's the use case I needed
to support when I wrote this in the first place..

Also, it's never going to be 100% without a separate thread, as the
server can spontaneously generate output (e.g. because of NOTIFY)
regardless of how careful we are with our queries.

There's actually another problem with this code: the subdivision into
smaller batches is not transparent if autocommit is on. We send a Sync
at the end of the batch which will cause an implicit commit. We should
be sending a Flush, but it's harder for the driver to handle this as a
Flush does not provoke a response message from the server, so we would
have to track the protocol state more closely. Given that JDBC is silent
about the autocommit semantics of batch execution anyway, I'm not too
worried about fixing this urgently.

I'd like to see that this is really the problem before tweaking this
code. Given that the OP said that batch sizes of 1000-2000 worked OK,
I'm not sure that this code is the problem since the maximum number of
queries we'll send per batch is around 250 by default.

-O

Re: [BUGS] BUG #1347: Bulk Import stopps after a while (

From
Oliver Jowett
Date:
Kris Jurka wrote:
>
> On Mon, 13 Dec 2004, PostgreSQL Bugs List wrote:
>
>
>>The following bug has been logged online:
>>
>>Bug reference:      1347
>>PostgreSQL version: 8.0 Beta
>>Operating system:   Windows XP
>>Description:        Bulk Import  stopps after a while ( 8.0.0. RC1)
>>
>> - I have written a java program to transfer  data from SQL Server 2000 to
>>PosgresSQL 8.0.0 RC1 release. I am updating the data in batches.
>>If my batch size is 1000/2000 records at a time.. This works fine.. And if I
>>change this size to say 20,000, it does only finishes one loop.. and then
>>stays idle. The CPU usage down to 10 % which was before 100 % while applying
>>the first batch of 20, 000 records.
>>
>>
>>The execution of program is halting just at
>>int n [] = stmt.batchUpdate();
>>
>
>
> This may be a problem with the JDBC driver deadlocking as described in the
> below code comment.  When originally written I asked Oliver about the
> estimate of MAX_BUFFERED_QUERIES and he felt confident in that number.  It
> would be good to know if lowering this number fixes your problem. [...]

I'm not convinced that this is the problem as MAX_BUFFERED_QUERIES is
around 250 by default, and yet batches of 1000/2000 execute OK.

Note that I haven't seen much benefit to batch sizes above perhaps 50 on
a LAN anyway. Batch sizes of 20,000 are almost certainly excessive (but
the driver shouldn't fail!)

Are you executing the batch with autocommit on or off?

Anyway, there are two bits of debug output that would be useful to see:

1. A thread dump of the importing JVM when it gets stuck. On unix-like
systems you'd do this by sending the JVM a SIGQUIT. I don't know how
you'd do the same under Windows.

2. Driver debug output:

   a. add "?logLevel=2" to the driver URL
   b. set a log writer on DriverManager at the start of the run:

     // Point this somewhere else, e.g. to a file,
     // if it's more convenient.
     java.sql.DriverManager.setLogWriter(
        new java.io.PrintWriter(System.err)))

This should trace all the protocol messages being sent or received to
System.err. If the driver is wedging at the protocol level, it should
show up in that output. Send me this output off-list and compressed --
it'll be pretty big for 20,000+ queries!

-O

Re: [BUGS] BUG #1347: Bulk Import stopps after a while (

From
Markus Schaber
Date:
Hi, Oliver,

On Tue, 14 Dec 2004 09:58:16 +1300
Oliver Jowett <oliver@opencloud.com> wrote:

> Also, it's never going to be 100% without a separate thread, as the
> server can spontaneously generate output (e.g. because of NOTIFY)
> regardless of how careful we are with our queries.

Sadly, we cannot abandon compatibility with older JDK releases, this
would allow us to use the new asynchroneous I/O interfaces.

Async I/O is a great tool when in the right hands, you might google for
Felix von Leitner and his "fnord" and "gatling" servers that even beat
the kernel based khttpd.

Thanks,
Markus

--
markus schaber | dipl. informatiker
logi-track ag | rennweg 14-16 | ch 8001 zürich
phone +41-43-888 62 52 | fax +41-43-888 62 53
mailto:schabios@logi-track.com | www.logi-track.com

Re: [BUGS] BUG #1347: Bulk Import stopps after a while (

From
Oliver Jowett
Date:
Markus Schaber wrote:
> Hi, Oliver,
>
> On Tue, 14 Dec 2004 09:58:16 +1300
> Oliver Jowett <oliver@opencloud.com> wrote:
>
>
>>Also, it's never going to be 100% without a separate thread, as the
>>server can spontaneously generate output (e.g. because of NOTIFY)
>>regardless of how careful we are with our queries.

I think my analysis here was wrong. It doesn't matter if the server
unexpectedly fills up its output pipe so long as the driver doesn't do
the same.

> Sadly, we cannot abandon compatibility with older JDK releases, this
> would allow us to use the new asynchroneous I/O interfaces.

Hm, is async I/O standardized yet? I thought JSR203 ("more NIO") covered
this, but it's still in the early stages according to jcp.org.

> Async I/O is a great tool when in the right hands, you might google for
> Felix von Leitner and his "fnord" and "gatling" servers that even beat
> the kernel based khttpd.

Current NIO isn't async I/O, but it's still fairly good (use at least
1.4.2_06, though, or you can have GC problems when collecting direct
buffers). We have written several resource adaptors that use it and they
perform well. The main win is that you don't need N threads for N
clients. That can't really be applied to the JDBC driver easily as the
API means that the client controls most of the threading, but the
ability to get at non-blocking primitives would still be useful.

I wonder if we can have some threads-or-NIO abstraction .. the main
thing we need is the ability to call out to some other code to do reads
either when we discover our writes will block (NIO) or when we have data
ready to read (threads). We then read and process messages until we
would block (NIO) or we *do* block (threads).

I doubt I'll have time to look at this in the near future though.

-O