-
Notifications
You must be signed in to change notification settings - Fork 808
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
pgx hangs on 10k+ rows batch #374
Comments
I was able to reproduce this issue. Send writes all queued queries before reading any results. The deadlock occurs when the batched queries to be sent are so large that the PostgreSQL server cannot receive it all at once. PostgreSQL received some of the queued queries and starts executing them. As PostgreSQL executes the queries it sends responses back. pgx will not read any of these responses until it has finished sending. Therefore, if all network buffers are full in both directions pgx will not be able to finish sending the queries and PostgreSQL will not be able to finish sending the responses. This is a non-trivial issue to resolve. The simplest solution would be if there was some guaranteed number of queries that would be safe and to limit batch to queuing that number. However, the number of safe queries varies based on multiple factors such as the type of query and type of connection. Another approach would be to start a goroutine before sending the batched queries that reads and buffers responses until the send has finished. But this would introduce quite a bit more complexity to an already complex system. Another approach would be to internally break large batches into multiple interleaved send and receives. But again, it adds a lot of error-prone complexity to an already complex system. For the time being, I have added documentation to |
This is resolved in v4 (currently prerelease). |
@jackc I have the same problem, SendBatch hangs on 1200 total items (100 per batch, contains a JSONB field). I am using v4.10.1. Should I use BeginTx and execute each insert command instead? |
@kataras Do you have a simple repro case? As far as I knew this issue was resolved a long time ago. |
Hello @jackc , unfurtunally (or fortunately for you :)) the project I am starting to use your library is a production-level one for a company and the repository is private. However, the insert command is very simple, I fetch the data, asynchronous, from another source (an external API) with a pagination of 100 per time, and after some conversation I am sending them to the postgresql database. Each Maybe it's a configuration option inside the postegresql database server itself? The Update: A moment ago, I replaced Batch and |
It sounds like you might be reusing the same
Well, that's a workaround, but obviously you lose a lot of performance there. |
Hello @jackc, happy new year! Ofc I did use a new I have another critical question, I want to remove database rows that are inside a slice of UUIDs. I tried to use []string but it can't convert from string to uuid (that's postgresql thing) so I am using var payload = struct {
IDs []pgtype.UUID `json:"ids"` // it completes the json marshaler interface.
}{} Example payload: {
"ids": ["dcb823b8-524c-4817-87bc-b73839640c37","b5a98047-121d-4003-8778-7bff42ab7313"]
} var args pgtype.UUIDArray
args.Set(ids) // ids is a type of: []pgtype.UUID
info, err := db.Exec(queryCtx, query, args) The query looks exactly as: It gives me
My temporarly solution: func (db *DB) DeleteByIDs(ctx context.Context, tableName string, ids []string) (int64, error) {
// Unfortunately we have to do the query look like that, which is not the safest method:
var b strings.Builder
lastIdx := len(ids) - 1
for i, id := range ids {
b.WriteString(xstrconv.SingleQuote(id))
if i < lastIdx {
b.WriteByte(',')
}
}
query := fmt.Sprintf("DELETE FROM %s WHERE id IN(%s)", tableName, b.String())
if db.Options.Trace {
log.Println(query, ids)
}
info, err := db.Exec(ctx, query)
return info.AffectedRows(), err
} Is there a workaround to delete one or more rows based on a list of uuids?
Thanks! |
The problem is that the e.g. See https://www.postgresql.org/docs/current/functions-comparisons.html#id-1.5.8.30.16. |
I'm also getting a similar problem, using batch with 10k updates. The program hangs and doesn't complete or return an error. I am using
If I change out |
I'm not sure what could be going there. SendBatch uses a goroutine to read and write at the same time to avoid the network buffer deadlock that was happening before. And this test works for me even when changed to 100K inserts. Lines 153 to 192 in 3ce50c0
By any chance are you using the simple protocol? |
I'm not sure about the protocol, couldn't find anything in the docs with that terminology, this is my connection setup: dbpool, err := pgxpool.Connect(context.Background(), connString)
if err != nil {
return err
}
defer dbpool.Close() I see in the test it's using 1,000 inserts, my issue only was reproducible with a 10,000 batch queue and worked with a 1,000 batch queue. Also maybe something with update vs insert? |
The extended protocol is used by default so if you haven't changed anything that is what is used.
I changed it locally to 100K and it still worked for me. Maybe try ctrl+\ when it is hung to get a stack trace where it is stuck. |
This is anecdotal but thought I should report it. In my set up, a batch of 200K statements (a mix of updates and inserts) reproducibly caused the Postgres server process to be terminated because of "excessive memory consumption". Reducing the batch size to 200 resolved the problem. |
Hello,
When I execute the same batch (same prepared statement that do INSERT) with exactly the same data with queue of around 500 elements then everything works fine and executes almost instantly but when I try with queue of 10100 elements then pgx just hangs never completing the Send(), no error is returned also. In postgres logs I can see: could not receive data from client: connection reset by peer.
The text was updated successfully, but these errors were encountered: