activerecord 3.2.22.5 → 4.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1024 -543
- data/MIT-LICENSE +1 -1
- data/README.rdoc +20 -29
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +55 -44
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/associations.rb +204 -276
- data/lib/active_record/associations/alias_tracker.rb +1 -1
- data/lib/active_record/associations/association.rb +30 -35
- data/lib/active_record/associations/association_scope.rb +40 -40
- data/lib/active_record/associations/belongs_to_association.rb +15 -2
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +35 -57
- data/lib/active_record/associations/builder/collection_association.rb +54 -40
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +13 -50
- data/lib/active_record/associations/builder/singular_association.rb +13 -13
- data/lib/active_record/associations/collection_association.rb +92 -88
- data/lib/active_record/associations/collection_proxy.rb +913 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
- data/lib/active_record/associations/has_many_association.rb +35 -9
- data/lib/active_record/associations/has_many_through_association.rb +24 -14
- data/lib/active_record/associations/has_one_association.rb +33 -13
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_helper.rb +1 -11
- data/lib/active_record/associations/preloader.rb +14 -17
- data/lib/active_record/associations/preloader/association.rb +29 -33
- data/lib/active_record/associations/preloader/collection_association.rb +1 -1
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/through_association.rb +13 -17
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +2 -2
- data/lib/active_record/attribute_assignment.rb +133 -153
- data/lib/active_record/attribute_methods.rb +196 -93
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +31 -28
- data/lib/active_record/attribute_methods/primary_key.rb +38 -30
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +62 -91
- data/lib/active_record/attribute_methods/serialization.rb +97 -66
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
- data/lib/active_record/attribute_methods/write.rb +32 -39
- data/lib/active_record/autosave_association.rb +56 -70
- data/lib/active_record/base.rb +53 -450
- data/lib/active_record/callbacks.rb +53 -18
- data/lib/active_record/coders/yaml_column.rb +11 -9
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
- data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
- data/lib/active_record/connection_adapters/column.rb +46 -24
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +428 -0
- data/lib/active_record/counter_cache.rb +106 -108
- data/lib/active_record/dynamic_matchers.rb +110 -63
- data/lib/active_record/errors.rb +25 -8
- data/lib/active_record/explain.rb +8 -58
- data/lib/active_record/explain_subscriber.rb +6 -3
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +146 -148
- data/lib/active_record/inheritance.rb +77 -59
- data/lib/active_record/integration.rb +5 -5
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +38 -42
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration.rb +318 -153
- data/lib/active_record/migration/command_recorder.rb +90 -31
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/model_schema.rb +69 -92
- data/lib/active_record/nested_attributes.rb +113 -148
- data/lib/active_record/null_relation.rb +65 -0
- data/lib/active_record/persistence.rb +188 -97
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +91 -36
- data/lib/active_record/railties/console_sandbox.rb +0 -2
- data/lib/active_record/railties/controller_runtime.rb +2 -2
- data/lib/active_record/railties/databases.rake +90 -309
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +7 -3
- data/lib/active_record/reflection.rb +72 -56
- data/lib/active_record/relation.rb +241 -157
- data/lib/active_record/relation/batches.rb +25 -22
- data/lib/active_record/relation/calculations.rb +143 -121
- data/lib/active_record/relation/delegation.rb +96 -18
- data/lib/active_record/relation/finder_methods.rb +117 -183
- data/lib/active_record/relation/merger.rb +133 -0
- data/lib/active_record/relation/predicate_builder.rb +90 -42
- data/lib/active_record/relation/query_methods.rb +666 -136
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/result.rb +33 -6
- data/lib/active_record/sanitization.rb +24 -50
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +31 -39
- data/lib/active_record/schema_migration.rb +36 -0
- data/lib/active_record/scoping.rb +0 -124
- data/lib/active_record/scoping/default.rb +48 -45
- data/lib/active_record/scoping/named.rb +74 -103
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/store.rb +119 -15
- data/lib/active_record/tasks/database_tasks.rb +158 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
- data/lib/active_record/test_case.rb +61 -38
- data/lib/active_record/timestamp.rb +8 -9
- data/lib/active_record/transactions.rb +65 -51
- data/lib/active_record/validations.rb +17 -15
- data/lib/active_record/validations/associated.rb +20 -14
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +93 -52
- data/lib/active_record/version.rb +4 -4
- data/lib/rails/generators/active_record.rb +3 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- metadata +53 -46
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/rails/generators/active_record/migration.rb +0 -15
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
| @@ -1,5 +1,3 @@ | |
| 1 | 
            -
            require 'active_support/core_ext/array/wrap'
         | 
| 2 | 
            -
             | 
| 3 1 | 
             
            module ActiveRecord
         | 
| 4 2 | 
             
              # = Active Record Callbacks
         | 
| 5 3 | 
             
              #
         | 
| @@ -25,7 +23,7 @@ module ActiveRecord | |
| 25 23 | 
             
              # Check out <tt>ActiveRecord::Transactions</tt> for more details about <tt>after_commit</tt> and
         | 
| 26 24 | 
             
              # <tt>after_rollback</tt>.
         | 
| 27 25 | 
             
              #
         | 
| 28 | 
            -
              # Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that | 
| 26 | 
            +
              # Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that
         | 
| 29 27 | 
             
              # is found and instantiated by a finder, with <tt>after_initialize</tt> being triggered after new objects
         | 
| 30 28 | 
             
              # are instantiated as well.
         | 
| 31 29 | 
             
              #
         | 
| @@ -36,8 +34,8 @@ module ActiveRecord | |
| 36 34 | 
             
              # Examples:
         | 
| 37 35 | 
             
              #   class CreditCard < ActiveRecord::Base
         | 
| 38 36 | 
             
              #     # Strip everything but digits, so the user can specify "555 234 34" or
         | 
| 39 | 
            -
              #     # "5552-3434"  | 
| 40 | 
            -
              #     before_validation(: | 
| 37 | 
            +
              #     # "5552-3434" and both will mean "55523434"
         | 
| 38 | 
            +
              #     before_validation(on: :create) do
         | 
| 41 39 | 
             
              #       self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
         | 
| 42 40 | 
             
              #     end
         | 
| 43 41 | 
             
              #   end
         | 
| @@ -202,6 +200,40 @@ module ActiveRecord | |
| 202 200 | 
             
              # Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
         | 
| 203 201 | 
             
              # methods on the model, which are called last.
         | 
| 204 202 | 
             
              #
         | 
| 203 | 
            +
              # == Ordering callbacks
         | 
| 204 | 
            +
              #
         | 
| 205 | 
            +
              # Sometimes the code needs that the callbacks execute in a specific order. For example, a +before_destroy+
         | 
| 206 | 
            +
              # callback (+log_children+ in this case) should be executed before the children get destroyed by the +dependent: destroy+ option.
         | 
| 207 | 
            +
              #
         | 
| 208 | 
            +
              # Let's look at the code below:
         | 
| 209 | 
            +
              #
         | 
| 210 | 
            +
              #   class Topic < ActiveRecord::Base
         | 
| 211 | 
            +
              #     has_many :children, dependent: destroy
         | 
| 212 | 
            +
              #
         | 
| 213 | 
            +
              #     before_destroy :log_children
         | 
