Sql server EXECUTE后的事务计数表示BEGIN和COMMIT语句的数量不匹配。当编码标准不符合';还不够吗?

Sql server EXECUTE后的事务计数表示BEGIN和COMMIT语句的数量不匹配。当编码标准不符合';还不够吗?,sql-server,transactions,Sql Server,Transactions,我们的商店有大量的T-SQL存储过程逻辑,支持通过JDBC连接的web应用程序。SQLServer2008R2 有时,一个SQL开发人员会犯错误,编写一个使事务保持打开状态的存储过程。如果你曾在野外见过这种情况,那真是太难看了。连接被释放回池,并带有其恶意、未提交的事务。后续的事务似乎工作得很好,但锁实际上在累积,并且工作实际上是未提交的。一段时间后,阻塞变得足够严重,以至于用户或监控软件会注意到它。识别阻塞链的头和打开的事务非常容易,但是哪个过程使事务保持打开状态,您可以对此做些什么?在这一点

我们的商店有大量的T-SQL存储过程逻辑,支持通过JDBC连接的web应用程序。SQLServer2008R2

有时,一个SQL开发人员会犯错误,编写一个使事务保持打开状态的存储过程。如果你曾在野外见过这种情况,那真是太难看了。连接被释放回池,并带有其恶意、未提交的事务。后续的事务似乎工作得很好,但锁实际上在累积,并且工作实际上是未提交的。一段时间后,阻塞变得足够严重,以至于用户或监控软件会注意到它。识别阻塞链的头和打开的事务非常容易,但是哪个过程使事务保持打开状态,您可以对此做些什么?在这一点上,最好的反应似乎是杀死spid,清除阻塞,并承担后果

我们有许多编码标准和实践来防止这种情况发生,但是错误返回或嵌套不良的IF-block或TRY/CATCH块可能会造成严重破坏,通常是在一些没有完全覆盖的边缘情况下

问题,对于tl;dr crowd,这是不是:有什么技术可以确保启动事务的过程不会在退出时意外地将其打开,即使开发人员犯了愚蠢的错误?

我知道这是个大问题,但请听我说完。我们不希望防止编码错误的任何后果。我们只是希望包含特定交易的后果。简而言之,我们希望将错误266(“执行后的事务计数…”)的行为更改为仅回滚所有事务。甚至是真正的提交所有事务。我们的过程属于SQL Try/Catch块,因此该过程可以对不匹配本身作出响应。(如果我是世界之王,那将是我的选择——让我捕捉SQL中的SQL运行时错误,我将决定怎么做。)

假设我们的问题代码在proc AddProductToRecentlyViewed中。最近有人对此进行了修改:

IF @UserName='Grue' RETURN -100;  -- Grue is a jerk.
Grue出现了,看了看一些产品,在最近观看的视频中没有看到它们,然后去吃午饭。慢慢地,这个事务被传递到下一个web事务,一切都停止了。更糟糕的是,我们的实际购买似乎已经承诺,有被退回的危险。在操作方面,我们很快就知道有一个错误,但要追溯到这个过程需要相当多的挖掘和时间

心不在焉地打断别人的谈话是一回事。关闭整个应用程序是另一回事

我们在SQL中有一个标准的try/catch块,与推荐的块类似。我们有单元测试和代码审查,等等。事实上,在代码审查和测试中发现了错误,并进行了更正——但预更正的版本意外升级。我的副总裁说:“我们永远不会阻止每一个愚蠢的错误。我们怎样才能让下一个愚蠢的错误不那么痛苦呢?”

如果我们有办法确保程序不能让事务保持打开状态,我们可能会遭受AddProductToRecentlyViewed无法工作的后果。但我找不到任何配置值或运行时设置来提供这种效果

除了不在T-SQL代码中出错之外,还有谁知道一种方法可以让它更加防弹吗

以下是我们正在考虑的几件事:

  • 在应用程序中实现一个标准错误处理程序,以监视错误266并执行某些操作。我们可能会从应用程序错误处理程序发出一个显式回滚,或者调用一个只检查XACT_状态、回滚和日志的存储过程。(这对我们来说有点挑战,因为我们没有DAO的中央错误处理程序,因此可能需要一些时间来添加。此外,我们更希望在数据库中强制执行“无打开的事务”规则,而不是依赖所有调用应用程序来正确执行。)
  • 在我们最常调用的一个存储过程中,如果XACT_STATE0回滚或如果XACT_STATE0提交到该过程的最开始,则添加该插件。这是相当困难的,但它会对事务计数施加持续的向下压力。这无助于确定根本原因,而且在tran保持打开状态和频繁的进程将其击落之间,我们还有一个小的时间窗口。但是当我们对日志和监视器做出反应时,它会使伤口完全自我愈合

是否有其他设置或配置有助于检测和防止此错误,或者有其他解决方案可用于控制此错误?

是否考虑过在每次打开连接时执行
IF@@tracount>0回滚
?Dan,谢谢。我们已经讨论过了——我正在与我们的应用程序团队合作,看看它是否可行,是否适合应用程序。这是一个ColdFusion应用程序,所以我很确定他们只是声明了一个带有datasource属性的,该属性指向机器上的已知数据源。CF在该范围之外处理连接池,因此当连接超出范围时,CF将其返回到池并用于下一个请求。(如果它不这样做,打开的事务就不重要了。)我认为没有地方可以这样“初始化”连接……我相信查询需要在应用层完成,因为JDBC(至少是Microsoft驱动程序)依赖应用服务器来处理实际的池。这与其他一些API不同,在这些API中,池由API本身完成,并且在使用sp_reset_connection RPC调用重用时清理连接。