パフォーマンス向上のため、PrepareStmtをtrueにした。
https://gorm.io/docs/performance.html#Caches-Prepared-Statement
これは標準パッケージの PrepareContext
の実行結果の sql.Stmt
をキャッシュしておいて、2回目以降の実行を速くする。
https://github.com/go-gorm/gorm/blob/206613868439c5ee7e62e116a46503eddf55a548/prepare_stmt.go#L68
これを有効にしたときにメモリリークが発生するようになったため、原因を調べた。
再現方法
以下のようなSQLを実行していた。
gormDb, err := gorm.Open("mydb", &gorm.Config{
PrepareStmt: true,
})
if err != nil {
return nil, err
}
// weightは関数の引数と想定。ほぼ被ることのないバラバラの値とする
gormDb.Order(fmt.Sprintf("(ranking * %s) asc", weight)).First(&result)
これの問題点は、 weight
の値ごとに異なるSQLが発行されることだった。
PrepareStmt: true のとき、GORMの実装では発行されたSQL文をキーにmap型の値にキャッシュされるため、 weight
の値がばらつくほどキャッシュされる量も増える。
これによってメモリリークが発生していた。
解決方法
解決方法は、ちゃんとprepared statementにすることだ。
gormDb.Clauses(clause.OrderBy{
Expression: clause.Expr{
SQL: "(ranking * ?) asc",
Vars: []any{
weight,
},
},
}).Limit(1).Find(&tpoint)
- 単純なOrderには指定ができなかったので、clauseを使って生SQLを書く
First
を指定するとORDER BY <primary key>
で上書きされてしまったため、Limit(1).Find
にした