You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

并发场景下FetchUserForAuthV1报错,FetchUserForAuth运行正常求助

并发场景下FetchUserForAuthV1过早超时故障的排查与优化建议

问题描述

我有两个功能相似的函数用于并发测试,其中FetchUserForAuthV1在请求量更低时就开始报超时错误,而FetchUserForAuth表现稳定。FetchUserForAuthV1通过结构体传递查询语句,并调用其FetchRows方法从数据库取数,目的是复用通用函数减少重复代码。

相关错误信息

context deadline exceeded

错误发生在common_db_operations.go的这段代码中:

tx, err := dbConnection.BeginTx(ctx, pgx.TxOptions{AccessMode: pgx.ReadWrite})
if err != nil {
    logger.Logger.Error("MODELS :: Error while begin transaction", zap.Error(err), zap.String("requestId", uuidString))
    return id, err
}

代码片段

FetchUserForAuth 函数

func FetchUserForAuth(email string) UserSchema {
    logger.Logger.Info("MODELS :: Will fetch user details for auth", zap.String("email", email))

    var userData UserSchema
    dbConnection := DbPool()
    ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
    defer cancel()
    // ctx := context.Background()

    tx, err := dbConnection.BeginTx(ctx, pgx.TxOptions{AccessMode: pgx.ReadOnly})
    if err != nil {
        logger.Logger.Error("MODELS :: Error while begin transaction", zap.Error(err))
        return userData
    }
    defer func() {
        if err != nil {
            tx.Rollback(ctx)
        } else {
            tx.Commit(ctx)
        }
    }()

    query := fmt.Sprintf(`SELECT
                            u.id,
                            u.first_name, 
                            u.last_name, 
                            u.email, 
                            u.password
                            FROM users u
                            WHERE u.email='%s' LIMIT 1`, email)
    logger.Logger.Info("MODELS :: Query", zap.String("query", query))
    err = tx.QueryRow(ctx, query).Scan(
        &userData.Id,
        &userData.FirstName,
        &userData.LastName,
        &userData.Email,
        &userData.Password,
    )
    if err != nil {
        if err == pgx.ErrNoRows {
            logger.Logger.Info("MODELS :: Query - No rows found. ", zap.String("query", query))
            return userData
        }
        logger.Logger.Error("MODELS :: Error while executing query.",
            zap.Error(err),
        )
        return userData
    }
    return userData
}

FetchUserForAuthV1 函数

func FetchUserForAuthV1(email string) UserSchema {
    logger.Logger.Info("MODELS :: Will fetch user details for auth v1", zap.String("email", email))

    var userData UserSchema
    query := fmt.Sprintf(`SELECT
                            u.id,
                            u.first_name, 
                            u.last_name, 
                            u.email, 
                            u.password
                            FROM users u
                            WHERE u.email='%s' LIMIT 1`, email)
    logger.Logger.Info("MODELS :: Query", zap.String("query", query))

    queryToExecute := QueryStructToExecute{Query: query}
    rows, _, err := queryToExecute.FetchRows("")
    logger.Logger.Info("user models : ", zap.Any("rows ", rows), zap.Error(err))
    if err != nil {
        return userData
    }

    if len(rows) == 0 {
        return userData
    }

    singleRow := rows[0]

    jsonbody, err := json.Marshal(singleRow)
    if err != nil {
        // do error check
        fmt.Println(err)
    }

    if err := json.Unmarshal(jsonbody, &userData); err != nil {
        // do error check
        fmt.Println(err)
    }

    logger.Logger.Info("MODELS :: before sending dat a", zap.Any("data", userData), zap.Any("id", userData.Id))
    return userData
}

排查思路与优化建议

  • 修正事务模式:FetchUserForAuth使用ReadOnly事务模式,而FetchRows用了ReadWrite模式。只读事务锁竞争更小,并发场景下不会抢占写锁资源,建议把FetchRows中的事务模式改为ReadOnly,降低数据库压力。
  • 统一上下文超时:FetchUserForAuth明确设置了60秒超时,检查FetchRows是否设置了合理的上下文超时,避免因默认短超时导致并发时快速触发超时,最好允许调用方传入自定义上下文。
  • 优化连接池使用:确认FetchRows是否正确复用数据库连接池,若每次调用未正确释放连接,并发时会耗尽连接池导致请求超时。确保和FetchUserForAuth一致,使用DbPool()获取连接并正确关闭事务/连接。
  • 消除冗余数据转换:FetchUserForAuthV1多了JSON序列化和反序列化步骤,并发下会增加CPU开销,间接导致请求变慢。建议在FetchRows中直接将结果映射到目标结构体,避免不必要的转换。
  • 替换SQL拼接为参数化查询:两个函数都用fmt.Sprintf拼接SQL,存在注入风险且无法复用数据库查询计划。改用参数化查询(如tx.QueryRow(ctx, "SELECT ... WHERE email=$1", email)),提升并发执行效率。
  • 移除不必要的事务:单条SELECT语句无需使用事务,直接用db.QueryRow代替事务操作,减少事务带来的额外开销和锁竞争。

内容的提问来源于stack exchange,提问作者Deepen Patel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 11:57:18