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

Asp.Net(IIS)访问Firebird高峰偶发ArgumentOutOfRangeException异常

问题背景

部署在IIS 10.0.14393.0、基于.NET Framework 4.6.1开发的业务系统运行时出现间歇故障,具体特征如下:

  • 高峰时段随机抛出System.ArgumentOutOfRangeException,异常提示为operation=随机值、Parameter name: operation,参数值无规律,可能为0、159,甚至1380273440这类超出合理范围的极大值
  • 回收IIS对应的数据库连接池后异常会暂时消失,低访问量时段无该故障
  • 异常触发位置不固定,多个表单页面均会出现,开发环境始终无法复现
  • 系统当前依赖组件:Firebird 2.1数据库、FirebirdSql.Data.FirebirdClient 6.6.0驱动,开发环境为Visual Studio 2022 17.1.3版本

故障触发频率最高的为页面权限校验逻辑,相关代码如下:

protected void Page_Load(object sender, EventArgs e)
{
    if ((Session["NomeUsuario"] == null) || (Session["NomeSistema"].ToString() != Master.Sistema()))
    {
        Response.Redirect("login.aspx");
    }

    tag = Request.QueryString["tag"];

    if (!Page.IsPostBack)
    {
        ControleAcesso controleAcesso = new ControleAcesso();
        string formulario = System.IO.Path.GetFileName(System.Web.HttpContext.Current.Request.Url.AbsolutePath);
        if (controleAcesso.ValidaRotina(Session["NomeSistema"].ToString(), Session["NomeUsuario"].ToString(), formulario.ToLower()) == false) { Response.Redirect("Menu.aspx?acesso=1"); }
        else
        {
            if (controleAcesso.ValidaOpcao(Session["NomeSistema"].ToString(), Session["NomeUsuario"].ToString(), "incluir") == true) { butIncluir.Visible = true; } else { butIncluir.Visible = false; }
            if (controleAcesso.ValidaOpcao(Session["NomeSistema"].ToString(), Session["NomeUsuario"].ToString(), "alterar") == true) { griPadrao.Columns[0].Visible = true; } else { griPadrao.Columns[0].Visible = false; }
            if (controleAcesso.ValidaOpcao(Session["NomeSistema"].ToString(), Session["NomeUsuario"].ToString(), "filhos") == true) { griPadrao.Columns[1].Visible = true; } else { griPadrao.Columns[1].Visible = false; }
            if (controleAcesso.ValidaOpcao(Session["NomeSistema"].ToString(), Session["NomeUsuario"].ToString(), "excluir") == true) { griPadrao.Columns[2].Visible = true; } else { griPadrao.Columns[2].Visible = false; }
            if (controleAcesso.ValidaOpcao(Session["NomeSistema"].ToString(), Session["NomeUsuario"].ToString(), "perfil") == true) { griPadrao.Columns[3].Visible = true; } else { griPadrao.Columns[3].Visible = false; }
            if (controleAcesso.ValidaOpcao(Session["NomeSistema"].ToString(), Session["NomeUsuario"].ToString(), "historico-log") == true) { griPadrao.Columns[4].Visible = true; } else { griPadrao.Columns[4].Visible = false; }
            if (controleAcesso.ValidaOpcao(Session["NomeSistema"].ToString(), Session["NomeUsuario"].ToString(), "listagem") == true) { tabListagem.Enabled = true; } else { tabListagem.Enabled = false; }
        }

        hypRaca.ImageUrl = "~/Imagens/pastafechada.png";
        hypPai.ImageUrl = "~/Imagens/pastafechada.png";
        hypMae.ImageUrl = "~/Imagens/pastafechada.png";
        hypPaiPermanente.ImageUrl = "~/Imagens/dna_nao.png";
        hypMaePermanente.ImageUrl = "~/Imagens/dna_nao.png";
        txtMae.Attributes.Add("onfocus", "this.select()");
        txtPai.Attributes.Add("onfocus", "this.select()");
        tabManutencao.Enabled = false;
        tabPerfilGeral.Enabled = false;
        tabLogHistorico.Enabled = false;
        tabFilhos.Enabled = false;
        tabListagem.Enabled = false;
        txtCadastro.Text = DateTime.Today.ToShortDateString();
        tabCadastro.ActiveTabIndex = 0;
        tabPerfilLinhagemCelular.Enabled = false;
    }
}

public bool ValidaRotina(string sistema, string usuario, string formulario)
{
    using (FbConnection conexao = new FbConnection(System.Configuration.ConfigurationManager.ConnectionStrings["Dados"].ToString()))
        try
        {
            FbCommand comando = new FbCommand("select count(1) from SYS_PERMISSAO where SISTEMA=@SISTEMA and USUARIO=@USUARIO and FORMULARIO=@FORMULARIO and ((OPCAO is null) or (OPCAO=''))", conexao);
            comando.Parameters.Add("@SISTEMA", FbDbType.VarChar);
            comando.Parameters.Add("@USUARIO", FbDbType.VarChar);
            comando.Parameters.Add("@FORMULARIO", FbDbType.VarChar);
            comando.Parameters[0].Value = sistema;
            comando.Parameters[1].Value = usuario;
            comando.Parameters[2].Value = formulario.ToLower();
            conexao.Open();
            return !(((int)comando.ExecuteScalar()) > 0);
        }
        catch (Exception erro)
        {
            return false;
        }
        finally
        {
            conexao.Close();
        }
}

