Skip to content

Database drivers

River makes use of drivers to insulate itself from third party packages, enabling use of different databases, database packages, and new major versions. River currently includes riverpgxv5 for Postgres, riversqlite for SQLite, and riverdatabasesql for Postgres through Go's built-in database/sql. For Postgres, riverpgxv5 is the recommended option.


Drivers wrap third party packages

The River Client takes a generic TTx type parameter representing the type of the transaction in use for functions like InsertTx and InsertManyTx. TTx is derived from the client's driver, an agnostic interface to a third party package that provides access to the target database.

Most of the time, the only time code references a database driver is when it's initializing a River client. NewClient takes a driver as its first parameter, and the driver wraps a database pool:

import "github.com/riverqueue/river"
import "github.com/riverqueue/river/riverdriver/riverpgxv5"
...
dbPool, err := pgxpool.New(ctx, os.Getenv("DATABASE_URL"))
if err != nil {
panic(err)
}
defer dbPool.Close()
riverClient, err := river.NewClient(riverpgxv5.New(dbPool), &river.Config{
...
})
if err != nil {
panic(err)
}

See the InsertAndWork example for complete code.

Using riverdatabasesql

The riverdatabasesql driver adapts a standard *sql.DB for use with River. This lets River share a database pool and transactions with other packages built around database/sql, including Bun and GORM. Clients using this driver use *sql.Tx as their transaction type.

Create a client by passing its *sql.DB to riverdatabasesql.New:

riverClient, err := river.NewClient(
riverdatabasesql.New(sqlDB),
&river.Config{
Workers: workers,
},
)
if err != nil {
panic(err)
}

This constructor uses only the supplied database/sql pool. It's appropriate for clients that only insert jobs, migration tools, or installations where LISTEN isn't available. Clients that work jobs should generally also configure a Pgx listener.

In general, riverpgxv5 should be considered River's main supported driver. Pgx is performant, feature complete, production hardened, and well maintained, while Go's database/sql is broadly considered misdesigned, and too generic to provide access to important Postgres features.

Using LISTEN/NOTIFY

Go's database/sql package doesn't expose a way to implement Postgres' LISTEN feature. A client created with riverdatabasesql.New therefore runs in "poll only mode", polling periodically for newly available jobs.

Clients that work jobs can receive notifications by supplying a separate Pgx pool to riverdatabasesql.NewWithPgxListener:

listenerPool, err := pgxpool.New(ctx, os.Getenv("DATABASE_URL"))
if err != nil {
panic(err)
}
defer listenerPool.Close()
riverClient, err := river.NewClient(
riverdatabasesql.NewWithPgxListener(sqlDB, listenerPool),
&river.Config{
Workers: workers,
},
)
if err != nil {
panic(err)
}

The database/sql pool continues to execute all database operations and transactions. The Pgx pool is used only to acquire dedicated connections for LISTEN, and both pools must connect to the same database and resolve the same schema. The caller owns and must close both pools.

River Pro provides the equivalent riverprodatabasesql.NewWithPgxListener constructor.