Last active
July 19, 2017 05:10
-
-
Save cha55son/2486a6b10374f12387e2a4e9a1e194c4 to your computer and use it in GitHub Desktop.
Disable Rails 5 Connection Pool
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# config/application.rb | |
require_relative 'boot' | |
require 'rails/all' | |
# Require the gems listed in Gemfile, including any gems | |
# you've limited to :test, :development, or :production. | |
Bundler.require(*Rails.groups) | |
module MyApp | |
class Application < Rails::Application | |
# Settings in config/environments/* take precedence over those specified here. | |
# Application configuration should go into files in config/initializers | |
# -- all .rb files in that directory are automatically loaded. | |
end | |
end | |
require_relative "../lib/connection_pool" | |
ActiveRecord::ConnectionAdapters::ConnectionPool = ConnectionPool |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# config/initializers/db_setup.rb | |
Rails.application.config.after_initialize do | |
ActiveSupport.on_load(:active_record) do | |
ActiveRecord::Base.establish_connection( | |
adapter: <adapter type>, | |
data_source: <pooled_data_source>, | |
) | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# lib/connection_pool.rb | |
require 'thread' | |
require 'concurrent/map' | |
require 'monitor' | |
require_relative 'reaper' | |
require_relative 'queue' | |
# Following the contract supplied by ConnectionPool | |
# http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/ConnectionPool.html | |
class ConnectionPool | |
include MonitorMixin | |
include ActiveRecord::ConnectionAdapters::QueryCache::ConnectionPoolConfiguration | |
attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache | |
attr_reader :spec, :connections, :size, :reaper | |
def initialize(spec) | |
trace "ConnectionPool.initialize", 5 | |
super() | |
@spec = spec | |
@checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5 | |
@reaper = Reaper.new(self, (spec.config[:reaping_frequency] && spec.config[:reaping_frequency].to_f)) | |
@reaper.run | |
@size = (spec.config[:pool] && spec.config[:pool].to_i) || 5 | |
@thread_cached_conns = Concurrent::Map.new | |
@connections = [] | |
@automatic_reconnect = true | |
@now_connecting = 0 | |
@threads_blocking_new_connections = 0 | |
@available = Queue.new | |
@lock_thread = false | |
end | |
def lock_thread=(lock_thread) | |
trace "ConnectionPool.lock_thread" | |
if lock_thread | |
@lock_thread = Thread.current | |
else | |
@lock_thread = nil | |
end | |
end | |
def connection | |
trace "ConnectionPool.connection" | |
@thread_cached_conns[connection_cache_key(@lock_thread || Thread.current)] ||= checkout | |
end | |
def active_connection? | |
trace "ConnectionPool.active_connection?" | |
@thread_cached_conns[connection_cache_key(Thread.current)] | |
end | |
def release_connection(owner_thread = Thread.current) | |
trace "ConnectionPool.release_connection" | |
if conn = @thread_cached_conns.delete(connection_cache_key(owner_thread)) | |
checkin conn | |
end | |
end | |
def with_connection | |
trace "ConnectionPool.with_connection" | |
unless conn = @thread_cached_conns[connection_cache_key(Thread.current)] | |
conn = connection | |
fresh_connection = true | |
end | |
yield conn | |
ensure | |
release_connection if fresh_connection | |
end | |
def connected? | |
trace "ConnectionPool.connected?" | |
synchronize { @connections.any? } | |
end | |
def disconnect(raise_on_acquisition_timeout = true) | |
trace "ConnectionPool.disconnect" | |
synchronize do | |
@connections.each do |conn| | |
if conn.in_use? | |
conn.steal! | |
checkin conn | |
end | |
conn.disconnect! | |
end | |
@connections = [] | |
@available.clear | |
end | |
end | |
def disconnect! | |
trace "ConnectionPool.disconnect!" | |
disconnect(false) | |
end | |
def clear_reloadable_connections(raise_on_acquisition_timeout = true) | |
trace "ConnectionPool.clear_reloadable_connections" | |
synchronize do | |
@connections.each do |conn| | |
if conn.in_use? | |
conn.steal! | |
checkin conn | |
end | |
conn.disconnect! if conn.requires_reloading? | |
end | |
@connections.delete_if(&:requires_reloading?) | |
@available.clear | |
end | |
end | |
def clear_reloadable_connections! | |
trace "ConnectionPool.clear_reloadable_connections!" | |
clear_reloadable_connections(false) | |
end | |
def checkout(checkout_timeout = @checkout_timeout) | |
trace "ConnectionPool.checkout" | |
conn = checkout_new_connection | |
conn.lease | |
checkout_and_verify(conn) | |
end | |
def checkin(conn) | |
trace "ConnectionPool.checkin" | |
synchronize do | |
remove_connection_from_thread_cache conn | |
conn._run_checkin_callbacks do | |
conn.expire | |
end | |
@available.add conn | |
end | |
end | |
def remove(conn) | |
trace "ConnectionPool.remove" | |
synchronize do | |
remove_connection_from_thread_cache conn | |
@connections.delete conn | |
@available.delete conn | |
end | |
end | |
def reap | |
trace "ConnectionPool.reap" | |
end | |
def num_waiting_in_queue | |
trace "ConnectionPool.num_waiting_in_queue" | |
@available.num_waiting | |
end | |
def stat | |
trace "ConnectionPool.stat" | |
{} | |
end | |
private | |
def connection_cache_key(thread) | |
thread | |
end | |
def remove_connection_from_thread_cache(conn, owner_thread = conn.owner) | |
@thread_cached_conns.delete_pair(connection_cache_key(owner_thread), conn) | |
end | |
alias_method :release, :remove_connection_from_thread_cache | |
def checkout_new_connection | |
conn = new_connection | |
conn.pool = self | |
@connections << conn | |
conn | |
end | |
def new_connection | |
ActiveRecord::Base.send(spec.adapter_method, spec.config).tap do |conn| | |
conn.schema_cache = schema_cache.dup if schema_cache | |
end | |
end | |
def checkout_and_verify(c) | |
c._run_checkout_callbacks do | |
c.verify! | |
end | |
c | |
rescue | |
remove c | |
c.disconnect! | |
raise | |
end | |
def trace(msg, depth = 1) | |
puts msg | |
caller[1..depth].each do |line| | |
puts " \\---> #{line}" | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# lib/connection_poll/queue.rb | |
require 'monitor' | |
class ConnectionPool | |
class Queue | |
include MonitorMixin | |
def initialize(lock = Monitor.new) | |
@lock = lock | |
@cond = @lock.new_cond | |
@num_waiting = 0 | |
@queue = [] | |
end | |
# Returns the number of threads currently waiting on this | |
# queue. | |
def num_waiting | |
synchronize do | |
@num_waiting | |
end | |
end | |
# Add +element+ to the queue. Never blocks. | |
def add(element) | |
synchronize do | |
@queue.push element | |
@cond.signal | |
end | |
end | |
# If +element+ is in the queue, remove and return it, or +nil+. | |
def delete(element) | |
synchronize do | |
@queue.delete(element) | |
end | |
end | |
# Remove all elements from the queue. | |
def clear | |
synchronize do | |
@queue.clear | |
end | |
end | |
private | |
def synchronize(&block) | |
@lock.synchronize(&block) | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# lib/connection_pool/reaper.rb | |
class ConnectionPool | |
class Reaper | |
attr_reader :pool, :frequency | |
def initialize(pool, frequency) | |
@pool = pool; | |
@frequency = frequency; | |
end | |
def run; end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment