Skip to content

Inserting jobs from 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 (
    "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
}

gormDB, err := gorm.Open(postgres.New(postgres.Config{
    Conn: sqlDB,
}), &gorm.Config{})
if err != nil {
    return nil, err
}

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

Database/sql does not support listen

The database/sql package doesn't support Postgres LISTEN, so if a client using riverdatabasesql is started for work, it does so in "poll only mode", meaning that jobs are fetched by polling periodically rather than being notified through a Postgres listen/notify channel.

For maximum throughput performance, use of riverdatabasesql should be restricted to compatibility with packages like GORM, and that a separate client with a Pgx pool and using the more standard riverpgxv5 driver is used for working jobs.

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
}