| 214 | 
            +
              #
         | 
| 215 | 
            +
              #     private
         | 
| 216 | 
            +
              #       def log_children
         | 
| 217 | 
            +
              #         # Child processing
         | 
| 218 | 
            +
              #       end
         | 
| 219 | 
            +
              #   end
         | 
| 220 | 
            +
              #
         | 
| 221 | 
            +
              # In this case, the problem is that when the +before_destroy+ callback is executed, the children are not available
         | 
| 222 | 
            +
              # because the +destroy+ callback gets executed first. You can use the +prepend+ option on the +before_destroy+ callback to avoid this.
         | 
| 223 | 
            +
              #
         | 
| 224 | 
            +
              #   class Topic < ActiveRecord::Base
         | 
| 225 | 
            +
              #     has_many :children, dependent: destroy
         | 
| 226 | 
            +
              #
         | 
| 227 | 
            +
              #     before_destroy :log_children, prepend: true
         | 
| 228 | 
            +
              #
         | 
| 229 | 
            +
              #     private
         | 
| 230 | 
            +
              #       def log_children
         | 
| 231 | 
            +
              #         # Child processing
         | 
| 232 | 
            +
              #       end
         | 
| 233 | 
            +
              #   end
         | 
| 234 | 
            +
              #
         | 
| 235 | 
            +
              # This way, the +before_destroy+ gets executed before the <tt>dependent: destroy</tt> is called, and the data is still available.
         | 
| 236 | 
            +
              #
         | 
| 205 237 | 
             
              # == Transactions
         | 
| 206 238 | 
             
              #
         | 
| 207 239 | 
             
              # The entire callback chain of a +save+, <tt>save!</tt>, or +destroy+ call runs
         | 
| @@ -215,23 +247,23 @@ module ActiveRecord | |
| 215 247 | 
             
              # instead of quietly returning +false+.
         | 
| 216 248 | 
             
              #
         | 
| 217 249 | 
             
              # == Debugging callbacks
         | 
| 218 | 
            -
              # | 
| 219 | 
            -
              # The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support | 
| 250 | 
            +
              #
         | 
| 251 | 
            +
              # The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support
         | 
| 220 252 | 
             
              # <tt>:before</tt>, <tt>:after</tt> and <tt>:around</tt> as values for the <tt>kind</tt> property. The <tt>kind</tt> property
         | 
| 221 253 | 
             
              # defines what part of the chain the callback runs in.
         | 
| 222 | 
            -
              # | 
| 223 | 
            -
              # To find all callbacks in the before_save callback chain: | 
| 224 | 
            -
              # | 
| 254 | 
            +
              #
         | 
| 255 | 
            +
              # To find all callbacks in the before_save callback chain:
         | 
| 256 | 
            +
              #
         | 
| 225 257 | 
             
              #   Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }
         | 
| 226 | 
            -
              # | 
| 258 | 
            +
              #
         | 
| 227 259 | 
             
              # Returns an array of callback objects that form the before_save chain.
         | 
| 228 | 
            -
              # | 
| 260 | 
            +
              #
         | 
| 229 261 | 
             
              # To further check if the before_save chain contains a proc defined as <tt>rest_when_dead</tt> use the <tt>filter</tt> property of the callback object:
         | 
| 230 | 
            -
              # | 
| 262 | 
            +
              #
         | 
| 231 263 | 
             
              #   Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }.collect(&:filter).include?(:rest_when_dead)
         | 
| 232 | 
            -
              # | 
| 264 | 
            +
              #
         | 
| 233 265 | 
             
              # Returns true or false depending on whether the proc is contained in the before_save callback chain on a Topic model.
         | 
| 234 | 
            -
              # | 
| 266 | 
            +
              #
         | 
| 235 267 | 
             
              module Callbacks
         | 
| 236 268 | 
             
                extend ActiveSupport::Concern
         | 
| 237 269 |  | 
| @@ -242,8 +274,11 @@ module ActiveRecord | |
| 242 274 | 
             
                  :before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
         | 
| 243 275 | 
             
                ]
         | 
| 244 276 |  | 
| 277 | 
            +
                module ClassMethods
         | 
| 278 | 
            +
                  include ActiveModel::Callbacks
         | 
| 279 | 
            +
                end
         | 
| 280 | 
            +
             | 
| 245 281 | 
             
                included do
         | 
| 246 | 
            -
                  extend ActiveModel::Callbacks
         | 
| 247 282 | 
             
                  include ActiveModel::Validations::Callbacks
         | 
| 248 283 |  | 
| 249 284 | 
             
                  define_model_callbacks :initialize, :find, :touch, :only => :after
         | 
| @@ -264,11 +299,11 @@ module ActiveRecord | |
| 264 299 | 
             
                  run_callbacks(:save) { super }
         | 
| 265 300 | 
             
                end
         | 
| 266 301 |  | 
| 267 | 
            -
                def  | 
| 302 | 
            +
                def create_record #:nodoc:
         | 
| 268 303 | 
             
                  run_callbacks(:create) { super }
         | 
| 269 304 | 
             
                end
         | 
| 270 305 |  | 
| 271 | 
            -
                def  | 
| 306 | 
            +
                def update_record(*) #:nodoc:
         | 
| 272 307 | 
             
                  run_callbacks(:update) { super }
         | 
| 273 308 | 
             
                end
         | 
| 274 309 | 
             
              end
         | 
| @@ -1,12 +1,9 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
              # :stopdoc:
         | 
| 3 | 
            -
              module Coders
         | 
| 4 | 
            -
                class YAMLColumn
         | 
| 5 | 
            -
                  RESCUE_ERRORS = [ ArgumentError ]
         | 
| 1 | 
            +
            require 'yaml'
         | 
| 6 2 |  | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 3 | 
            +
            module ActiveRecord
         | 
| 4 | 
            +
              module Coders # :nodoc:
         | 
| 5 | 
            +
                class YAMLColumn # :nodoc:
         | 
| 6 | 
            +
                  RESCUE_ERRORS = [ ArgumentError, Psych::SyntaxError ]
         | 
| 10 7 |  | 
| 11 8 | 
             
                  attr_accessor :object_class
         | 
| 12 9 |  | 
| @@ -15,6 +12,12 @@ module ActiveRecord | |
| 15 12 | 
             
                  end
         | 
| 16 13 |  | 
| 17 14 | 
             
                  def dump(obj)
         | 
| 15 | 
            +
                    return if obj.nil?
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    unless obj.is_a?(object_class)
         | 
| 18 | 
            +
                      raise SerializationTypeMismatch,
         | 
| 19 | 
            +
                        "Attribute was supposed to be a #{object_class}, but was a #{obj.class}. -- #{obj.inspect}"
         | 
| 20 | 
            +
                    end
         | 
| 18 21 | 
             
                    YAML.dump obj
         | 
| 19 22 | 
             
                  end
         | 
| 20 23 |  | 
| @@ -37,5 +40,4 @@ module ActiveRecord | |
| 37 40 | 
             
                  end
         | 
| 38 41 | 
             
                end
         | 
| 39 42 | 
             
              end
         | 
| 40 | 
            -
              # :startdoc
         | 
| 41 43 | 
             
            end
         | 
