Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

Silent self-deadlock with SetMaxOpenConns(1) when insert hooks use the pool

Đang mở
#1,411 1 bình luận 0 reaction 0 người được giao Xem trên GitHub

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
Loại issue
Lỗi
Độ rõ ràng
Khá rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
go, sqlite
Lĩnh vực
backend, database

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:

  1. 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 use InsertTx.
  2. 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.Exec directly on the raw pool, but it would cover River-level re-entry.
  3. 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

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. 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.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của riverqueue/river

Tất cả issue của riverqueue/river

Issue tương tự

Thêm issue về Go

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.