Reputation: 11
I am 4 days can't resolve my issue and sadly, but chatGPT also weak on this.
In short:
I have Go, Gin-gonic v1.9.0 API which using Gorm ORM v1.24.5 and Postgresql server v14 hosted on the Digitalocean. I also enabled the PgBouncer feature with pool_mode = transaction
for a specific database.
Question: I can't fully understand how should I correctly use Gorm ORM with transaction pool mode to handle each API request and after sql query executed be able to return connection to the pgbouncer pool. I checked that ORM using jackc/pgx
under hood but that library have pgxpool
and ability to Acquire & Release connection, and I didn't find any information how Gorm cover it.
Why title about prepared statements? Before I set the options: PrepareStmt: false
and PreferSimpleProtocol: true
, mostly received errors: prepared statement_* already exists
.
Based on docs from PgBouncer: http://www.pgbouncer.org/faq.html#how-to-use-prepared-statements-with-transaction-pooling, it's not possible to use prepared statements with transaction mode so I disable it. When I deployed my API (only one endpoint) to tests with a team, we watch how at least every fourth sql query failed with ERROR: prepared statement_6230 doesn't exists
. When I executed:
SELECT * FROM pg_prepared_statements;
I see a list of prepared statements which a living ~ 30 min, guess it's until connection alive. Using DEALLOCATE ALL
only drops current statements.
My endpoint and db connection where hidden not relevant code.
func main() {
...
client := setupPostgresql()
...
r.GET("/deploy/:id", func(c *gin.Context) {
id := c.Param("id")
var deploy Deploy // small model
timeoutCtx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second)
defer cancel()
if err := client.WithContext(timeoutCtx).Where("id = ?", id).First(&deploy).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": "Deploy with id: " + id + " not found"})
return
} else {
log.Panic(err) // it will be covered by gin recover
}
}
c.AbortWithStatusJSON(http.StatusOK, deploy)
})
...
}
func setupPostgresql() *gorm.DB {
dsn := "host=" + AppConfig.DBHost +
" user=" + AppConfig.DBUser +
" password=" + AppConfig.DBPass +
" dbname=" + AppConfig.DBName +
" port=" + AppConfig.Port +
" sslmode=" + AppConfig.SSL
client, err := gorm.Open(postgres.New(postgres.Config{
DSN: dsn,
PreferSimpleProtocol: true,
}), &gorm.Config{
SkipDefaultTransaction: false,
DisableAutomaticPing: true,
PrepareStmt: false,
NowFunc: func() time.Time {
return time.Now().UTC()
},
Logger: logger.Default.LogMode(logger.Silent),
})
...
underlyingDB, _ := client.DB()
underlyingDB.SetMaxIdleConns(11)
underlyingDB.SetMaxOpenConns(11)
underlyingDB.SetConnMaxIdleTime(15 * time.Minute)
underlyingDB.SetConnMaxLifetime(30 * time.Minute)
return client
}
Infrastructure example I expected to correctly configure Gorm ORM to use PgBouncer in transaction mode.
Upvotes: 0
Views: 1181
Reputation: 1
db, err := gorm.Open(postgres.New(postgres.Config{
DSN: dsn,
**PreferSimpleProtocol: true,**
}), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
TablePrefix: "wa.",
SingularTable: false,
},
})
Upvotes: -1