Skip to content

Batch job insertion

River supports batch inserts, wherein many jobs are inserted at once for fewer round trips and optimal performance.


Insert many

Batch inserts are executed with Client.InsertMany and Client.InsertManyTx. Both take a slice of InsertManyParams structs, which like a call to a normal non-batch insert function, take job args and optional InsertOpts.

results, err := riverClient.InsertMany(ctx, []river.InsertManyParams{
    {Args: BatchInsertArgs{}},
    {Args: BatchInsertArgs{}},
    {Args: BatchInsertArgs{}},
    {Args: BatchInsertArgs{}, InsertOpts: &river.InsertOpts{Priority: 3}},
    {Args: BatchInsertArgs{}, InsertOpts: &river.InsertOpts{Priority: 4}},
})
if err != nil {
    panic(err)
}
fmt.Printf("Inserted %d jobs\n", len(results))

See the BatchInsert example for complete code.

InsertManyTx takes a transaction, and like InsertTx, all the normal transactional enqueuing benefits apply, like that jobs aren't worked until the transaction commits, and are removed if it rolls back.

results, err := riverClient.InsertManyTx(ctx, tx, []river.InsertManyParams{
    ...
}

An even faster variant

Normal job insertions are quite fast so it's usually not necessary to resort to batch job insertion, but it may be desireable in situations where multiple jobs are being inserted at once because batch insertion requires fewer round trips.

A third option exists for cases where thousands of jobs are being inserted at once: InsertManyFast / InsertManyFastTx. Under the hood these methods use Postgres COPY FROM (only in the riverpgxv5 driver), has a few further performance benefits like reduced logging overhead. This method has some limitations, however, such as its inability to return inserted data or to detect and handle unique conflicts cleanly without rolling back the entire transaction.