| @@ -1,11 +1,12 @@ | |
| 1 1 | 
             
            require 'thread'
         | 
| 2 | 
            +
            require 'thread_safe'
         | 
| 2 3 | 
             
            require 'monitor'
         | 
| 3 4 | 
             
            require 'set'
         | 
| 4 | 
            -
            require 'active_support/core_ext/module/deprecation'
         | 
| 5 5 |  | 
| 6 6 | 
             
            module ActiveRecord
         | 
| 7 7 | 
             
              # Raised when a connection could not be obtained within the connection
         | 
| 8 | 
            -
              # acquisition timeout period | 
| 8 | 
            +
              # acquisition timeout period: because max connections in pool
         | 
| 9 | 
            +
              # are in use.
         | 
| 9 10 | 
             
              class ConnectionTimeoutError < ConnectionNotEstablished
         | 
| 10 11 | 
             
              end
         | 
| 11 12 |  | 
| @@ -50,20 +51,179 @@ module ActiveRecord | |
| 50 51 | 
             
                #
         | 
| 51 52 | 
             
                # == Options
         | 
| 52 53 | 
             
                #
         | 
| 53 | 
            -
                # There are  | 
| 54 | 
            +
                # There are several connection-pooling-related options that you can add to
         | 
| 54 55 | 
             
                # your database connection configuration:
         | 
| 55 56 | 
             
                #
         | 
| 56 57 | 
             
                # * +pool+: number indicating size of connection pool (default 5)
         | 
| 57 | 
            -
                # * + | 
| 58 | 
            -
                #    | 
| 59 | 
            -
                # | 
| 60 | 
            -
                #    | 
| 61 | 
            -
                #    | 
| 58 | 
            +
                # * +checkout_timeout+: number of seconds to block and wait for a connection
         | 
| 59 | 
            +
                #   before giving up and raising a timeout error (default 5 seconds).
         | 
| 60 | 
            +
                # * +reaping_frequency+: frequency in seconds to periodically run the
         | 
| 61 | 
            +
                #   Reaper, which attempts to find and close dead connections, which can
         | 
| 62 | 
            +
                #   occur if a programmer forgets to close a connection at the end of a
         | 
| 63 | 
            +
                #   thread or a thread dies unexpectedly. (Default nil, which means don't
         | 
| 64 | 
            +
                #   run the Reaper).
         | 
| 65 | 
            +
                # * +dead_connection_timeout+: number of seconds from last checkout
         | 
| 66 | 
            +
                #   after which the Reaper will consider a connection reapable. (default
         | 
| 67 | 
            +
                #   5 seconds).
         | 
| 62 68 | 
             
                class ConnectionPool
         | 
| 69 | 
            +
                  # Threadsafe, fair, FIFO queue.  Meant to be used by ConnectionPool
         | 
| 70 | 
            +
                  # with which it shares a Monitor.  But could be a generic Queue.
         | 
| 71 | 
            +
                  #
         | 
| 72 | 
            +
                  # The Queue in stdlib's 'thread' could replace this class except
         | 
| 73 | 
            +
                  # stdlib's doesn't support waiting with a timeout.
         | 
| 74 | 
            +
                  class Queue
         | 
| 75 | 
            +
                    def initialize(lock = Monitor.new)
         | 
| 76 | 
            +
                      @lock = lock
         | 
| 77 | 
            +
                      @cond = @lock.new_cond
         | 
| 78 | 
            +
                      @num_waiting = 0
         | 
| 79 | 
            +
                      @queue = []
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                    # Test if any threads are currently waiting on the queue.
         | 
| 83 | 
            +
                    def any_waiting?
         | 
| 84 | 
            +
                      synchronize do
         | 
| 85 | 
            +
                        @num_waiting > 0
         | 
| 86 | 
            +
                      end
         | 
| 87 | 
            +
                    end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                    # Return the number of threads currently waiting on this
         | 
| 90 | 
            +
                    # queue.
         | 
| 91 | 
            +
                    def num_waiting
         | 
| 92 | 
            +
                      synchronize do
         | 
| 93 | 
            +
                        @num_waiting
         | 
| 94 | 
            +
                      end
         | 
| 95 | 
            +
                    end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                    # Add +element+ to the queue.  Never blocks.
         | 
| 98 | 
            +
                    def add(element)
         | 
| 99 | 
            +
                      synchronize do
         | 
| 100 | 
            +
                        @queue.push element
         | 
| 101 | 
            +
                        @cond.signal
         | 
| 102 | 
            +
                      end
         | 
| 103 | 
            +
                    end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                    # If +element+ is in the queue, remove and return it, or nil.
         | 
| 106 | 
            +
                    def delete(element)
         | 
| 107 | 
            +
                      synchronize do
         | 
| 108 | 
            +
                        @queue.delete(element)
         | 
| 109 | 
            +
                      end
         | 
| 110 | 
            +
                    end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                    # Remove all elements from the queue.
         | 
| 113 | 
            +
                    def clear
         | 
| 114 | 
            +
                      synchronize do
         | 
| 115 | 
            +
                        @queue.clear
         | 
| 116 | 
            +
                      end
         | 
| 117 | 
            +
                    end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                    # Remove the head of the queue.
         | 
| 120 | 
            +
                    #
         | 
| 121 | 
            +
                    # If +timeout+ is not given, remove and return the head the
         | 
| 122 | 
            +
                    # queue if the number of available elements is strictly
         | 
| 123 | 
            +
                    # greater than the number of threads currently waiting (that
         | 
| 124 | 
            +
                    # is, don't jump ahead in line).  Otherwise, return nil.
         | 
| 125 | 
            +
                    #
         | 
| 126 | 
            +
                    # If +timeout+ is given, block if it there is no element
         | 
| 127 | 
            +
                    # available, waiting up to +timeout+ seconds for an element to
         | 
| 128 | 
            +
                    # become available.
         | 
| 129 | 
            +
                    #
         | 
| 130 | 
            +
                    # Raises:
         | 
| 131 | 
            +
                    # - ConnectionTimeoutError if +timeout+ is given and no element
         | 
| 132 | 
            +
                    # becomes available after +timeout+ seconds,
         | 
| 133 | 
            +
                    def poll(timeout = nil)
         | 
| 134 | 
            +
                      synchronize do
         | 
| 135 | 
            +
                        if timeout
         | 
| 136 | 
            +
                          no_wait_poll || wait_poll(timeout)
         | 
| 137 | 
            +
                        else
         | 
| 138 | 
            +
                          no_wait_poll
         | 
| 139 | 
            +
                        end
         | 
| 140 | 
            +
                      end
         | 
| 141 | 
            +
                    end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                    private
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                    def synchronize(&block)
         | 
| 146 | 
            +
                      @lock.synchronize(&block)
         | 
| 147 | 
            +
                    end
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                    # Test if the queue currently contains any elements.
         | 
| 150 | 
            +
                    def any?
         | 
| 151 | 
            +
                      !@queue.empty?
         | 
| 152 | 
            +
                    end
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                    # A thread can remove an element from the queue without
         | 
| 155 | 
            +
                    # waiting if an only if the number of currently available
         | 
| 156 | 
            +
                    # connections is strictly greater than the number of waiting
         | 