public bool ValidaOpcao(string sistema, string usuario, string opcao)
{
    using (FbConnection conexao = new FbConnection(System.Configuration.ConfigurationManager.ConnectionStrings["Dados"].ToString()))
        try
        {
            string formulario = System.IO.Path.GetFileName(System.Web.HttpContext.Current.Request.Url.AbsolutePath);

            FbCommand comando = new FbCommand("select count(1) from SYS_PERMISSAO where SISTEMA=@SISTEMA and USUARIO=@USUARIO and FORMULARIO=@FORMULARIO and OPCAO=@OPCAO", conexao);
            comando.Parameters.Add("@SISTEMA", FbDbType.VarChar);
            comando.Parameters.Add("@USUARIO", FbDbType.VarChar);
            comando.Parameters.Add("@FORMULARIO", FbDbType.VarChar);
            comando.Parameters.Add("@OPCAO", FbDbType.VarChar);
            comando.Parameters[0].Value = sistema;
            comando.Parameters[1].Value = usuario;
            comando.Parameters[2].Value = formulario.ToLower();
            comando.Parameters[3].Value = opcao.ToLower();
            conexao.Open();
            return !(((int)comando.ExecuteScalar()) > 0);
        }
        catch (Exception erro)
        {
            return false;                
        }
        finally
        {
            conexao.Close();
        }
}
根因分析
  1. 核心诱因为FirebirdSql.Data.FirebirdClient 6.6.0版本存在连接池线程安全缺陷:该版本驱动内部维护连接池异步操作状态时,未对operation枚举字段的读写加线程锁,高并发场景下多线程同时取用、归还连接时会发生内存值踩踏,导致字段值被写脏,出现随机值甚至超出枚举范围的极大值,最终抛出参数越界异常。
  2. 现有代码逻辑放大了故障触发概率:单次页面首次加载会执行至少7次独立数据库查询(1次路由校验+6次操作权限校验),短时间内频繁从连接池申请、归还连接,高并发下极易触发线程竞争;同时FbCommand对象未纳入资源释放逻辑,存在数据库对象泄漏问题,会进一步加剧连接池状态混乱。
  3. 现象匹配逻辑:回收连接池会重置所有池内连接的状态,清空已被写脏的内存值,因此异常会暂时消失,待并发量上升后内存再次被写脏故障复现;开发环境无高并发压力,无法触发多线程同时读写同一块内存的场景,因此始终无法复现。
  4. 现有异常处理逻辑干扰排查:catch块直接吞掉所有异常返回false,会将真实的数据库驱动异常伪装成权限不足,触发页面跳转回菜单,进一步增加问题定位难度。
修复方案

彻底修复

  1. 升级FirebirdSql.Data.FirebirdClient驱动至7.10.1及以上稳定版本,7.x分支已完全修复连接池线程安全缺陷;若升级后存在与Firebird 2.1的兼容性问题,可选择6.6.0之后发布的6.x补丁版本,优先选择标记修复了连接池并发问题的构建版本。
  2. 修正数据库操作代码的资源释放逻辑:所有FbCommand、FbDataReader等实现了IDisposable接口的数据库对象,全部放入using块中自动释放,移除多余的手动Close()调用,避免资源泄漏。修正后的权限校验方法示例:
    public bool ValidaRotina(string sistema, string usuario, string formulario)
    {
        using (FbConnection conexao = new FbConnection(System.Configuration.ConfigurationManager.ConnectionStrings["Dados"].ToString()))
        using (FbCommand comando = new FbCommand("select count(1) from SYS_PERMISSAO where SISTEMA=@SISTEMA and USUARIO=@USUARIO and FORMULARIO=@FORMULARIO and ((OPCAO is null) or (OPCAO=''))", conexao))
        {
            try
            {
                comando.Parameters.Add("@SISTEMA", FbDbType.VarChar).Value = sistema;
                comando.Parameters.Add("@USUARIO", FbDbType.VarChar).Value = usuario;
                comando.Parameters.Add("@FORMULARIO", FbDbType.VarChar).Value = formulario.ToLower();
                conexao.Open();
                return !(((int)comando.ExecuteScalar()) > 0);
            }
            catch (Exception erro)
            {
                // 新增错误日志记录,禁止直接吞异常
                LogHelper.WriteError(erro, $"权限校验失败,系统:{sistema},用户:{usuario},表单:{formulario}");
                return false;
            }
        }
    }
    
  3. 优化权限校验逻辑:将当前用户的所有权限一次性查询出来,缓存到当前请求上下文或Session中,单次页面加载仅执行1次数据库查询,大幅降低连接池并发压力。

临时规避(无法立即升级驱动时使用)

在数据库连接字符串中添加参数Pooling=false关闭驱动自带的连接池,每次请求直接新建物理连接、用完直接销毁,绕开连接池的线程安全缺陷。该方案会降低数据库访问性能,仅可作为短期过渡方案,待驱动升级完成后恢复连接池配置。


内容的提问来源于stack exchange,提问作者Osvaldo G. Da Cruz Júnior

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 17:51:24