From 3e6c69c73f6e50cc1f3e7121b07628a0e2926e82 Mon Sep 17 00:00:00 2001 From: 2748109647 <196059414+2748109647@users.noreply.github.com> Date: Mon, 5 Oct 2026 06:17:02 +0800 Subject: [PATCH] docs: explain single-connection insert deadlocks --- client.go | 4 ++++ riverdriver/riversqlite/river_sqlite_driver.go | 7 +++++++ rivertype/river_type.go | 8 ++++++++ 3 files changed, 19 insertions(+) diff --git a/client.go b/client.go index 72c798361..3a539d775 100644 --- a/client.go +++ b/client.go @@ -1890,6 +1890,10 @@ var errNoDriverDBPool = errors.New("driver must have non-nil database pool to us // provided context is used for the underlying Postgres insert and can be used // to cancel the operation or apply a timeout. // +// If the application already holds a transaction on a pool limited to one +// connection, use InsertTx with that transaction instead; Insert opens its own +// transaction and waits for a pool connection. +// // jobRow, err := client.Insert(insertCtx, MyArgs{}, nil) // if err != nil { // // handle error diff --git a/riverdriver/riversqlite/river_sqlite_driver.go b/riverdriver/riversqlite/river_sqlite_driver.go index 73ae1430c..24693cc9b 100644 --- a/riverdriver/riversqlite/river_sqlite_driver.go +++ b/riverdriver/riversqlite/river_sqlite_driver.go @@ -11,6 +11,13 @@ // errors is to set the maximum pool size to one connection like // `dbPool.SetMaxOpenConns(1)`. // +// With a single-connection pool, insert hooks and middleware must not query +// the same pool while an insert is running: the insert holds its connection +// until they return, so their query can wait indefinitely for that connection. +// Likewise, application code that already holds a transaction should use +// `Client.InsertTx` or `Client.InsertManyTx` with that transaction instead of +// `Client.Insert` or `Client.InsertMany`. +// // A known deficiency in this driver compared to Postgres is that due to // limitations in sqlc, it performs operations like completion and `InsertMany` // one row at a time instead of in batches. This means that it's slower than the diff --git a/rivertype/river_type.go b/rivertype/river_type.go index fe74d7076..32f7d9ad4 100644 --- a/rivertype/river_type.go +++ b/rivertype/river_type.go @@ -341,6 +341,10 @@ type Hook interface { } // HookInsertBegin is an interface to a hook that runs before job insertion. +// The hook runs synchronously as part of the insert operation and does not +// receive the insert transaction. With a pool limited to one connection, a +// hook that queries the same pool can block waiting for the connection held by +// the insert until its context is canceled. type HookInsertBegin interface { Hook @@ -548,6 +552,10 @@ type Plugin interface { // JobInsertMiddleware provides an interface for middleware that integrations // can use to encapsulate common logic around job insertion. +// Middleware runs synchronously around the insert operation. With a +// single-connection pool, querying the same pool from middleware can block +// until the insert releases its connection, which cannot happen until the +// middleware returns. // // Implementations should embed river.JobMiddlewareDefaults to inherit default // implementations for phases where no custom code is needed, and for forward