| 157 | 
            +
                    # threads.
         | 
| 158 | 
            +
                    def can_remove_no_wait?
         | 
| 159 | 
            +
                      @queue.size > @num_waiting
         | 
| 160 | 
            +
                    end
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                    # Removes and returns the head of the queue if possible, or nil.
         | 
| 163 | 
            +
                    def remove
         | 
| 164 | 
            +
                      @queue.shift
         | 
| 165 | 
            +
                    end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                    # Remove and return the head the queue if the number of
         | 
| 168 | 
            +
                    # available elements is strictly greater than the number of
         | 
| 169 | 
            +
                    # threads currently waiting.  Otherwise, return nil.
         | 
| 170 | 
            +
                    def no_wait_poll
         | 
| 171 | 
            +
                      remove if can_remove_no_wait?
         | 
| 172 | 
            +
                    end
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                    # Waits on the queue up to +timeout+ seconds, then removes and
         | 
| 175 | 
            +
                    # returns the head of the queue.
         | 
| 176 | 
            +
                    def wait_poll(timeout)
         | 
| 177 | 
            +
                      @num_waiting += 1
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                      t0 = Time.now
         | 
| 180 | 
            +
                      elapsed = 0
         | 
| 181 | 
            +
                      loop do
         | 
| 182 | 
            +
                        @cond.wait(timeout - elapsed)
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                        return remove if any?
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                        elapsed = Time.now - t0
         | 
| 187 | 
            +
                        if elapsed >= timeout
         | 
| 188 | 
            +
                          msg = 'could not obtain a database connection within %0.3f seconds (waited %0.3f seconds)' %
         | 
| 189 | 
            +
                            [timeout, elapsed]
         | 
| 190 | 
            +
                          raise ConnectionTimeoutError, msg
         | 
| 191 | 
            +
                        end
         | 
| 192 | 
            +
                      end
         | 
| 193 | 
            +
                    ensure
         | 
| 194 | 
            +
                      @num_waiting -= 1
         | 
| 195 | 
            +
                    end
         | 
| 196 | 
            +
                  end
         | 
| 197 | 
            +
             | 
| 198 | 
            +
                  # Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
         | 
| 199 | 
            +
                  # A reaper instantiated with a nil frequency will never reap the
         | 
| 200 | 
            +
                  # connection pool.
         | 
| 201 | 
            +
                  #
         | 
| 202 | 
            +
                  # Configure the frequency by setting "reaping_frequency" in your
         | 
| 203 | 
            +
                  # database yaml file.
         | 
| 204 | 
            +
                  class Reaper
         | 
| 205 | 
            +
                    attr_reader :pool, :frequency
         | 
| 206 | 
            +
             | 
| 207 | 
            +
                    def initialize(pool, frequency)
         | 
| 208 | 
            +
                      @pool      = pool
         | 
| 209 | 
            +
                      @frequency = frequency
         | 
| 210 | 
            +
                    end
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                    def run
         | 
| 213 | 
            +
                      return unless frequency
         | 
| 214 | 
            +
                      Thread.new(frequency, pool) { |t, p|
         | 
| 215 | 
            +
                        while true
         | 
| 216 | 
            +
                          sleep t
         | 
| 217 | 
            +
                          p.reap
         | 
| 218 | 
            +
                        end
         | 
| 219 | 
            +
                      }
         | 
| 220 | 
            +
                    end
         | 
| 221 | 
            +
                  end
         | 
| 222 | 
            +
             | 
| 63 223 | 
             
                  include MonitorMixin
         | 
| 64 224 |  | 
| 65 | 
            -
                  attr_accessor :automatic_reconnect
         | 
| 66 | 
            -
                  attr_reader :spec, :connections
         | 
| 225 | 
            +
                  attr_accessor :automatic_reconnect, :checkout_timeout, :dead_connection_timeout
         | 
| 226 | 
            +
                  attr_reader :spec, :connections, :size, :reaper
         | 
| 67 227 |  | 
| 68 228 | 
             
                  # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
         | 
| 69 229 | 
             
                  # object which describes database connection information (e.g. adapter,
         | 
| @@ -76,20 +236,29 @@ module ActiveRecord | |
| 76 236 |  | 
| 77 237 | 
             
                    @spec = spec
         | 
| 78 238 |  | 
| 79 | 
            -
                     | 
| 80 | 
            -
                    @ | 
| 81 | 
            -
             | 
| 82 | 
            -
                    @ | 
| 83 | 
            -
                    # 'wait_timeout', the backward-compatible key, conflicts with spec key 
         | 
| 84 | 
            -
                    # used by mysql2 for something entirely different, checkout_timeout
         | 
| 85 | 
            -
                    # preferred to avoid conflict and allow independent values. 
         | 
| 86 | 
            -
                    @timeout = spec.config[:checkout_timeout] || spec.config[:wait_timeout] || 5
         | 
| 239 | 
            +
                    @checkout_timeout = spec.config[:checkout_timeout] || 5
         | 
| 240 | 
            +
                    @dead_connection_timeout = spec.config[:dead_connection_timeout] || 5
         | 
| 241 | 
            +
                    @reaper  = Reaper.new self, spec.config[:reaping_frequency]
         | 
| 242 | 
            +
                    @reaper.run
         | 
| 87 243 |  | 
| 88 244 | 
             
                    # default max pool size to 5
         | 
| 89 245 | 
             
                    @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
         | 
| 90 246 |  | 
| 247 | 
            +
                    # The cache of reserved connections mapped to threads
         | 
| 248 | 
            +
                    @reserved_connections = ThreadSafe::Cache.new(:initial_capacity => @size)
         | 
| 249 | 
            +
             | 
| 91 250 | 
             
                    @connections         = []
         | 
| 92 251 | 
             
                    @automatic_reconnect = true
         | 
| 252 | 
            +
             | 
| 253 | 
            +
                    @available = Queue.new self
         | 
| 254 | 
            +
                  end
         | 
| 255 | 
            +
             | 
| 256 | 
            +
                  # Hack for tests to be able to add connections.  Do not call outside of tests
         | 
| 257 | 
            +
                  def insert_connection_for_test!(c) #:nodoc:
         | 
| 258 | 
            +
                    synchronize do
         | 
| 259 | 
            +
                      @connections << c
         | 
| 260 | 
            +
                      @available.add c
         | 
| 261 | 
            +
                    end
         | 
| 93 262 | 
             
                  end
         | 
| 94 263 |  | 
| 95 264 | 
             
                  # Retrieve the connection associated with the current thread, or call
         | 
| @@ -98,7 +267,9 @@ module ActiveRecord | |
| 98 267 | 
             
                  # #connection can be called any number of times; the connection is
         | 
| 99 268 | 
             
                  # held in a hash keyed by the thread id.
         | 
| 100 269 | 
             
                  def connection
         | 
| 101 | 
            -
                     | 
| 270 | 
            +
                    # this is correctly done double-checked locking
         | 
| 271 | 
            +
                    # (ThreadSafe::Cache's lookups have volatile semantics)
         | 
| 272 | 
            +
                    @reserved_connections[current_connection_id] || synchronize do
         | 
| 102 273 | 
             
                      @reserved_connections[current_connection_id] ||= checkout
         | 
