Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/53.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 死锁后的数据丢失-SQL Server 2008、Ruby on Rails、Apache、Phusion Passenger、Linux、FreeTDS_Ruby On Rails_Sql Server 2008_Passenger_Freetds - Fatal编程技术网

Ruby on rails 死锁后的数据丢失-SQL Server 2008、Ruby on Rails、Apache、Phusion Passenger、Linux、FreeTDS

Ruby on rails 死锁后的数据丢失-SQL Server 2008、Ruby on Rails、Apache、Phusion Passenger、Linux、FreeTDS,ruby-on-rails,sql-server-2008,passenger,freetds,Ruby On Rails,Sql Server 2008,Passenger,Freetds,死锁后的数据丢失-SQL Server 2008、Ruby on Rails、Phusion Passenger、Linux、FreeTDS 在我负责的RubyonRails内部网应用程序中,我遇到了一个导致数据丢失的神秘问题。如果这不是严格意义上的编程问题,我深表歉意——至少我维护了应用程序的Ruby代码。到目前为止,这一问题在两年内已经发生了三次 环境: Linux-RedHat Enterprise Server 5.2 Apache2Web服务器(httpd-2.2.3-11.el5_

死锁后的数据丢失-SQL Server 2008、Ruby on Rails、Phusion Passenger、Linux、FreeTDS

在我负责的RubyonRails内部网应用程序中,我遇到了一个导致数据丢失的神秘问题。如果这不是严格意义上的编程问题,我深表歉意——至少我维护了应用程序的Ruby代码。到目前为止,这一问题在两年内已经发生了三次

环境:

  • Linux-RedHat Enterprise Server 5.2
  • Apache2Web服务器(httpd-2.2.3-11.el5_2.4.rpm)
  • Phusion乘客2.2.15
  • Ruby 1.8.7、Rails 2.3.8和gems:
    • actionmailer(2.3.8)
    • actionpack(2.3.8)
    • activerecord(2.3.8)
    • activerecord sqlserver适配器(2.3.8)
    • activeresource(2.3.8)
    • activesupport(2.3.8)
    • 阿卡米(1.2.0)
    • 建筑商(3.0.0)
    • 例外情况通知(2.3.3.0)
    • fastthread(1.0.7)
    • gyoku(0.4.6)
    • httpi(1.1.1)
    • mime类型(1.16)
    • nokogiri(1.4.4)
    • 诺丽(1.1.3)
    • 乘客(2.2.15)
    • 机架(1.1.0)
    • 轨道(2.3.8)
    • 耙(0.8.7)
    • RubyNetLDAP(0.0.4)
    • rubyjedi actionwebservice(2.3.5.20100714122544)
    • 萨沃恩(1.1.0)
    • 芥末(2.5.1)
    • 将分页(2.3.14)
  • SQL Server 2008数据库服务器
  • 通过ActiveRecord访问数据库
  • 数据库驱动程序:freetds-0.82、unixODBC-2.3.0.tar.gz、ruby-odbc-0.99991.tar.gz
症状:

  • 请求锁定数据库资源的用户操作涉及死锁情况
  • SQL Server通过终止死锁中涉及的进程来解决死锁,这样至少其中一些进程可以成功完成
  • 在Rails应用程序端,死锁导致未处理的异常(我通过exception_通知gem得到通知)
  • 在死锁之后,活动Rails进程的数量在增加(这触发了监控系统的另一个通知),这些进程似乎处于挂起状态
  • 发生这种情况的原因尚不清楚。这些进程似乎挂在数据库操作中(根据Rails日志)。通常情况下,我希望SQL server的死锁解决功能不会让阻塞进程悬而未决
  • 在前两个案例中,我重新启动了web服务器,作为对异常/挂起过程的反应。在第三种情况下(我正在度假),没有人对通知做出反应,但周末运行的cronjob显然也在停止进程(通过乘客触摸“restart.txt”软重启,效果相同)
  • web服务器重新启动后,用户报告数据丢失。在重新启动web服务器之前,从用户的角度来看,数据按预期进行了处理。与我们通信的其他系统中的Rails日志和数据似乎表明事务已正确提交。web服务器重新启动后,突然之间,自死锁发生以来的所有数据库更改都丢失了。例如,我们有一个“users”表,其中有一个“last_access”列,该列在每个用户操作时更新。重新启动web服务器后,最新的“last_access”值已存在一天。似乎丢失了所有事务,只有@标识值与数据丢失前设置的值保持一致
  • 我从我们的IT部门(维护数据库服务器的IT部门)收到的信息似乎表明,所有丢失的DB操作都是一个大型事务的一部分,该事务缺少最终提交。当然,我期望的是每个Rails用户操作都运行一个或多个单独的事务,但是SQL Server事务日志将所有操作显示为一个巨大事务的一部分
在我看来,好像发生了这样的事情:

  • 其中一个相关组件(例如Phusion Passenger、FreeTDS、SQL Server)中的错误导致并行运行的Rails进程共享数据库连接,也可能导致进程挂起
  • 其中一个涉及的进程位于事务中,并在提交之前挂起
  • 由于其他进程共享相同的连接(我假设),因此它们也在同一事务中
  • 由于进程共享连接,因此用户能够看到数据更改(在web服务器重新启动之前),即使提交处于挂起状态
  • web服务器重新启动强制中断连接并回滚事务
这有意义吗?我想知道是否有人有过类似的经历或暗示,我可以进一步研究。我怀疑Passenger中有一个bug,它可能导致数据库连接的文件描述符分叉,但我无法重现它。乘客似乎在每个分叉上正确地创建了新的DB连接


我正在考虑将数据库的隔离模型更改为“读取提交的快照”,以减少死锁的数量,但我知道这并不能解决问题的根本原因,这可能会给我带来其他问题。

我现在能够自己找到问题所在,并希望与现在或将来可能面临类似问题的任何人分享解决方案

发生了以下情况:

  • 作为事务的一部分运行的更新操作涉及死锁
  • SQL Server已选择更新操作作为死锁牺牲品并回滚事务
  • 导致出现如下异常的
    ActiveRecord::StatementInvalid
    异常:

    A ActiveRecord::StatementInvalid occurred in (...):
    
    ODBC::Error: 37000 (1205) [FreeTDS][SQL Server]Transaction (Process ID 55) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.: UPDATE [(....tablename....)] SET [position] = 1 WHERE [id] = 795419
    /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/connection_adapters/abstract_adapter.rb:221:in `log'
    
  • ActiveRecord::ConnectionAdapters::DatabaseStatements.transaction()中,异常的处理方式如下:

    rescue Exception => database_transaction_rollback
      if transaction_open && !outside_transaction?
        transaction_open = false
        decrement_open_transactions
        if open_transactions == 0
          rollback_db_transaction
        else
          rollback_to_savepoint
        end
      end
      raise unless database_transaction_rollback.is_a?(ActiveRecord::Rollback)
    end
    
  • transaction\u op
    
    def outside_transaction?
      info_schema_query { select_value("SELECT @@TRANCOUNT") == 0 }
    end
    
    SQL (1.0ms)   SELECT @@TRANCOUNT
    => TRANCOUNT=0
    
    class ApplicationController < ActionController::Base
      rescue_from Exception, :with => :render_error
    
    flash[:exception_message] = exception.message
    
    EXECUTE (1.2ms)   BEGIN TRANSACTION
    SQL (1.1ms)   SELECT session_id, data,id FROM sessions WHERE id=150091
    EXECUTE (1.3ms)   UPDATE sessions SET updated_at=CURRENT_TIMESTAMP, data='BAh7FDoWdW9faGlk(........)' WHERE id=150091
    CACHE (0.0ms)   SELECT @@TRANCOUNT
    => TRANCOUNT=0
    
    if outside_transaction? # (this does the SELECT @@TRANCOUNT)
      @open_transactions = 0      # Getting here!
    elsif transaction_open
      decrement_open_transactions
      begin
        if open_transactions == 0
          commit_db_transaction   # NOT getting here!
        else
          release_savepoint
        end
      rescue Exception => database_transaction_rollback
        if open_transactions == 0
          rollback_db_transaction
        else
          rollback_to_savepoint
        end
        raise
      end
    end