Silent self-deadlock with SetMaxOpenConns(1) when insert hooks use the pool
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 52/100
Hướng nghiên cứu
Start by running the provided Go reproduction with MaxOpenConns(1), then compare the client.Insert and InsertTx paths with hook and middleware execution. Review the SetMaxOpenConns guidance and relevant hook documentation before choosing a scoped direction. Done should include coverage for the deadlock scenario and a clear user-facing explanation or failure mode.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Note: this issue was written by Claude (an AI assistant) from an investigation it ran, and @bgentry reviewed it and okayed filing it. @brandur, could you take a look?
The riversqlite package docs recommend dbPool.SetMaxOpenConns(1) to avoid SQLITE_BUSY errors from River's parallel internal operations. With that setting, two fairly natural patterns hang silently until the context deadline, or forever if the context has none. database/sql has no way to detect the self-deadlock, so the user sees no error, only a stuck insert.
1. A hook or middleware that uses the pool during a non-transactional insert. client.Insert opens its own transaction, which takes the pool's only connection, and runs insert hooks and middleware inside it. Hooks and middleware don't receive that transaction, so a hook that needs the database has to go through the pool. It then waits for the connection its own insert is holding.
2. client.Insert (instead of InsertTx) while the app holds an open transaction on the same pool. River's BEGIN waits for the connection that the app's transaction holds.
Reproduction against master (9573203e) with riversqlite and modernc.org/sqlite, a file database in WAL mode:
type poolHook struct {
river.HookDefaults
db *sql.DB
}
func (h *poolHook) InsertBegin(ctx context.Context, params *rivertype.JobInsertParams) error {
_, err := h.db.ExecContext(ctx, "SELECT 1")
return err
}
// db.SetMaxOpenConns(n); client configured with Hooks: []rivertype.Hook{&poolHook{db: db}}
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
_, err := client.Insert(ctx, someArgs{}, nil)
MaxOpenConns(1) hook uses pool during client.Insert: err=context deadline exceeded after 2.001s
MaxOpenConns(4) hook uses pool during client.Insert: err=<nil> after 1ms
MaxOpenConns(1) client.Insert while app holds a tx: err=error beginning transaction: context deadline exceeded after 2s
This isn't strictly SQLite-specific. Any database/sql or pgx pool capped at one connection behaves the same way. It mostly bites SQLite users in practice, because that's where the docs recommend a single connection.
Some possible directions, not mutually exclusive:
- Document it. Next to the
SetMaxOpenConns(1)advice, and in the hooks/middleware docs, explain that hook and middleware code runs while River holds a connection, and that app code inside its own transaction should useInsertTx. - Fail fast on re-entry. River could mark the context while it holds its own insert transaction, so a re-entrant River call on the same client returns a clear error instead of hanging. This wouldn't catch a hook calling
db.Execdirectly on the raw pool, but it would cover River-level re-entry. - Let hooks and middleware use River's transaction. Expose the insert's transaction to them, for example through the context, so they can do database work without a second connection. This is a bigger API change.
My suggestion is (1) plus (2), with (3) as a possible follow-up.
- Ngôn ngữ chính
- Go
- Star
- 5.7k
- Fork
- 187
- Merge trung bình
- 22 giờ 36 phút
- Pull request đã merge (30 ngày)
- 89
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của riverqueue/river
-
Notifier spins without backoff when the Start context ends by deadlineCó thể đã có người làm @brandur đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
riverqueue/river#1493 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
riverqueue/river#1454 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Remote JobCancel() can be silently lost while the notifier is reconnecting (no durable-poll fallback)Có thể đã có người làm Có pull request liên kết đang mở hoặc đã được merge. Đang mở
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 45/100
riverqueue/river#1358 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
River job stuck at runningĐang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
riverqueue/river#1258 · 7 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 45/100
riverqueue/river#1225 · 14 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của riverqueue/river
Issue tương tự
-
bug frontend good first issue
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 86/100
Maintainer thường phản hồi trong vòng 1 ngày
-
trust: update-propagation-directive requires developer mode while add and remove do notCó thể đã có người làm @bhuvan-somisetty đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 2 ngày
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 82/100
oalders/clodhopper#133 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
peasant-labs/peasant#596 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
hatchet-dev/hatchet#5179 ·
Maintainer thường phản hồi trong vòng 1 ngày