| 103 274 | 
             
                    end
         | 
| 104 275 | 
             
                  end
         | 
| @@ -116,8 +287,10 @@ module ActiveRecord | |
| 116 287 | 
             
                  # #release_connection releases the connection-thread association
         | 
| 117 288 | 
             
                  # and returns the connection to the pool.
         | 
| 118 289 | 
             
                  def release_connection(with_id = current_connection_id)
         | 
| 119 | 
            -
                     | 
| 120 | 
            -
             | 
| 290 | 
            +
                    synchronize do
         | 
| 291 | 
            +
                      conn = @reserved_connections.delete(with_id)
         | 
| 292 | 
            +
                      checkin conn if conn
         | 
| 293 | 
            +
                    end
         | 
| 121 294 | 
             
                  end
         | 
| 122 295 |  | 
| 123 296 | 
             
                  # If a connection already exists yield it to the block. If no connection
         | 
| @@ -139,19 +312,20 @@ module ActiveRecord | |
| 139 312 | 
             
                  # Disconnects all connections in the pool, and clears the pool.
         | 
| 140 313 | 
             
                  def disconnect!
         | 
| 141 314 | 
             
                    synchronize do
         | 
| 142 | 
            -
                      @reserved_connections | 
| 315 | 
            +
                      @reserved_connections.clear
         | 
| 143 316 | 
             
                      @connections.each do |conn|
         | 
| 144 317 | 
             
                        checkin conn
         | 
| 145 318 | 
             
                        conn.disconnect!
         | 
| 146 319 | 
             
                      end
         | 
| 147 320 | 
             
                      @connections = []
         | 
| 321 | 
            +
                      @available.clear
         | 
| 148 322 | 
             
                    end
         | 
| 149 323 | 
             
                  end
         | 
| 150 324 |  | 
| 151 325 | 
             
                  # Clears the cache which maps classes.
         | 
| 152 326 | 
             
                  def clear_reloadable_connections!
         | 
| 153 327 | 
             
                    synchronize do
         | 
| 154 | 
            -
                      @reserved_connections | 
| 328 | 
            +
                      @reserved_connections.clear
         | 
| 155 329 | 
             
                      @connections.each do |conn|
         | 
| 156 330 | 
             
                        checkin conn
         | 
| 157 331 | 
             
                        conn.disconnect! if conn.requires_reloading?
         | 
| @@ -159,121 +333,37 @@ module ActiveRecord | |
| 159 333 | 
             
                      @connections.delete_if do |conn|
         | 
| 160 334 | 
             
                        conn.requires_reloading?
         | 
| 161 335 | 
             
                      end
         | 
| 162 | 
            -
             | 
| 163 | 
            -
             | 
| 164 | 
            -
             | 
| 165 | 
            -
                  # Verify active connections and remove and disconnect connections
         | 
| 166 | 
            -
                  # associated with stale threads.
         | 
| 167 | 
            -
                  def verify_active_connections! #:nodoc:
         | 
| 168 | 
            -
                    synchronize do
         | 
| 169 | 
            -
                      clear_stale_cached_connections!
         | 
| 170 | 
            -
                      @connections.each do |connection|
         | 
| 171 | 
            -
                        connection.verify!
         | 
| 336 | 
            +
                      @available.clear
         | 
| 337 | 
            +
                      @connections.each do |conn|
         | 
| 338 | 
            +
                        @available.add conn
         | 
| 172 339 | 
             
                      end
         | 
| 173 340 | 
             
                    end
         | 
| 174 341 | 
             
                  end
         | 
| 175 342 |  | 
| 176 | 
            -
                  def  | 
| 177 | 
            -
                     | 
| 178 | 
            -
                      c.schema_cache.columns
         | 
| 179 | 
            -
                    end
         | 
| 180 | 
            -
                  end
         | 
| 181 | 
            -
                  deprecate :columns
         | 
| 182 | 
            -
             | 
| 183 | 
            -
                  def columns_hash
         | 
| 184 | 
            -
                    with_connection do |c|
         | 
| 185 | 
            -
                      c.schema_cache.columns_hash
         | 
| 186 | 
            -
                    end
         | 
| 187 | 
            -
                  end
         | 
| 188 | 
            -
                  deprecate :columns_hash
         | 
| 189 | 
            -
             | 
| 190 | 
            -
                  def primary_keys
         | 
| 191 | 
            -
                    with_connection do |c|
         | 
| 192 | 
            -
                      c.schema_cache.primary_keys
         | 
| 193 | 
            -
                    end
         | 
| 194 | 
            -
                  end
         | 
| 195 | 
            -
                  deprecate :primary_keys
         | 
| 196 | 
            -
             | 
| 197 | 
            -
                  def clear_cache!
         | 
| 198 | 
            -
                    with_connection do |c|
         | 
| 199 | 
            -
                      c.schema_cache.clear!
         | 
| 200 | 
            -
                    end
         | 
| 201 | 
            -
                  end
         | 
| 202 | 
            -
                  deprecate :clear_cache!
         | 
| 203 | 
            -
             | 
| 204 | 
            -
                  # Return any checked-out connections back to the pool by threads that
         | 
| 205 | 
            -
                  # are no longer alive.
         | 
| 206 | 
            -
                  def clear_stale_cached_connections!
         | 
| 207 | 
            -
                    keys = @reserved_connections.keys - Thread.list.find_all { |t|
         | 
| 208 | 
            -
                      t.alive?
         | 
| 209 | 
            -
                    }.map { |thread| thread.object_id }
         | 
| 210 | 
            -
                    keys.each do |key|
         | 
| 211 | 
            -
                      conn = @reserved_connections[key]
         | 
| 212 | 
            -
                      ActiveSupport::Deprecation.warn(<<-eowarn) if conn.in_use?
         | 
| 213 | 
            -
            Database connections will not be closed automatically, please close your
         | 
| 214 | 
            -
            database connection at the end of the thread by calling `close` on your
         | 
| 215 | 
            -
            connection.  For example: ActiveRecord::Base.connection.close
         | 
| 216 | 
            -
                      eowarn
         | 
| 217 | 
            -
                      checkin conn
         | 
| 218 | 
            -
                      @reserved_connections.delete(key)
         | 
| 219 | 
            -
                    end
         | 
| 343 | 
            +
                  def clear_stale_cached_connections! # :nodoc:
         | 
| 344 | 
            +
                    reap
         | 
| 220 345 | 
             
                  end
         | 
| 346 | 
            +
                  deprecate :clear_stale_cached_connections! => "Please use #reap instead"
         | 
| 221 347 |  | 
| 222 348 | 
             
                  # Check-out a database connection from the pool, indicating that you want
         | 
| 223 349 | 
             
                  # to use it. You should call #checkin when you no longer need this.
         | 
| 224 350 | 
             
                  #
         | 
| 225 | 
            -
                  # This is done by either returning  | 
| 226 | 
            -
                  # a new connection | 
| 227 | 
            -
                  # | 
| 228 | 
            -
                  #  | 
| 229 | 
            -
                  #  | 
| 230 | 
            -
                  #  | 
| 231 | 
            -
                  # exception will be raised.
         | 
| 351 | 
            +
                  # This is done by either returning and leasing existing connection, or by
         | 
