如何在ruby中为oracle 11设置查询超时

时间:2014-06-09 17:17:51

标签: ruby-on-rails ruby oracle activerecord timeout

我看到其他线程说明了如何为mySql做,甚至如何在java中做,但不知道如何在ruby中设置查询超时。

我正在尝试使用OJDBC7在Jruby中使用setQueryTimeout函数,但无法在ruby中找到如何使用它。我尝试了以下内容:

@c.connection.instance_variable_get(:@connection).instance_variable_set(:@query_timeout, 1)
@c.connection.instance_variable_get(:@connection).instance_variable_set(:@read_timeout, 1)
@c.connection.setQueryTimeout(1)

我也尝试修改我的database.yml文件以包含

adapter: jdbc
  driver: oracle.jdbc.driver.OracleDriver
  timeout: 1

以上都没有任何影响,除了引发方法错误的setQueryTimeout。

任何帮助都会很棒

2 个答案:

答案 0 :(得分:0)

所以我找到了让它发挥作用的方法,但我不喜欢它。它是对数据库的非常hackish和孤儿查询,但它至少允许我的应用程序继续执行。我仍然希望找到一种取消声明的方法,这样我就不会发现需要超过10秒的孤立查询。

query_thread = Thread.new {
   #execute query
}

begin
  Timeout::timeout(10) do
    query_thread.join()
  end
rescue
  Thread.kill(query_thread)
  results = Array.new
end

答案 1 :(得分:0)

Oracle-DB上的查询超时适用于Rails 4和JRuby

使用JRuby,您可以使用JBDC-function statement.setQueryTimeout来定义查询超时。

突然间,这需要修补oracle-enhanced_adapter,如下所示。 此示例是迭代器查询的实现,不将结果存储在数组中,该数组也使用查询超时。

# hold open SQL-Cursor and iterate over SQL-result without storing whole result in Array
# Peter Ramm, 02.03.2016

# expand class by getter to allow access on internal variable @raw_statement
ActiveRecord::ConnectionAdapters::OracleEnhancedJDBCConnection::Cursor.class_eval do
  def get_raw_statement
    @raw_statement
  end
end

# Class extension by Module-Declaration : module ActiveRecord, module ConnectionAdapters, module OracleEnhancedDatabaseStatements
# does not work as Engine with Winstone application server, therefore hard manipulation of class ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter
# and extension with method iterate_query

ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do

  # Method comparable with ActiveRecord::ConnectionAdapters::OracleEnhancedDatabaseStatements.exec_query,
  # but without storing whole result in memory
  def iterate_query(sql, name = 'SQL', binds = [], modifier = nil, query_timeout = nil, &block)
    type_casted_binds = binds.map { |col, val|
      [col, type_cast(val, col)]
    }
    log(sql, name, type_casted_binds) do
      cursor = nil
      cached = false
      if without_prepared_statement?(binds)
        cursor = @connection.prepare(sql)
      else
        unless @statements.key? sql
          @statements[sql] = @connection.prepare(sql)
        end

        cursor = @statements[sql]

        binds.each_with_index do |bind, i|
          col, val = bind
          cursor.bind_param(i + 1, type_cast(val, col), col)
        end

        cached = true
      end

      cursor.get_raw_statement.setQueryTimeout(query_timeout) if query_timeout

      cursor.exec

      if name == 'EXPLAIN' and sql =~ /^EXPLAIN/
        res = true
      else
        columns = cursor.get_col_names.map do |col_name|
          @connection.oracle_downcase(col_name).freeze
        end
        fetch_options = {:get_lob_value => (name != 'Writable Large Object')}
        while row = cursor.fetch(fetch_options)
          result_hash = {}
          columns.each_index do |index|
            result_hash[columns[index]] = row[index]
            row[index] = row[index].strip if row[index].class == String   # Remove possible 0x00 at end of string, this leads to error in Internet Explorer
          end
          result_hash.extend SelectHashHelper
          modifier.call(result_hash)  unless modifier.nil?
          yield result_hash
        end
      end

      cursor.close unless cached
      nil
    end
  end #iterate_query


end #class_eval




class SqlSelectIterator

  def initialize(stmt, binds, modifier, query_timeout)
    @stmt           = stmt
    @binds          = binds
    @modifier       = modifier              # proc for modifikation of record
    @query_timeout  = query_timeout
  end

  def each(&block)
    # Execute SQL and call block for every record of result
    ActiveRecord::Base.connection.iterate_query(@stmt, 'sql_select_iterator', @binds, @modifier, @query_timeout, &block)
  end

end

使用上面的类SqlSelectIterator,如下例所示:

SqlSelectIterator.new(stmt, binds, modifier, query_timeout).each do |record|
  process(record) 
end