Ticket #428: oci_reconnect.patch
| File oci_reconnect.patch, 6.6 kB (added by mschoen, 3 years ago) |
|---|
-
activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
old new 25 25 @@reconnect_success = 0 26 26 @@reconnect_failure = 0 27 27 def self.reconnect_success_rate 28 @@reconnect_success.to_f / (@@reconnect_success + @@reconnect_failure) 28 @@reconnect_success.to_f / (@@reconnect_success + @@reconnect_failure) * 100 29 29 end 30 30 31 31 def initialize(connection, logger = nil) #:nodoc: … … 38 38 def adapter_name 39 39 'Abstract' 40 40 end 41 41 42 42 # Does this adapter support migrations? Backend specific, as the 43 43 # abstract adapter always returns +false+. 44 44 def supports_migrations? … … 50 50 rt 51 51 end 52 52 53 protected 53 protected 54 54 def log(sql, name) 55 55 if block_given? 56 56 if @logger and @logger.level <= Logger::INFO … … 124 124 end 125 125 end 126 126 end 127 -
activerecord/lib/active_record/connection_adapters/oci_adapter.rb
old new 23 23 # portions Copyright 2005 Graham Jenkins 24 24 25 25 require 'active_record/connection_adapters/abstract_adapter' 26 require 'delegate' 26 27 27 28 begin 28 29 require_library_or_gem 'oci8' unless self.class.const_defined? :OCI8 … … 30 31 module ActiveRecord 31 32 class Base 32 33 def self.oci_connection(config) #:nodoc: 33 conn = OCI8.new config[:username], config[:password], config[:host] 34 conn.exec %q{alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'} 35 conn.exec %q{alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS'} 36 conn.autocommit = true 37 ConnectionAdapters::OCIAdapter.new conn, logger 34 # Use OCI8AutoRecover instead of normal OCI8 driver. 35 ConnectionAdapters::OCIAdapter.new OCI8AutoRecover.new(config), logger 38 36 end 39 37 40 38 # Enable the id column to be bound into the sql later, by the adapter's insert method. … … 213 211 end 214 212 215 213 214 # CONNECTION MANAGEMENT ====================================# 215 216 # Returns true if the connection is active. 217 def active? 218 # Just checks the active flag, which is set false if the last exec 219 # got an error indicating a bad connection. An alternative would be 220 # to call #ping, which is more expensive (and should always get 221 # the same result). 222 @connection.active? 223 end 224 225 # Reconnects to the database. 226 def reconnect! 227 begin 228 @connection.reset! 229 rescue OCIError => e 230 @logger.warn "#{adapter_name} automatic reconnection failed: #{e.message}" 231 end 232 end 233 234 216 235 # DATABASE STATEMENTS ====================================== 217 236 # 218 237 # see: abstract/database_statements.rb … … 337 356 and syn.owner (+)= cat.owner } 338 357 end 339 358 340 select_all(table_cols ).map do |row|359 select_all(table_cols, name).map do |row| 341 360 row['data_default'].sub!(/^'(.*)'\s*$/, '\1') if row['data_default'] 342 361 OCIColumn.new( 343 362 oci_downcase(row['column_name']), … … 485 504 when 187 : @stmt.defineByPos(i, OraDate) # Read TIMESTAMP values 486 505 else define_a_column_pre_ar i 487 506 end 488 end507 end 489 508 end 490 509 end 491 510 511 512 # The OCIConnectionFactory factors out the code necessary to connect and 513 # configure an OCI connection. 514 class OCIConnectionFactory 515 def new_connection(username, password, host) 516 conn = OCI8.new username, password, host 517 conn.exec %q{alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'} 518 conn.exec %q{alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS'} 519 conn.autocommit = true 520 conn 521 end 522 end 523 524 525 # The OCI8AutoRecover class enhances the OCI8 driver with auto-recover and 526 # reset functionality. If a call to #exec fails, and autocommit is turned on 527 # (ie., we're not in the middle of a longer transaction), it will 528 # automatically reconnect and try again. If autocommit is turned off, 529 # this would be dangerous (as the earlier part of the implied transaction 530 # may have failed silently if the connection died) -- so instead the 531 # connection is marked as dead, to be reconnected on it's next use. 532 class OCI8AutoRecover < DelegateClass(OCI8) 533 attr_accessor :active 534 alias :active? :active 535 536 cattr_accessor :auto_retry 537 class << self 538 alias :auto_retry? :auto_retry 539 end 540 @@auto_retry = false 541 542 def initialize(config, factory = OCIConnectionFactory.new) 543 @active = true 544 @username, @password, @host = config[:username], config[:password], config[:host] 545 @factory = factory 546 @connection = @factory.new_connection @username, @password, @host 547 super @connection 548 end 549 550 # Checks connection, returns true if active. Note that ping actively 551 # checks the connection, while #active? simply returns the last 552 # known state. 553 def ping 554 @active = true 555 begin 556 @connection.commit 557 rescue 558 @active = false 559 end 560 active? 561 end 562 563 # Resets connection, by logging off and creating a new connection. 564 def reset! 565 logoff rescue nil 566 begin 567 @connection = @factory.new_connection @username, @password, @host 568 __setobj__ @connection 569 @active = true 570 rescue 571 @active = false 572 raise 573 end 574 end 575 576 # ORA-00028: your session has been killed 577 # ORA-01012: not logged on 578 # ORA-03113: end-of-file on communication channel 579 # ORA-03114: not connected to ORACLE 580 LOST_CONNECTION_ERROR_CODES = [ 28, 1012, 3113, 3114 ] 581 582 # Adds auto-recovery functionality. 583 # 584 # See: http://www.jiubao.org/ruby-oci8/api.en.html#label-11 585 def exec(sql, *bindvars) 586 should_retry = self.class.auto_retry? && autocommit? 587 588 begin 589 @connection.exec(sql, *bindvars) 590 rescue OCIError => e 591 raise unless LOST_CONNECTION_ERROR_CODES.include?(e.code) 592 @active = false 593 raise unless should_retry 594 should_retry = false 595 reset! rescue nil 596 retry 597 end 598 end 599 600 end 601 492 602 rescue LoadError 493 603 # OCI8 driver is unavailable. 494 604 end