| 352 | 
            +
                  # creating a new connection and leasing it.
         | 
| 353 | 
            +
                  #
         | 
| 354 | 
            +
                  # If all connections are leased and the pool is at capacity (meaning the
         | 
| 355 | 
            +
                  # number of currently leased connections is greater than or equal to the
         | 
| 356 | 
            +
                  # size limit set), an ActiveRecord::ConnectionTimeoutError exception will be raised.
         | 
| 232 357 | 
             
                  #
         | 
| 233 358 | 
             
                  # Returns: an AbstractAdapter object.
         | 
| 234 359 | 
             
                  #
         | 
| 235 360 | 
             
                  # Raises:
         | 
| 236 | 
            -
                  # - ConnectionTimeoutError: no connection can be obtained from the pool
         | 
| 237 | 
            -
                  #   within the timeout period.
         | 
| 361 | 
            +
                  # - ConnectionTimeoutError: no connection can be obtained from the pool.
         | 
| 238 362 | 
             
                  def checkout
         | 
| 239 363 | 
             
                    synchronize do
         | 
| 240 | 
            -
                       | 
| 241 | 
            -
             | 
| 242 | 
            -
                       | 
| 243 | 
            -
                        conn = @connections.find { |c| c.lease }
         | 
| 244 | 
            -
             | 
| 245 | 
            -
                        unless conn
         | 
| 246 | 
            -
                          if @connections.size < @size
         | 
| 247 | 
            -
                            conn = checkout_new_connection
         | 
| 248 | 
            -
                            conn.lease
         | 
| 249 | 
            -
                          end
         | 
| 250 | 
            -
                        end
         | 
| 251 | 
            -
             | 
| 252 | 
            -
                        if conn
         | 
| 253 | 
            -
                          checkout_and_verify conn
         | 
| 254 | 
            -
                          return conn
         | 
| 255 | 
            -
                        end
         | 
| 256 | 
            -
             | 
| 257 | 
            -
                        if waited_time >= @timeout
         | 
