Skip to content

Using with GORM

By dropping down to common database/sql constructs, River can share connections and transactions with GORM, a well known ORM (Object Relational Mapper) in the Go ecosystem.


Sharing a database handle

The same *sql.DB handle can be configured on GORM and a River client:

import (
"database/sql"
"github.com/jackc/pgx/v5/pgxpool"
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/riverqueue/river"
"github.com/riverqueue/river/riverdriver/riverdatabasesql"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
sqlDB, err := sql.Open("pgx", "postgres://localhost/river")
if err != nil {
return nil, err
}
// Providing a separate listener pool is optional, but gives the client
// access to listen/notify and more responsive to incoming jobs.
listenerPool, err := pgxpool.New(ctx, "postgres://localhost/river")
if err != nil {
return nil, err
}
defer listenerPool.Close()
gormDB, err := gorm.Open(postgres.New(postgres.Config{
Conn: sqlDB,
}), &gorm.Config{})
if err != nil {
return nil, err
}
riverClient, err := river.NewClient(
riverdatabasesql.NewWithPgxListener(sqlDB, listenerPool),
&river.Config{
Workers: workers,
},
)
if err != nil {
return nil, err
}

Pgx provides LISTEN/NOTIFY

The database/sql package doesn't expose Postgres LISTEN, so NewWithPgxListener uses listenerPool to receive notifications while all queries and transactions continue through sqlDB.

Use riverdatabasesql.New instead to intentionally run in poll-only mode.

Sharing a transaction

Transactions are shareable by starting them from GORM, then unwrapping their underlying *sql.Tx with a type assertion and using it with a River client's InsertTx:

tx := gormDB.Begin()
if err := tx.Error; err != nil {
return nil, err
}
// If in a transaction, ConnPool can be type asserted as an *sql.Tx so
// operations from GORM and River occur on the same transaction.
sqlTx := tx.Statement.ConnPool.(*sql.Tx)
_, err = riverClient.InsertTx(ctx, sqlTx, SortArgs{
Strings: []string{
"whale", "tiger", "bear",
},
}, nil)
if err != nil {
return nil, err
}
if err := tx.Commit().Error; err != nil {
return nil, err
}