makara 0.3.5
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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +28 -0
- data/CHANGELOG.md +27 -0
- data/Gemfile +18 -0
- data/LICENSE.txt +22 -0
- data/README.md +278 -0
- data/Rakefile +9 -0
- data/gemfiles/ar30.gemfile +30 -0
- data/gemfiles/ar31.gemfile +29 -0
- data/gemfiles/ar32.gemfile +29 -0
- data/gemfiles/ar40.gemfile +15 -0
- data/gemfiles/ar41.gemfile +15 -0
- data/gemfiles/ar42.gemfile +15 -0
- data/lib/active_record/connection_adapters/jdbcmysql_makara_adapter.rb +25 -0
- data/lib/active_record/connection_adapters/jdbcpostgresql_makara_adapter.rb +25 -0
- data/lib/active_record/connection_adapters/makara_abstract_adapter.rb +209 -0
- data/lib/active_record/connection_adapters/makara_jdbcmysql_adapter.rb +25 -0
- data/lib/active_record/connection_adapters/makara_jdbcpostgresql_adapter.rb +25 -0
- data/lib/active_record/connection_adapters/makara_mysql2_adapter.rb +44 -0
- data/lib/active_record/connection_adapters/makara_postgresql_adapter.rb +44 -0
- data/lib/active_record/connection_adapters/mysql2_makara_adapter.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql_makara_adapter.rb +44 -0
- data/lib/makara.rb +25 -0
- data/lib/makara/cache.rb +53 -0
- data/lib/makara/cache/memory_store.rb +28 -0
- data/lib/makara/cache/noop_store.rb +15 -0
- data/lib/makara/config_parser.rb +200 -0
- data/lib/makara/connection_wrapper.rb +170 -0
- data/lib/makara/context.rb +46 -0
- data/lib/makara/error_handler.rb +39 -0
- data/lib/makara/errors/all_connections_blacklisted.rb +13 -0
- data/lib/makara/errors/blacklist_connection.rb +14 -0
- data/lib/makara/errors/no_connections_available.rb +14 -0
- data/lib/makara/logging/logger.rb +23 -0
- data/lib/makara/logging/subscriber.rb +38 -0
- data/lib/makara/middleware.rb +109 -0
- data/lib/makara/pool.rb +188 -0
- data/lib/makara/proxy.rb +277 -0
- data/lib/makara/railtie.rb +14 -0
- data/lib/makara/version.rb +15 -0
- data/makara.gemspec +19 -0
- data/spec/active_record/connection_adapters/makara_abstract_adapter_error_handling_spec.rb +92 -0
- data/spec/active_record/connection_adapters/makara_abstract_adapter_spec.rb +114 -0
- data/spec/active_record/connection_adapters/makara_mysql2_adapter_spec.rb +183 -0
- data/spec/active_record/connection_adapters/makara_postgresql_adapter_spec.rb +121 -0
- data/spec/cache_spec.rb +59 -0
- data/spec/config_parser_spec.rb +102 -0
- data/spec/connection_wrapper_spec.rb +33 -0
- data/spec/context_spec.rb +107 -0
- data/spec/middleware_spec.rb +84 -0
- data/spec/pool_spec.rb +158 -0
- data/spec/proxy_spec.rb +182 -0
- data/spec/spec_helper.rb +46 -0
- data/spec/support/configurator.rb +13 -0
- data/spec/support/deep_dup.rb +12 -0
- data/spec/support/mock_objects.rb +67 -0
- data/spec/support/mysql2_database.yml +17 -0
- data/spec/support/mysql2_database_with_custom_errors.yml +17 -0
- data/spec/support/pool_extensions.rb +14 -0
- data/spec/support/postgresql_database.yml +13 -0
- data/spec/support/proxy_extensions.rb +33 -0
- data/spec/support/schema.rb +7 -0
- metadata +144 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in makara.gemspec
|
4
|
+
gemspec :path => '../'
|
5
|
+
|
6
|
+
|
7
|
+
gem 'rake'
|
8
|
+
gem 'activerecord', '3.1.8'
|
9
|
+
gem 'rspec'
|
10
|
+
gem 'rack', '1.6.0'
|
11
|
+
gem 'i18n', '~> 0.6.0'
|
12
|
+
gem 'mysql2', '~> 0.3.10', :platform => :ruby
|
13
|
+
gem 'activerecord-jdbcmysql-adapter', :platform => :jruby
|
14
|
+
gem 'activerecord-jdbcpostgresql-adapter', :platform => :jruby
|
15
|
+
|
16
|
+
rmajor, rminor, rpatch = RUBY_VERSION.split(/[^\d]/)[0..2].map(&:to_i)
|
17
|
+
|
18
|
+
if rmajor == 1 && rminor == 8
|
19
|
+
gem 'timecop', '0.5.9'
|
20
|
+
else
|
21
|
+
gem 'timecop'
|
22
|
+
end
|
23
|
+
|
24
|
+
# 1.8 - 1.9.2
|
25
|
+
if rmajor == 1 && (rminor == 8 || (rminor == 9 && rpatch < 3))
|
26
|
+
gem 'pg', '0.17.1', :platform => :ruby
|
27
|
+
else
|
28
|
+
gem 'pg', :platform => :ruby
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in makara.gemspec
|
4
|
+
gemspec :path => '../'
|
5
|
+
|
6
|
+
|
7
|
+
gem 'rake'
|
8
|
+
gem 'activerecord', '3.2.6'
|
9
|
+
gem 'rspec'
|
10
|
+
gem 'rack', '1.6.0'
|
11
|
+
gem 'i18n', '~> 0.6.0'
|
12
|
+
gem 'mysql2', '~> 0.3.10', :platform => :ruby
|
13
|
+
gem 'activerecord-jdbcmysql-adapter', :platform => :jruby
|
14
|
+
gem 'activerecord-jdbcpostgresql-adapter', :platform => :jruby
|
15
|
+
|
16
|
+
rmajor, rminor, rpatch = RUBY_VERSION.split(/[^\d]/)[0..2].map(&:to_i)
|
17
|
+
|
18
|
+
if rmajor == 1 && rminor == 8
|
19
|
+
gem 'timecop', '0.5.9'
|
20
|
+
else
|
21
|
+
gem 'timecop'
|
22
|
+
end
|
23
|
+
|
24
|
+
# 1.8 - 1.9.2
|
25
|
+
if rmajor == 1 && (rminor == 8 || (rminor == 9 && rpatch < 3))
|
26
|
+
gem 'pg', '0.17.1', :platform => :ruby
|
27
|
+
else
|
28
|
+
gem 'pg', :platform => :ruby
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in makara.gemspec
|
4
|
+
gemspec :path => '../'
|
5
|
+
|
6
|
+
|
7
|
+
gem 'rake'
|
8
|
+
gem 'activerecord', '4.0.0'
|
9
|
+
gem 'rspec'
|
10
|
+
gem 'rack', '1.6.0'
|
11
|
+
gem 'timecop'
|
12
|
+
gem 'mysql2', '~> 0.3.10', :platform => :ruby
|
13
|
+
gem 'activerecord-jdbcmysql-adapter', :platform => :jruby
|
14
|
+
gem 'pg', :platform => :ruby
|
15
|
+
gem 'activerecord-jdbcpostgresql-adapter', :platform => :jruby
|
@@ -0,0 +1,15 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in makara.gemspec
|
4
|
+
gemspec :path => '../'
|
5
|
+
|
6
|
+
|
7
|
+
gem 'rake'
|
8
|
+
gem 'activerecord', '4.1.0'
|
9
|
+
gem 'rspec'
|
10
|
+
gem 'rack', '1.6.0'
|
11
|
+
gem 'timecop'
|
12
|
+
gem 'mysql2', '~> 0.3.10', :platform => :ruby
|
13
|
+
gem 'activerecord-jdbcmysql-adapter', :platform => :jruby
|
14
|
+
gem 'pg', :platform => :ruby
|
15
|
+
gem 'activerecord-jdbcpostgresql-adapter', :platform => :jruby
|
@@ -0,0 +1,15 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in makara.gemspec
|
4
|
+
gemspec :path => '../'
|
5
|
+
|
6
|
+
|
7
|
+
gem 'rake'
|
8
|
+
gem 'activerecord', '~> 4.2.0'
|
9
|
+
gem 'rspec'
|
10
|
+
gem 'rack', '1.6.0'
|
11
|
+
gem 'timecop'
|
12
|
+
gem 'mysql2', '~> 0.3.10', :platform => :ruby
|
13
|
+
gem 'activerecord-jdbcmysql-adapter', :platform => :jruby
|
14
|
+
gem 'pg', :platform => :ruby
|
15
|
+
gem 'activerecord-jdbcpostgresql-adapter', :platform => :jruby
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'active_record/connection_adapters/makara_abstract_adapter'
|
2
|
+
require 'active_record/connection_adapters/jdbcmysql_adapter'
|
3
|
+
require 'active_record/connection_adapters/mysql2_makara_adapter'
|
4
|
+
|
5
|
+
if ActiveRecord::VERSION::MAJOR >= 4
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
module ConnectionHandling
|
9
|
+
def jdbcmysql_makara_connection(config)
|
10
|
+
mysql2_makara_connection(config)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
else
|
16
|
+
|
17
|
+
module ActiveRecord
|
18
|
+
class Base
|
19
|
+
def self.jdbcmysql_makara_connection(config)
|
20
|
+
self.mysql2_makara_connection(config)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'active_record/connection_adapters/makara_abstract_adapter'
|
2
|
+
require 'active_record/connection_adapters/jdbcpostgresql_adapter'
|
3
|
+
require 'active_record/connection_adapters/postgresql_makara_adapter'
|
4
|
+
|
5
|
+
if ActiveRecord::VERSION::MAJOR >= 4
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
module ConnectionHandling
|
9
|
+
def jdbcpostgresql_makara_connection(config)
|
10
|
+
postgresql_makara_connection(config)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
else
|
16
|
+
|
17
|
+
module ActiveRecord
|
18
|
+
class Base
|
19
|
+
def self.jdbcpostgresql_makara_connection(config)
|
20
|
+
self.postgresql_makara_connection(config)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
class MakaraAbstractAdapter < ::Makara::Proxy
|
6
|
+
|
7
|
+
|
8
|
+
class ErrorHandler < ::Makara::ErrorHandler
|
9
|
+
|
10
|
+
|
11
|
+
HARSH_ERRORS = [
|
12
|
+
'ActiveRecord::RecordNotUnique',
|
13
|
+
'ActiveRecord::InvalidForeignKey',
|
14
|
+
'Makara::Errors::BlacklistConnection'
|
15
|
+
].map(&:freeze).freeze
|
16
|
+
|
17
|
+
|
18
|
+
CONNECTION_MATCHERS = [
|
19
|
+
/(closed|lost|no|terminating|terminated)\s?([^\s]+)?\sconnection/,
|
20
|
+
/gone away/,
|
21
|
+
/connection[^:]+refused/,
|
22
|
+
/could not connect/,
|
23
|
+
/can\'t connect/,
|
24
|
+
/cannot connect/,
|
25
|
+
/connection[^:]+closed/,
|
26
|
+
/can\'t get socket descriptor/,
|
27
|
+
/connection to [a-z0-9.]+:[0-9]+ refused/,
|
28
|
+
/timeout expired/,
|
29
|
+
/could not translate host name/,
|
30
|
+
/timeout waiting for a response/,
|
31
|
+
/the database system is (starting|shutting)/
|
32
|
+
].map(&:freeze).freeze
|
33
|
+
|
34
|
+
|
35
|
+
def handle(connection)
|
36
|
+
|
37
|
+
yield
|
38
|
+
|
39
|
+
rescue Exception => e
|
40
|
+
# do it via class name to avoid version-specific constant dependencies
|
41
|
+
case e.class.name
|
42
|
+
when *harsh_errors
|
43
|
+
harshly(e)
|
44
|
+
else
|
45
|
+
if connection_message?(e) || custom_error_message?(connection, e)
|
46
|
+
gracefully(connection, e)
|
47
|
+
else
|
48
|
+
harshly(e)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def harsh_errors
|
56
|
+
HARSH_ERRORS
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def connection_matchers
|
61
|
+
CONNECTION_MATCHERS
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def connection_message?(message)
|
66
|
+
message = message.to_s.downcase
|
67
|
+
|
68
|
+
case message
|
69
|
+
when *connection_matchers
|
70
|
+
true
|
71
|
+
else
|
72
|
+
false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
def custom_error_message?(connection, message)
|
78
|
+
custom_error_matchers = connection._makara_custom_error_matchers
|
79
|
+
return false if custom_error_matchers.empty?
|
80
|
+
|
81
|
+
message = message.to_s
|
82
|
+
|
83
|
+
custom_error_matchers.each do |matcher|
|
84
|
+
|
85
|
+
if matcher.is_a?(String)
|
86
|
+
|
87
|
+
# accept strings that look like "/.../" as a regex
|
88
|
+
if matcher =~ /^\/(.+)\/([a-z])?$/
|
89
|
+
|
90
|
+
options = $2 ? (($2.include?('x') ? Regexp::EXTENDED : 0) |
|
91
|
+
($2.include?('i') ? Regexp::IGNORECASE : 0) |
|
92
|
+
($2.include?('m') ? Regexp::MULTILINE : 0)) : 0
|
93
|
+
|
94
|
+
matcher = Regexp.new($1, options)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
return true if matcher === message
|
99
|
+
end
|
100
|
+
|
101
|
+
false
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
hijack_method :execute, :select_rows, :exec_query, :transaction
|
109
|
+
send_to_all :connect, :reconnect!, :verify!, :clear_cache!, :reset!
|
110
|
+
|
111
|
+
SQL_MASTER_MATCHERS = [/\A\s*select.+for update\Z/i, /select.+lock in share mode\Z/i].map(&:freeze).freeze
|
112
|
+
SQL_SLAVE_MATCHERS = [/\A\s*select\s/i].map(&:freeze).freeze
|
113
|
+
SQL_ALL_MATCHERS = [/\A\s*set\s/i].map(&:freeze).freeze
|
114
|
+
SQL_SKIP_STICKINESS_MATCHERS = [/\A\s*show\s([\w]+\s)?(field|table|database|schema|view|index)(es|s)?/i, /\A\s*(set|describe|explain|pragma)\s/i].map(&:freeze).freeze
|
115
|
+
|
116
|
+
|
117
|
+
def sql_master_matchers
|
118
|
+
SQL_MASTER_MATCHERS
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
def sql_slave_matchers
|
123
|
+
SQL_SLAVE_MATCHERS
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
def sql_all_matchers
|
128
|
+
SQL_ALL_MATCHERS
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
def sql_skip_stickiness_matchers
|
133
|
+
SQL_SKIP_STICKINESS_MATCHERS
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
def initialize(config)
|
138
|
+
@error_handler = ::ActiveRecord::ConnectionAdapters::MakaraAbstractAdapter::ErrorHandler.new
|
139
|
+
super(config)
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
protected
|
144
|
+
|
145
|
+
|
146
|
+
def appropriate_connection(method_name, args)
|
147
|
+
if needed_by_all?(method_name, args)
|
148
|
+
|
149
|
+
handling_an_all_execution(method_name) do
|
150
|
+
# slave pool must run first.
|
151
|
+
@slave_pool.provide_each do |con|
|
152
|
+
hijacked do
|
153
|
+
yield con
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
@master_pool.provide_each do |con|
|
158
|
+
hijacked do
|
159
|
+
yield con
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
else
|
165
|
+
|
166
|
+
super(method_name, args) do |con|
|
167
|
+
yield con
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
def should_stick?(method_name, args)
|
175
|
+
sql = args.first.to_s
|
176
|
+
return false if sql_skip_stickiness_matchers.any?{|m| sql =~ m }
|
177
|
+
super
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
def needed_by_all?(method_name, args)
|
182
|
+
sql = args.first.to_s
|
183
|
+
return true if sql_all_matchers.any?{|m| sql =~ m }
|
184
|
+
false
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
def needs_master?(method_name, args)
|
189
|
+
sql = args.first.to_s
|
190
|
+
return true if sql_master_matchers.any?{|m| sql =~ m }
|
191
|
+
return false if sql_slave_matchers.any?{|m| sql =~ m }
|
192
|
+
true
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
def connection_for(config)
|
197
|
+
config = Makara::ConfigParser.merge_and_resolve_default_url_config(config)
|
198
|
+
active_record_connection_for(config)
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
def active_record_connection_for(config)
|
203
|
+
raise NotImplementedError
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'active_record/connection_adapters/makara_abstract_adapter'
|
2
|
+
require 'active_record/connection_adapters/jdbcmysql_adapter'
|
3
|
+
require 'active_record/connection_adapters/makara_mysql2_adapter'
|
4
|
+
|
5
|
+
if ActiveRecord::VERSION::MAJOR >= 4
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
module ConnectionHandling
|
9
|
+
def makara_jdbcmysql_connection(config)
|
10
|
+
makara_mysql2_connection(config)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
else
|
16
|
+
|
17
|
+
module ActiveRecord
|
18
|
+
class Base
|
19
|
+
def self.makara_jdbcmysql_connection(config)
|
20
|
+
self.makara_mysql2_connection(config)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'active_record/connection_adapters/makara_abstract_adapter'
|
2
|
+
require 'active_record/connection_adapters/jdbcpostgresql_adapter'
|
3
|
+
require 'active_record/connection_adapters/makara_postgresql_adapter'
|
4
|
+
|
5
|
+
if ActiveRecord::VERSION::MAJOR >= 4
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
module ConnectionHandling
|
9
|
+
def makara_jdbcpostgresql_connection(config)
|
10
|
+
makara_postgresql_connection(config)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
else
|
16
|
+
|
17
|
+
module ActiveRecord
|
18
|
+
class Base
|
19
|
+
def self.makara_jdbcpostgresql_connection(config)
|
20
|
+
self.makara_postgresql_connection(config)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'active_record/connection_adapters/makara_abstract_adapter'
|
2
|
+
require 'active_record/connection_adapters/mysql2_adapter'
|
3
|
+
|
4
|
+
if ActiveRecord::VERSION::MAJOR >= 4
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
module ConnectionHandling
|
8
|
+
def makara_mysql2_connection(config)
|
9
|
+
ActiveRecord::ConnectionAdapters::MakaraMysql2Adapter.new(config)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
else
|
15
|
+
|
16
|
+
module ActiveRecord
|
17
|
+
class Base
|
18
|
+
def self.makara_mysql2_connection(config)
|
19
|
+
ActiveRecord::ConnectionAdapters::MakaraMysql2Adapter.new(config)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
module ActiveRecord
|
27
|
+
module ConnectionAdapters
|
28
|
+
class MakaraMysql2Adapter < ActiveRecord::ConnectionAdapters::MakaraAbstractAdapter
|
29
|
+
|
30
|
+
class << self
|
31
|
+
def visitor_for(*args)
|
32
|
+
ActiveRecord::ConnectionAdapters::Mysql2Adapter.visitor_for(*args)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def active_record_connection_for(config)
|
39
|
+
::ActiveRecord::Base.mysql2_connection(config)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|