我 4 天都无法解决我的问题,遗憾的是,chatGPT 在这方面也很弱。
简而言之: 我有 Go、Gin-gonic v1.9.0 API,它使用 Gorm ORM v1.24.5 和托管在 Digitalocean 上的 Postgresql 服务器 v14。我还使用
pool_mode = transaction
为特定数据库启用了 PgBouncer 功能。
问题:我无法完全理解如何正确使用带有事务池模式的 Gorm ORM 来处理每个 API 请求,并在执行 sql 查询后能够返回到 pgbouncer 池的连接。我在引擎盖下使用
jackc/pgx
检查了 ORM,但该库具有 pgxpool
以及 获取和释放 连接的能力,并且我没有找到 Gorm 如何覆盖它的任何信息。
为什么要标题准备好的陈述?在我设置选项:
PrepareStmt: false
和PreferSimpleProtocol: true
之前,大多数收到的错误:prepared statement_* already exists
。
基于 PgBouncer 的文档:http://www.pgbouncer.org/faq.html#how-to-use-prepared-statements-with-transaction-pooling,不可能在事务模式下使用准备好的语句,所以我禁用它。当我部署 API(仅一个端点)与团队进行测试时,我们观察到至少每四个 sql 查询都会失败并显示 ERROR: prepared statement_6230 doesn't exists
。当我执行时:
SELECT * FROM pg_prepared_statements;
我看到一个准备好的语句列表,大约需要 30 分钟,我猜要等到连接有效为止。使用
DEALLOCATE ALL
仅删除当前语句。
我的端点和数据库连接隐藏了不相关的代码。
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
}
db, err := gorm.Open(postgres.New(postgres.Config{ DSN:DSN, 首选简单协议:true, }), &gorm.Config{ 命名策略: schema.NamingStrategy{ 表前缀:“wa.”, 奇异表:假, }, })