使用正确的凭据获取SQL Server超时错误,立即拒绝(故意)不正确的凭据

时间:2010-10-27 21:37:52

标签: c# .net sql-server linq-to-sql timeout

这令人困惑。工作正常的我的ASP.NET 3.5应用程序突然开始出现超时错误...

System.Data.SqlClient.SqlException:超时已过期。操作完成之前经过的超时时间或服务器没有响应

...但仅适用于连接字符串中具有正确用户名/密码的请求。如果我们故意弄乱配置中的连接字符串,SQL Server会正确拒绝请求,但是不会在15秒等待(配置超时)之后立即拒绝,清楚地表明它与服务器通信没有问题。

我们退回了SQL Server框(SQL Server 2005,完全修补)和IIS框,触摸了web.config以强制重启应用程序等,但没有运气。所有请求都会挂起15秒,然后报告该错误。在任何时候我们都能够直接访问数据库服务器(管理工作室,监控工具),并且我能够在Visual Studio中配置我的本地站点副本,以便在没有事故的情况下命中同一个数据库服务器。这个突然出现的问题持续了大半天(错误开始在早上6:17登录)突然在下午4:30左右解决了。

似乎在这个Web服务器和这个数据库服务器之间存在网络路由问题,但只有一组特定的SQL凭据。我知道这没有任何意义,但我们可以想象没有任何其他情况。我是一位非常有经验的开发人员,无论是我和我们经验丰富的DBA还是系统管理员都无法在事件日志,监控违规行为等方面找到任何可以为这种奇异且自我解决的症状提供合理解释的方法。

这发生在我们客户的测试环境中,这并不理想,但是因为我们不明白到底发生了什么,我们担心的是这可能会突然出现在生产中,我们会在墙上击败我们的集体头脑所以任何想法或狂野的理论都非常受欢迎。

正在爆炸的LINQ-to-SQL生成的代码行是ExecuteMethodCall:

[Function(Name="dbo.spSetModelingNodeState")]
public int spSetModelingNodeState([Parameter(Name="NodeIdentifier", DbType="VarChar(60)")] string nodeIdentifier, [Parameter(Name="NodeStatus", DbType="Int")] System.Nullable<int> nodeStatus, [Parameter(Name="PoolWeighting", DbType="Float")] System.Nullable<double> poolWeighting)
{
    IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), nodeIdentifier, nodeStatus, poolWeighting);
    return ((int)(result.ReturnValue));
}

我调用该代码的代码是(缩写):

using (MyDataContext myDataContext = new MyDataContext(_connectionString))
{
    myDataContext.spSetModelingNodeState(
        Adapter.Identifier, // string
        (int)newHealthValue, // enum, cast to int
        PoolWeighting); // float
}

编辑:花了几天时间追踪,但我在下面添加了日志/个人资料信息,每个评论员请求以及有问题的存储过程正文

SQL Server日志只显示登录和注销,没有任何问题迹象。 Profiler曲线也没有吸烟枪,但以下似乎显示声明需要18秒。关注spid 82.它从4:37:50开始,做了一些事情,然后在4:38:07点了83条记录登录。 Spid 82接下来会以4:38:07的开始时间记录它的完成情况,但由于它们是按顺序记录的,所以它必须实际发生在4:38:07(之前的跟踪语句)和4:40:10之间(以下)语句)。

Spid   Starttime       Event               Query
82     4:37:50 PM      Login               -- network protocol: LPC  set quoted_identifier on  set arithabort off  set numeric_roundabort off  set ansi_warnings on  set ansi_padding on  set ansi_nulls on  set concat_null_yields_null on  set cursor_close_on_commit off  set implicit_transactions off
82     4:37:50 PM      SP:StmtStarting     EXEC @RETURN_VALUE = [dbo].[spSetModelingNodeState] @NodeIdentifier = @p0, @NodeStatus = @p1, @PoolWeighting = @p2
82     4:37:50 PM      SP:Starting         EXEC @RETURN_VALUE = [dbo].[spSetModelingNodeState] @NodeIdentifier = @p0, @NodeStatus = @p1, @PoolWeighting = @p2
82     4:37:50 PM      SP:StmtStarting     UPDATE    ModelingNodeState   SET    NodeStatus = @NodeStatus,    PoolWeighting = ISNULL(@PoolWeighting, PoolWeighting)   WHERE    NodeIdentifier = @NodeIdentifier     
83     4:38:07 PM      Login               -- network protocol: LPC  set quoted_identifier on  set arithabort off  set numeric_roundabort off  set ansi_warnings on  set ansi_padding on  set ansi_nulls on  set concat_null_yields_null on  set cursor_close_on_commit off  set implicit_transactions off
82     4:37:50 PM      SP:Completed        EXEC @RETURN_VALUE = [dbo].[spSetModelingNodeState] @NodeIdentifier = @p0, @NodeStatus = @p1, @PoolWeighting = @p2
80     4:40:10 PM      SP:StmtStarting     SELECT 'Server[@Name=' + quotename(CAST(serverproperty(N'Servername') AS sysname),'''') + ']' + '/Database[@Name=' + quotename(db_name(),'''') + ']' + '/Table[@Name=' + quotename(tbl.name,'''') + ' and @Schema=' + quotename(SCHEMA_NAME(tbl.schema_id),'''') + ']' AS [Urn], tbl.name AS [Name], SCHEMA_NAME(tbl.schema_id) AS [Schema], CAST(   case       when tbl.is_ms_shipped = 1 then 1      when (          select               major_id           from               sys.extended_properties           where               major_id = tbl.object_id and               minor_id = 0 and               class = 1 and               name = N'microsoft_database_tools_support')           is not null then 1      else 0  end                         AS bit) AS [IsSystemObject], tbl.create_date AS [CreateDate], stbl.name AS [Owner] FROM sys.tables AS tbl INNER JOIN sys.database_principals AS stbl ON stbl.principal_id = ISNULL(tbl.principal_id, (OBJECTPROPERTY(tbl.object_id, 'OwnerId'))) WHERE (CAST(   case       when tbl.is_ms_shipped = 1 then 1      when (          select               major_id           from               sys.extended_properties           where               major_id = tbl.object_id and               minor_id = 0 and               class = 1 and               name = N'microsoft_database_tools_support')           is not null then 1      else 0  end                         AS bit)=@_msparam_0) ORDER BY [Schema] ASC,[Name] ASC
80     4:40:10 PM      SQL:BatchStarting   use [master]
62     4:20:10 PM      Logout              NULL
55     4:20:02 PM      Logout              NULL
74     4:13:37 PM      Logout              NULL
59     4:20:10 PM      Logout              NULL
55     4:40:29 PM      Login               -- network protocol: TCP/IP  set quoted_identifier on  set arithabort off  set numeric_roundabort off  set ansi_warnings on  set ansi_padding on  set ansi_nulls on  set concat_null_yields_null on  set cursor_close_on_commit off  set implicit_transactions 
55     4:40:29 PM      SQL:BatchStarting   set transaction isolation level  read committed  set implicit_transactions off 
82     4:37:50 PM      Logout              NULL

