🤖

sqlc と pgxpool でトランザクション

2023/02/25に公開

モチベーション

sqlc でトランザクションを利用するのは ドキュメント に書いてあるが、 pgxpool を利用したトランザクションのサンプルを整理しておきたい。

まとめ

sqlcpgx の pgxpool でトランザクションを利用するのは pgxpool.Begin を利用するだけ。

サンプルコード

  • サンプルコードのコンパイルは通らない
  • サンプルコード自体に意味は特にない

sqlc.yaml

version: "2"
sql:
- queries: "db/query/"
  schema: "db/schema/"
  engine: "postgresql"
  gen:
    go:
      package: "sqlc"
      out: "./gen/sqlc"
      sql_package: "pgx/v4"
      emit_json_tags: true
      emit_prepared_queries: false
      emit_interface: true
      emit_exact_table_names: false
      emit_empty_slices: true
      emit_pointers_for_null_types: true

プールを作成する

// pgxpool でコネクションプールを生成する
// connStr は postgres://jack:secret@pg.example.com:5432/mydb?sslmode=verify-ca&pool_max_conns=10 みたいなやつ
func NewPool(connStr string) (*pgxpool.Pool, error) {
    // https://pkg.go.dev/github.com/jackc/pgx/v4/pgxpool#ParseConfig
    config, err := pgxpool.ParseConfig(connStr)
    if err != nil {
      return nil, err
    }

    // https://pkg.go.dev/github.com/jackc/pgx/v4/pgxpool#ConnectConfig
    pool, err := pgxpool.ConnectConfig(context.Background(), config)
    if err != nil {
      return nil, err
    }

    // https://pkg.go.dev/github.com/jackc/pgx/v4/pgxpool#Pool.Ping
    if err := pool.Ping(context.Background()); err != nil {
      return nil, err
    }

    return pool, nil
}

生成したプールを使って sqlc で生成したコードを呼び出す query を作る。
また、トランザクションを利用するように pool も取っておく。

import (
    // sqlc generate で生成したコード置き場
    db "example.com/gen/sqlc"
)

// 適当にグローバルに置いてるが別にグローバルでなくてもいい
const (
    // プール自体もトランザクション用に取っておく
    pool = pool,
    // sqlc と pgxpool を関連付ける
    query = db.New(pool)
)

トランザクションを利用する

func txQuerySample(c context.Context, id string) error {
    // pgxpool の Begin を利用する
    // https://pkg.go.dev/github.com/jackc/pgx/v4/pgxpool#Pool.Begin
    tx, err := pool.Begin(c)
    if err != nil {
        return err
    }
    // https://pkg.go.dev/github.com/jackc/pgx/v4/pgxpool#Tx.Rollback
    defer tx.Rollback(c)
    txQuery := query.WithTx(tx)

    spam, err := txQuery.CreateSpam(c, id)
    if err != nil {
        return err
    }

    if err := txQuery.UpdateHam(c, spam.Pk); err != nil {
        return err
    }

    if err := txQuery.DeleteSpam(c, spam.Pk); err != nil {
        return err
    }

    // https://pkg.go.dev/github.com/jackc/pgx/v4/pgxpool#Tx.Commit
    if err := tx.Commit(c); err != nil {
        return err
    }
    return nil
}

トランザクションを利用しない

func querySample(c context.Context, id string) error {
    spam, err := query.CreateSpam(c, id)
    if err != nil {
        return err
    }

    if err := query.UpdateHam(c, spam.Pk); err != nil {
        return err
    }

    if err := query.DeleteSpam(c, spam.Pk); err != nil {
        return err
    }

    return nil
}

Discussion