| 258 | 
            -
                          raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout} (waited #{waited_time} seconds). The max pool size is currently #{@size}; consider increasing it."
         | 
| 259 | 
            -
                        end
         | 
| 260 | 
            -
             | 
| 261 | 
            -
                        # Sometimes our wait can end because a connection is available,
         | 
| 262 | 
            -
                        # but another thread can snatch it up first. If timeout hasn't
         | 
| 263 | 
            -
                        # passed but no connection is avail, looks like that happened --
         | 
| 264 | 
            -
                        # loop and wait again, for the time remaining on our timeout. 
         | 
| 265 | 
            -
                        before_wait = Time.now
         | 
| 266 | 
            -
                        @queue.wait( [@timeout - waited_time, 0].max )
         | 
| 267 | 
            -
                        waited_time += (Time.now - before_wait)
         | 
| 268 | 
            -
             | 
| 269 | 
            -
                        # Will go away in Rails 4, when we don't clean up
         | 
| 270 | 
            -
                        # after leaked connections automatically anymore. Right now, clean
         | 
| 271 | 
            -
                        # up after we've returned from a 'wait' if it looks like it's
         | 
| 272 | 
            -
                        # needed, then loop and try again. 
         | 
| 273 | 
            -
                        if(active_connections.size >= @connections.size)
         | 
| 274 | 
            -
                          clear_stale_cached_connections!
         | 
| 275 | 
            -
                        end
         | 
| 276 | 
            -
                      end
         | 
| 364 | 
            +
                      conn = acquire_connection
         | 
| 365 | 
            +
                      conn.lease
         | 
| 366 | 
            +
                      checkout_and_verify(conn)
         | 
| 277 367 | 
             
                    end
         | 
| 278 368 | 
             
                  end
         | 
| 279 369 |  | 
| @@ -286,37 +376,78 @@ connection.  For example: ActiveRecord::Base.connection.close | |
| 286 376 | 
             
                    synchronize do
         | 
| 287 377 | 
             
                      conn.run_callbacks :checkin do
         | 
| 288 378 | 
             
                        conn.expire
         | 
| 289 | 
            -
                        @queue.signal
         | 
| 290 379 | 
             
                      end
         | 
| 291 380 |  | 
| 292 381 | 
             
                      release conn
         | 
| 382 | 
            +
             | 
| 383 | 
            +
                      @available.add conn
         | 
| 293 384 | 
             
                    end
         | 
| 294 385 | 
             
                  end
         | 
| 295 386 |  | 
| 296 | 
            -
                   | 
| 297 | 
            -
             | 
| 298 | 
            -
                  def  | 
| 387 | 
            +
                  # Remove a connection from the connection pool.  The connection will
         | 
| 388 | 
            +
                  # remain open and active but will no longer be managed by this pool.
         | 
| 389 | 
            +
                  def remove(conn)
         | 
| 299 390 | 
             
                    synchronize do
         | 
| 300 | 
            -
                       | 
| 391 | 
            +
                      @connections.delete conn
         | 
| 392 | 
            +
                      @available.delete conn
         | 
| 301 393 |  | 
| 302 | 
            -
                       | 
| 303 | 
            -
             | 
| 304 | 
            -
                       | 
| 305 | 
            -
             | 
| 306 | 
            -
             | 
| 307 | 
            -
             | 
| 394 | 
            +
                      # FIXME: we might want to store the key on the connection so that removing
         | 
| 395 | 
            +
                      # from the reserved hash will be a little easier.
         | 
| 396 | 
            +
                      release conn
         | 
| 397 | 
            +
             | 
| 398 | 
            +
                      @available.add checkout_new_connection if @available.any_waiting?
         | 
| 399 | 
            +
                    end
         | 
| 400 | 
            +
                  end
         | 
| 401 | 
            +
             | 
| 402 | 
            +
                  # Removes dead connections from the pool.  A dead connection can occur
         | 
| 403 | 
            +
                  # if a programmer forgets to close a connection at the end of a thread
         | 
| 404 | 
            +
                  # or a thread dies unexpectedly.
         | 
| 405 | 
            +
                  def reap
         | 
| 406 | 
            +
                    synchronize do
         | 
| 407 | 
            +
                      stale = Time.now - @dead_connection_timeout
         | 
| 408 | 
            +
                      connections.dup.each do |conn|
         | 
| 409 | 
            +
                        remove conn if conn.in_use? && stale > conn.last_use && !conn.active?
         | 
| 308 410 | 
             
                      end
         | 
| 411 | 
            +
                    end
         | 
| 412 | 
            +
                  end
         | 
| 413 | 
            +
             | 
| 414 | 
            +
                  private
         | 
| 309 415 |  | 
| 310 | 
            -
             | 
| 416 | 
            +
                  # Acquire a connection by one of 1) immediately removing one
         | 
| 417 | 
            +
                  # from the queue of available connections, 2) creating a new
         | 
| 418 | 
            +
                  # connection if the pool is not at capacity, 3) waiting on the
         | 
| 419 | 
            +
                  # queue for a connection to become available.
         | 
| 420 | 
            +
                  #
         | 
| 421 | 
            +
                  # Raises:
         | 
| 422 | 
            +
                  # - ConnectionTimeoutError if a connection could not be acquired
         | 
| 423 | 
            +
                  def acquire_connection
         | 
| 424 | 
            +
                    if conn = @available.poll
         | 
| 425 | 
            +
                      conn
         | 
| 426 | 
            +
                    elsif @connections.size < @size
         | 
| 427 | 
            +
                      checkout_new_connection
         | 
| 428 | 
            +
                    else
         | 
| 429 | 
            +
                      @available.poll(@checkout_timeout)
         | 
| 311 430 | 
             
                    end
         | 
| 312 431 | 
             
                  end
         | 
| 313 432 |  | 
| 433 | 
            +
                  def release(conn)
         | 
| 434 | 
            +
                    thread_id = if @reserved_connections[current_connection_id] == conn
         | 
| 435 | 
            +
                      current_connection_id
         | 
| 436 | 
            +
                    else
         | 
| 437 | 
            +
                      @reserved_connections.keys.find { |k|
         | 
| 438 | 
            +
                        @reserved_connections[k] == conn
         | 
| 439 | 
            +
                      }
         | 
| 440 | 
            +
                    end
         | 
| 441 | 
            +
             | 
| 442 | 
            +
                    @reserved_connections.delete thread_id if thread_id
         | 
| 443 | 
            +
                  end
         | 
| 444 | 
            +
             | 
| 314 445 | 
             
                  def new_connection
         | 
| 315 | 
            -
                     | 
| 446 | 
            +
                    Base.send(spec.adapter_method, spec.config)
         | 
| 316 447 | 
             
                  end
         | 
| 317 448 |  | 
| 318 449 | 
             
                  def current_connection_id #:nodoc:
         | 
| 319 | 
            -
                     | 
| 450 | 
            +
                    Base.connection_id ||= Thread.current.object_id
         | 
| 320 451 | 
             
                  end
         | 
| 321 452 |  | 
| 322 453 | 
             
                  def checkout_new_connection
         | 
| @@ -334,10 +465,6 @@ connection.  For example: ActiveRecord::Base.connection.close | |
| 334 465 | 
             
                    end
         | 
| 335 466 | 
             
                    c
         | 
| 336 467 | 
             
                  end
         | 
| 337 | 
            -
             | 
| 338 | 
            -
                  def active_connections
         | 
| 339 | 
            -
                    @connections.find_all { |c| c.in_use? }
         | 
| 340 | 
            -
                  end
         | 
| 341 468 | 
             
                end
         | 
| 342 469 |  | 
| 343 470 | 
             
                # ConnectionHandler is a collection of ConnectionPool objects. It is used
         | 
| @@ -362,43 +489,58 @@ connection.  For example: ActiveRecord::Base.connection.close | |
| 362 489 | 
             
                #
         | 
| 363 490 | 
             
                # Normally there is only a single ConnectionHandler instance, accessible via
         | 
| 364 491 | 
             
                # ActiveRecord::Base.connection_handler. Active Record models use this to
         | 
| 365 | 
            -
                # determine  | 
| 492 | 
            +
                # determine the connection pool that they should use.
         | 
| 366 493 | 
             
                class ConnectionHandler
         | 
| 367 | 
            -
                   | 
| 494 | 
            +
                  def initialize
         | 
| 495 | 
            +
                    # These caches are keyed by klass.name, NOT klass. Keying them by klass
         | 
| 496 | 
            +
                    # alone would lead to memory leaks in development mode as all previous
         | 
| 497 | 
            +
                    # instances of the class would stay in memory.
         | 
| 498 | 
            +
                    @owner_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
         | 
| 499 | 
            +
                      h[k] = ThreadSafe::Cache.new(:initial_capacity => 2)
         | 
| 500 | 
            +
                    end
         | 
| 501 | 
            +
                    @class_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
         | 
| 502 | 
            +
                      h[k] = ThreadSafe::Cache.new
         | 
| 503 | 
            +
                    end
         | 
| 504 | 
            +
                  end
         | 
| 505 | 
            +
             | 
| 506 | 
            +
                  def connection_pool_list
         | 
| 507 | 
            +
                    owner_to_pool.values.compact
         | 
| 508 | 
            +
                  end
         | 
| 368 509 |  | 
| 369 | 
            -
                  def  | 
| 370 | 
            -
                     | 
| 371 | 
            -
             | 
| 510 | 
            +
                  def connection_pools
         | 
| 511 | 
            +
                    ActiveSupport::Deprecation.warn(
         | 
| 512 | 
            +
                      "In the next release, this will return the same as #connection_pool_list. " \
         | 
| 513 | 
            +
                      "(An array of pools, rather than a hash mapping specs to pools.)"
         | 
| 514 | 
            +
                    )
         | 
| 515 | 
            +
                    Hash[connection_pool_list.map { |pool| [pool.spec, pool] }]
         | 
| 372 516 | 
             
                  end
         | 
| 373 517 |  | 
| 374 | 
            -
                  def establish_connection( | 
| 375 | 
            -
                    @ | 
| 376 | 
            -
                     | 
| 518 | 
            +
                  def establish_connection(owner, spec)
         | 
| 519 | 
            +
                    @class_to_pool.clear
         | 
| 520 | 
            +
                    raise RuntimeError, "Anonymous class is not allowed." unless owner.name
         | 
| 521 | 
            +
                    owner_to_pool[owner.name] = ConnectionAdapters::ConnectionPool.new(spec)
         | 
| 377 522 | 
             
                  end
         | 
| 378 523 |  | 
| 379 524 | 
             
                  # Returns true if there are any active connections among the connection
         | 
| 380 525 | 
             
                  # pools that the ConnectionHandler is managing.
         | 
| 381 526 | 
             
                  def active_connections?
         | 
| 382 | 
            -
                     | 
| 527 | 
            +
                    connection_pool_list.any?(&:active_connection?)
         | 
| 383 528 | 
             
                  end
         | 
| 384 529 |  | 
| 385 | 
            -
                  # Returns any connections in use by the current thread back to the pool | 
| 530 | 
            +
                  # Returns any connections in use by the current thread back to the pool,
         | 
| 531 | 
            +
                  # and also returns connections to the pool cached by threads that are no
         | 
| 532 | 
            +
                  # longer alive.
         | 
| 386 533 | 
             
                  def clear_active_connections!
         | 
| 387 | 
            -
                     | 
| 534 | 
            +
                    connection_pool_list.each(&:release_connection)
         | 
| 388 535 | 
             
                  end
         | 
| 389 536 |  | 
| 390 537 | 
             
                  # Clears the cache which maps classes.
         | 
| 391 538 | 
             
                  def clear_reloadable_connections!
         | 
| 392 | 
            -
                     | 
| 539 | 
            +
                    connection_pool_list.each(&:clear_reloadable_connections!)
         | 
| 393 540 | 
             
                  end
         | 
| 394 541 |  | 
| 395 542 | 
             
                  def clear_all_connections!
         | 
| 396 | 
            -
                     | 
| 397 | 
            -
                  end
         | 
| 398 | 
            -
             | 
| 399 | 
            -
                  # Verify active connections.
         | 
| 400 | 
            -
                  def verify_active_connections! #:nodoc:
         | 
| 401 | 
            -
                    @connection_pools.each_value {|pool| pool.verify_active_connections! }
         | 
| 543 | 
            +
                    connection_pool_list.each(&:disconnect!)
         | 
| 402 544 | 
             
                  end
         | 
| 403 545 |  | 
| 404 546 | 
             
                  # Locate the connection of the nearest super class. This can be an
         | 
| @@ -421,54 +563,65 @@ connection.  For example: ActiveRecord::Base.connection.close | |
| 421 563 | 
             
                  # connection and the defined connection (if they exist). The result
         | 
| 422 564 | 
             
                  # can be used as an argument for establish_connection, for easily
         | 
| 423 565 | 
             
                  # re-establishing the connection.
         | 
| 424 | 
            -
                  def remove_connection( | 
| 425 | 
            -
                    pool =  | 
| 426 | 
            -
             | 
| 427 | 
            -
             | 
| 428 | 
            -
             | 
| 429 | 
            -
             | 
| 430 | 
            -
                     | 
| 431 | 
            -
                    pool.spec.config
         | 
| 566 | 
            +
                  def remove_connection(owner)
         | 
| 567 | 
            +
                    if pool = owner_to_pool.delete(owner.name)
         | 
| 568 | 
            +
                      @class_to_pool.clear
         | 
| 569 | 
            +
                      pool.automatic_reconnect = false
         | 
| 570 | 
            +
                      pool.disconnect!
         | 
| 571 | 
            +
                      pool.spec.config
         | 
| 572 | 
            +
                    end
         | 
| 432 573 | 
             
                  end
         | 
| 433 574 |  | 
| 575 | 
            +
                  # Retrieving the connection pool happens a lot so we cache it in @class_to_pool.
         | 
| 576 | 
            +
                  # This makes retrieving the connection pool O(1) once the process is warm.
         | 
| 577 | 
            +
                  # When a connection is established or removed, we invalidate the cache.
         | 
| 578 | 
            +
                  #
         | 
| 579 | 
            +
                  # Ideally we would use #fetch here, as class_to_pool[klass] may sometimes be nil.
         | 
| 580 | 
            +
                  # However, benchmarking (https://gist.github.com/jonleighton/3552829) showed that
         | 
| 581 | 
            +
                  # #fetch is significantly slower than #[]. So in the nil case, no caching will
         | 
| 582 | 
            +
                  # take place, but that's ok since the nil case is not the common one that we wish
         | 
| 583 | 
            +
                  # to optimise for.
         | 
| 434 584 | 
             
                  def retrieve_connection_pool(klass)
         | 
| 435 | 
            -
                     | 
| 436 | 
            -
             | 
| 437 | 
            -
             | 
| 438 | 
            -
             | 
| 439 | 
            -
             | 
| 440 | 
            -
                end
         | 
| 441 | 
            -
             | 
| 442 | 
            -
                class ConnectionManagement
         | 
| 443 | 
            -
                  class Proxy # :nodoc:
         | 
| 444 | 
            -
                    attr_reader :body, :testing
         | 
| 585 | 
            +
                    class_to_pool[klass.name] ||= begin
         | 
| 586 | 
            +
                      until pool = pool_for(klass)
         | 
| 587 | 
            +
                        klass = klass.superclass
         | 
| 588 | 
            +
                        break unless klass <= Base
         | 
| 589 | 
            +
                      end
         | 
| 445 590 |  | 
| 446 | 
            -
             | 
| 447 | 
            -
                      @body    = body
         | 
| 448 | 
            -
                      @testing = testing
         | 
| 591 | 
            +
                      class_to_pool[klass.name] = pool
         | 
| 449 592 | 
             
                    end
         | 
| 593 | 
            +
                  end
         | 
| 450 594 |  | 
| 451 | 
            -
             | 
| 452 | 
            -
                      @body.send(method_sym, *arguments, &block)
         | 
| 453 | 
            -
                    end
         | 
| 595 | 
            +
                  private
         | 
| 454 596 |  | 
| 455 | 
            -
             | 
| 456 | 
            -
             | 
| 457 | 
            -
             | 
| 597 | 
            +
                  def owner_to_pool
         | 
| 598 | 
            +
                    @owner_to_pool[Process.pid]
         | 
| 599 | 
            +
                  end
         | 
| 458 600 |  | 
| 459 | 
            -
             | 
| 460 | 
            -
             | 
| 461 | 
            -
             | 
| 601 | 
            +
                  def class_to_pool
         | 
| 602 | 
            +
                    @class_to_pool[Process.pid]
         | 
| 603 | 
            +
                  end
         | 
| 462 604 |  | 
| 463 | 
            -
             | 
| 464 | 
            -
             | 
| 605 | 
            +
                  def pool_for(owner)
         | 
| 606 | 
            +
                    owner_to_pool.fetch(owner.name) {
         | 
| 607 | 
            +
                      if ancestor_pool = pool_from_any_process_for(owner)
         | 
| 608 | 
            +
                        # A connection was established in an ancestor process that must have
         | 
| 609 | 
            +
                        # subsequently forked. We can't reuse the connection, but we can copy
         | 
| 610 | 
            +
                        # the specification and establish a new connection with it.
         | 
| 611 | 
            +
                        establish_connection owner, ancestor_pool.spec
         | 
| 612 | 
            +
                      else
         | 
| 613 | 
            +
                        owner_to_pool[owner.name] = nil
         | 
| 614 | 
            +
                      end
         | 
| 615 | 
            +
                    }
         | 
| 616 | 
            +
                  end
         | 
| 465 617 |  | 
| 466 | 
            -
             | 
| 467 | 
            -
             | 
| 468 | 
            -
             | 
| 469 | 
            -
                    end
         | 
| 618 | 
            +
                  def pool_from_any_process_for(owner)
         | 
| 619 | 
            +
                    owner_to_pool = @owner_to_pool.values.find { |v| v[owner.name] }
         | 
| 620 | 
            +
                    owner_to_pool && owner_to_pool[owner.name]
         | 
| 470 621 | 
             
                  end
         | 
| 622 | 
            +
                end
         | 
| 471 623 |  | 
| 624 | 
            +
                class ConnectionManagement
         | 
| 472 625 | 
             
                  def initialize(app)
         | 
| 473 626 | 
             
                    @app = app
         | 
| 474 627 | 
             
                  end
         | 
| @@ -476,9 +629,12 @@ connection.  For example: ActiveRecord::Base.connection.close | |
| 476 629 | 
             
                  def call(env)
         | 
| 477 630 | 
             
                    testing = env.key?('rack.test')
         | 
| 478 631 |  | 
| 479 | 
            -
                     | 
| 632 | 
            +
                    response = @app.call(env)
         | 
| 633 | 
            +
                    response[2] = ::Rack::BodyProxy.new(response[2]) do
         | 
| 634 | 
            +
                      ActiveRecord::Base.clear_active_connections! unless testing
         | 
| 635 | 
            +
                    end
         | 
| 480 636 |  | 
| 481 | 
            -
                     | 
| 637 | 
            +
                    response
         | 
| 482 638 | 
             
                  rescue
         | 
| 483 639 | 
             
                    ActiveRecord::Base.clear_active_connections! unless testing
         | 
| 484 640 | 
             
                    raise
         |