这绝对是一个比我习惯的更深层次的SQL调试,所以如果我误读了这一点,请告诉我,但看起来这个声明似乎没有发生任何事故,只是非常缓慢。再次,它事先运行正常,开始超时,然后恢复正常运行。

我现在唯一能想到的就是我在公共场合反复思考的是,如果有一些长时间运行的锁堵塞了sproc调用的表,那可能就解释了它。因为LINQ-to-SQL在某种程度上隐藏了登录/注销过程,所以登录可能总是正常工作,而这只是一个由于块而超时的sproc调用。这些表是否被锁定,如果是,为什么,在这一点上是不可能的。这听起来像是我看到的最可能的解释,还是有人有另一种理论?

为了完整性,这里是sproc的主体:

/****** Object:  StoredProcedure [dbo].[spSetModelingNodeState]    Script Date: 10/29/2010 14:37:46 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[spSetModelingNodeState]
(
@NodeIdentifier varchar(60),
@NodeStatus int,
@PoolWeighting float = NULL
)
AS
/*try to update existing row to new state*/
UPDATE
    ModelingNodeState
SET
    NodeStatus = @NodeStatus,
    PoolWeighting = ISNULL(@PoolWeighting, PoolWeighting)
WHERE
    NodeIdentifier = @NodeIdentifier

IF @@ROWCOUNT = 0
    /*not found, so insert new one*/
    INSERT
        ModelingNodeState(
            NodeIdentifier,
            PoolWeighting,
            NodeStatus,
            LastModelingResult)
    VALUES(
        @NodeIdentifier,
        ISNULL(@PoolWeighting, 1),
        0,
        NULL)

DECLARE @timestamp datetime
SET @timestamp = CURRENT_TIMESTAMP

/*fill endtime of previous node state*/
UPDATE 
    ModelingNodeStateLog
SET
    EndTime = @timestamp
WHERE
    EndTime IS NULL AND
    NodeIdentifier = @NodeIdentifier AND
    NodeStatus <> @NodeStatus

/*start a new entry in the log (yes, I saw I should remove the IF check and always insert, but that's how it currently is in the db)*/
IF @@ROWCOUNT <> 0
    INSERT
        ModelingNodeStateLog
        (
        NodeIdentifier,
        NodeStatus,
        StartTime
        )
    VALUES
        (
        @NodeIdentifier,
        @NodeStatus,
        @timestamp
        )

RETURN

2 个答案:

答案 0 :(得分:1)

由于其他人没有插话,我将假设我所发生的进化理论是正确的。具体来说,这是由于在ModelingNodeState或ModelingNodeStateLog表上锁定了尚未确认的原点,导致语句超时,而不是连接超时。 LINQ-to-SQL通常便于管理连接,这种区别被混淆了。

这与所有观察到的症状一致:

  1. 连接字符串中故意错误凭据的Sproc调用在连接级别立即被拒绝
  2. 允许连接字符串中使用正确凭据的Sproc调用登录(SQL Server日志显示正常登录/注销),但超时执行锁定表的语句
  3. LINQ隐藏了底层SQL连接管理意味着捕获的堆栈跟踪无法用于区分连接和语句超时,我错误地认为它是连接超时。
  4. 我们不知道如何/为什么/如果这些表中的任何一个或两个都存在全天锁定,但某个地方可能已经完成了未提交的声明,因为这是在涉及DBA工作,迁移的一夜部署之后开始的脚本等等。这可以解释突然开始的事情(在脚本开始之后),持续一整天,然后在违规脚本提交或回滚时突然解决,也许是在工作日结束时关闭运行它的应用程序
  5. 经验教训:

    1. 不要以为你知道什么超时只是因为你在异常或堆栈跟踪中看到了“timeout”这个词
    2. 当我想到这一点时,我意识到应用程序的这个方面可以完全失败,而不会影响关键的应用程序功能。我将重写它,以便重现此问题不会像现在这样使整个应用程序失效。
    3. 感谢社群的反馈,引导我(让我们希望)正确答案!

答案 1 :(得分:0)

从其他计算机还原数据库备份后,我看到了这种行为。

这可以通过管理工作室中的SQL命令修复。见How to fix orphaned SQL Server users