forked from bdurand/seamless_database_pool
/
seamless_database_pool.rb
116 lines (103 loc) · 5.12 KB
/
seamless_database_pool.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'connect_timeout')
require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'connection_statistics')
require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'controller_filter')
require File.join(File.dirname(__FILE__), 'active_record', 'connection_adapters', 'seamless_database_pool_adapter')
$LOAD_PATH << File.dirname(__FILE__) unless $LOAD_PATH.include?(File.dirname(__FILE__))
# This module allows setting the read pool connection type. Generally you will use one of
#
# - use_random_read_connection
# - use_persistent_read_connection
# - use_master_connection
#
# Each of these methods can take an optional block. If they are called with a block, they
# will set the read connection type only within the block. Otherwise they will set the default
# read connection type. If none is ever called, the read connection type will be :master.
module SeamlessDatabasePool
READ_CONNECTION_METHODS = [:master, :persistent, :random]
class << self
# Call this method to use a random connection from the read pool for every select statement.
# This method is good if your replication is very fast. Otherwise there is a chance you could
# get inconsistent results from one request to the next. This can result in mysterious failures
# if your code selects a value in one statement and then uses in another statement. You can wind
# up trying to use a value from one server that hasn't been replicated to another one yet.
# This method is best if you have few processes which generate a lot of queries and you have
# fast replication.
def use_random_read_connection
if block_given?
set_read_only_connection_type(:random){yield}
else
Thread.current[:read_only_connection] = :random
end
end
# Call this method to pick a random connection from the read pool and use it for all subsequent
# select statements. This provides consistency from one select statement to the next. This
# method should always be called with a block otherwise you can end up with an imbalanced read
# pool. This method is best if you have lots of processes which have a relatively few select
# statements or a slow replication mechanism. Generally this is the best method to use for web
# applications.
def use_persistent_read_connection
if block_given?
set_read_only_connection_type(:persistent){yield}
else
Thread.current[:read_only_connection] = {}
end
end
# Call this method to use the master connection for all subsequent select statements. This
# method is most useful when you are doing lots of updates since it guarantees consistency
# if you do a select immediately after an update or insert.
#
# The master connection will also be used for selects inside any transaction blocks. It will
# also be used if you pass :readonly => false to any ActiveRecord.find method.
def use_master_connection
if block_given?
set_read_only_connection_type(:master){yield}
else
Thread.current[:read_only_connection] = :master
end
end
# Set the read only connection type to either :master, :random, or :persistent.
def set_read_only_connection_type (connection_type)
saved_connection = Thread.current[:read_only_connection]
retval = nil
begin
connection_type = {} if connection_type == :persistent
Thread.current[:read_only_connection] = connection_type
retval = yield if block_given?
ensure
Thread.current[:read_only_connection] = saved_connection
end
return retval
end
# Get the read only connection type currently in use. Will be one of :master, :random, or :persistent.
def read_only_connection_type (default = :master)
connection_type = Thread.current[:read_only_connection] || default
connection_type = :persistent if connection_type.kind_of?(Hash)
return connection_type
end
# Get a read only connection from a connection pool.
def read_only_connection (pool_connection)
return pool_connection.master_connection if pool_connection.using_master_connection?
connection_type = Thread.current[:read_only_connection]
if connection_type.kind_of?(Hash)
connection = connection_type[pool_connection]
unless connection
connection = pool_connection.random_read_connection
connection_type[pool_connection] = connection
end
return connection
elsif connection_type == :random
return pool_connection.random_read_connection
else
return pool_connection.master_connection
end
end
# This method is provided as a way to change the persistent connection when it fails and a new one is substituted.
def set_persistent_read_connection (pool_connection, read_connection)
connection_type = Thread.current[:read_only_connection]
connection_type[pool_connection] = read_connection if connection_type.kind_of?(Hash)
end
def clear_read_only_connection
Thread.current[:read_only_connection] = nil
end
end
end