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.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +28 -0
  7. data/CHANGELOG.md +27 -0
  8. data/Gemfile +18 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +278 -0
  11. data/Rakefile +9 -0
  12. data/gemfiles/ar30.gemfile +30 -0
  13. data/gemfiles/ar31.gemfile +29 -0
  14. data/gemfiles/ar32.gemfile +29 -0
  15. data/gemfiles/ar40.gemfile +15 -0
  16. data/gemfiles/ar41.gemfile +15 -0
  17. data/gemfiles/ar42.gemfile +15 -0
  18. data/lib/active_record/connection_adapters/jdbcmysql_makara_adapter.rb +25 -0
  19. data/lib/active_record/connection_adapters/jdbcpostgresql_makara_adapter.rb +25 -0
  20. data/lib/active_record/connection_adapters/makara_abstract_adapter.rb +209 -0
  21. data/lib/active_record/connection_adapters/makara_jdbcmysql_adapter.rb +25 -0
  22. data/lib/active_record/connection_adapters/makara_jdbcpostgresql_adapter.rb +25 -0
  23. data/lib/active_record/connection_adapters/makara_mysql2_adapter.rb +44 -0
  24. data/lib/active_record/connection_adapters/makara_postgresql_adapter.rb +44 -0
  25. data/lib/active_record/connection_adapters/mysql2_makara_adapter.rb +44 -0
  26. data/lib/active_record/connection_adapters/postgresql_makara_adapter.rb +44 -0
  27. data/lib/makara.rb +25 -0
  28. data/lib/makara/cache.rb +53 -0
  29. data/lib/makara/cache/memory_store.rb +28 -0
  30. data/lib/makara/cache/noop_store.rb +15 -0
  31. data/lib/makara/config_parser.rb +200 -0
  32. data/lib/makara/connection_wrapper.rb +170 -0
  33. data/lib/makara/context.rb +46 -0
  34. data/lib/makara/error_handler.rb +39 -0
  35. data/lib/makara/errors/all_connections_blacklisted.rb +13 -0
  36. data/lib/makara/errors/blacklist_connection.rb +14 -0
  37. data/lib/makara/errors/no_connections_available.rb +14 -0
  38. data/lib/makara/logging/logger.rb +23 -0
  39. data/lib/makara/logging/subscriber.rb +38 -0
  40. data/lib/makara/middleware.rb +109 -0
  41. data/lib/makara/pool.rb +188 -0
  42. data/lib/makara/proxy.rb +277 -0
  43. data/lib/makara/railtie.rb +14 -0
  44. data/lib/makara/version.rb +15 -0
  45. data/makara.gemspec +19 -0
  46. data/spec/active_record/connection_adapters/makara_abstract_adapter_error_handling_spec.rb +92 -0
  47. data/spec/active_record/connection_adapters/makara_abstract_adapter_spec.rb +114 -0
  48. data/spec/active_record/connection_adapters/makara_mysql2_adapter_spec.rb +183 -0
  49. data/spec/active_record/connection_adapters/makara_postgresql_adapter_spec.rb +121 -0
  50. data/spec/cache_spec.rb +59 -0
  51. data/spec/config_parser_spec.rb +102 -0
  52. data/spec/connection_wrapper_spec.rb +33 -0
  53. data/spec/context_spec.rb +107 -0
  54. data/spec/middleware_spec.rb +84 -0
  55. data/spec/pool_spec.rb +158 -0
  56. data/spec/proxy_spec.rb +182 -0
  57. data/spec/spec_helper.rb +46 -0
  58. data/spec/support/configurator.rb +13 -0
  59. data/spec/support/deep_dup.rb +12 -0
  60. data/spec/support/mock_objects.rb +67 -0
  61. data/spec/support/mysql2_database.yml +17 -0
  62. data/spec/support/mysql2_database_with_custom_errors.yml +17 -0
  63. data/spec/support/pool_extensions.rb +14 -0
  64. data/spec/support/postgresql_database.yml +13 -0
  65. data/spec/support/proxy_extensions.rb +33 -0
  66. data/spec/support/schema.rb +7 -0
  67. 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