otelsqlの導入方法 Go sql DBにOpenTelemetryのSpanを追加する
PostgreSQLのDBに sqlx を使って接続している。
import (
"context"
"fmt"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
)
func NewDB(host, user, password, db, port, sslmode string) (*sqlx.DB, error) {
dsn := fmt.Sprintf(
"host=%s user=%s password=%s dbname=%s port=%v sslmode=%s",
host, user, password, db, port, sslmode,
)
db, err := sqlx.Connect("postgres", dsn)
if err != nil {
return nil, fmt.Errorf("failed connect db: %w", err)
}
return db, nil
}
func main() {
db, err := NewDB("","","","","","")
// ....
jason = Person{}
err = db.Get(&jason, "SELECT * FROM person WHERE first_name=?", "Jason")
// ....
}
このコードに XSAM/otelsql を追加してトレースを取得しようとしたところ、 internal server error: pq: syntax error at or near \")\"
といったエラーが発生してSQLの実行に失敗するようになった。
import (
"github.com/XSAM/otelsql"
)
otelDriverName, err := otelsql.Register("postgres")
if err != nil {
return nil, fmt.Errorf("failed to register otelsql driver: %w", err)
}
db, err := sqlx.Connect(otelDriverName, dsn)
if err != nil {
return nil, fmt.Errorf("failed connect db: %w", err)
}
原因
PostgreSQLのplaceholderには ?
は使用できず、$1
, $2
を使用する。
本来は SELECT * FROM person WHERE first_name=?
はエラーになるのだが、sqlxが内部で変換してくれている。
https://github.com/jmoiron/sqlx/blob/41dac167fdad5e3fd81d66cafba0951dc6823a30/bind.go#L60
実際sqlxを外して標準の database/sql
を使うとエラーになる。
これを知らなかったので、otelsqlを追加したのが悪いのか、だれかが変換してくれていたのか、原因を探すのに手間取った。
解決策
デフォルトで、postgres
driverは DOLLAR
を使うように指定されている。
https://github.com/jmoiron/sqlx/blob/41dac167fdad5e3fd81d66cafba0951dc6823a30/bind.go#L24-L29
var defaultBinds = map[int][]string{
DOLLAR: {"postgres", "pgx", "pq-timeouts", "cloudsqlpostgres", "ql", "nrpostgres", "cockroach"},
QUESTION: {"mysql", "sqlite3", "nrmysql", "nrsqlite3"},
NAMED: {"oci8", "ora", "goracle", "godror"},
AT: {"sqlserver", "azuresql"},
}
これに合わせて、sqlx.BindDriver を使ってDriver名を教えてあげればよい。
otelDriverName, err := otelsql.Register("postgres")
if err != nil {
return nil, fmt.Errorf("failed to register otelsql driver: %w", err)
}
+ sqlx.BindDriver(otelDriverName, sqlx.DOLLAR)
db, err := sqlx.Connect(otelDriverName, dsn)
if err != nil {
return nil, fmt.Errorf("failed connect db: %w", err)
}
これでSQL文は変えずに今まで通り実行できるようになった。