makara 0.4.1 → 0.5.1

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 (88) hide show
  1. checksums.yaml +5 -5
  2. data/.github/dependabot.yml +11 -0
  3. data/.github/workflows/CI.yml +88 -0
  4. data/.github/workflows/gem-publish-public.yml +36 -0
  5. data/.rspec +1 -1
  6. data/.rubocop.yml +15 -0
  7. data/.rubocop_todo.yml +670 -0
  8. data/CHANGELOG.md +69 -48
  9. data/Gemfile +1 -16
  10. data/README.md +8 -9
  11. data/Rakefile +1 -1
  12. data/gemfiles/activerecord_5.2.gemfile +8 -0
  13. data/gemfiles/activerecord_6.0.gemfile +8 -0
  14. data/gemfiles/activerecord_6.1.gemfile +8 -0
  15. data/gemfiles/activerecord_head.gemfile +6 -0
  16. data/lib/active_record/connection_adapters/jdbcmysql_makara_adapter.rb +4 -18
  17. data/lib/active_record/connection_adapters/jdbcpostgresql_makara_adapter.rb +4 -18
  18. data/lib/active_record/connection_adapters/makara_abstract_adapter.rb +107 -30
  19. data/lib/active_record/connection_adapters/makara_jdbcmysql_adapter.rb +4 -18
  20. data/lib/active_record/connection_adapters/makara_jdbcpostgresql_adapter.rb +4 -18
  21. data/lib/active_record/connection_adapters/makara_mysql2_adapter.rb +4 -20
  22. data/lib/active_record/connection_adapters/makara_postgis_adapter.rb +4 -19
  23. data/lib/active_record/connection_adapters/makara_postgresql_adapter.rb +4 -20
  24. data/lib/active_record/connection_adapters/mysql2_makara_adapter.rb +4 -20
  25. data/lib/active_record/connection_adapters/postgresql_makara_adapter.rb +4 -20
  26. data/lib/makara/cache.rb +0 -2
  27. data/lib/makara/config_parser.rb +7 -15
  28. data/lib/makara/connection_wrapper.rb +43 -22
  29. data/lib/makara/context.rb +1 -0
  30. data/lib/makara/cookie.rb +1 -0
  31. data/lib/makara/error_handler.rb +0 -9
  32. data/lib/makara/errors/all_connections_blacklisted.rb +0 -2
  33. data/lib/makara/errors/blacklist_connection.rb +0 -2
  34. data/lib/makara/errors/blacklisted_while_in_transaction.rb +12 -0
  35. data/lib/makara/errors/invalid_shard.rb +14 -0
  36. data/lib/makara/errors/makara_error.rb +0 -1
  37. data/lib/makara/errors/no_connections_available.rb +0 -2
  38. data/lib/makara/logging/logger.rb +0 -4
  39. data/lib/makara/logging/subscriber.rb +0 -2
  40. data/lib/makara/middleware.rb +1 -2
  41. data/lib/makara/pool.rb +49 -31
  42. data/lib/makara/proxy.rb +56 -30
  43. data/lib/makara/railtie.rb +0 -2
  44. data/lib/makara/strategies/abstract.rb +1 -0
  45. data/lib/makara/strategies/priority_failover.rb +2 -0
  46. data/lib/makara/strategies/round_robin.rb +1 -3
  47. data/lib/makara/strategies/shard_aware.rb +45 -0
  48. data/lib/makara/version.rb +1 -3
  49. data/lib/makara.rb +7 -6
  50. data/makara.gemspec +26 -3
  51. data/spec/active_record/connection_adapters/makara_abstract_adapter_error_handling_spec.rb +1 -6
  52. data/spec/active_record/connection_adapters/makara_abstract_adapter_spec.rb +0 -9
  53. data/spec/active_record/connection_adapters/makara_mysql2_adapter_spec.rb +9 -22
  54. data/spec/active_record/connection_adapters/makara_postgis_adapter_spec.rb +2 -10
  55. data/spec/active_record/connection_adapters/makara_postgresql_adapter_spec.rb +62 -18
  56. data/spec/cache_spec.rb +0 -1
  57. data/spec/config_parser_spec.rb +54 -56
  58. data/spec/connection_wrapper_spec.rb +1 -2
  59. data/spec/cookie_spec.rb +4 -4
  60. data/spec/middleware_spec.rb +2 -2
  61. data/spec/pool_spec.rb +25 -14
  62. data/spec/proxy_spec.rb +0 -4
  63. data/spec/spec_helper.rb +6 -1
  64. data/spec/strategies/priority_failover_spec.rb +3 -4
  65. data/spec/strategies/round_robin_spec.rb +4 -8
  66. data/spec/strategies/shard_aware_spec.rb +218 -0
  67. data/spec/support/deep_dup.rb +1 -1
  68. data/spec/support/helpers.rb +5 -5
  69. data/spec/support/mock_objects.rb +5 -4
  70. data/spec/support/mysql2_database.yml +2 -2
  71. data/spec/support/mysql2_database_with_custom_errors.yml +2 -2
  72. data/spec/support/pool_extensions.rb +0 -3
  73. data/spec/support/postgis_schema.rb +1 -1
  74. data/spec/support/postgresql_database.yml +0 -2
  75. data/spec/support/proxy_extensions.rb +1 -3
  76. data/spec/support/schema.rb +1 -1
  77. data/spec/support/user.rb +1 -2
  78. metadata +165 -20
  79. data/.travis.yml +0 -105
  80. data/gemfiles/ar-head.gemfile +0 -24
  81. data/gemfiles/ar30.gemfile +0 -36
  82. data/gemfiles/ar31.gemfile +0 -36
  83. data/gemfiles/ar32.gemfile +0 -36
  84. data/gemfiles/ar40.gemfile +0 -24
  85. data/gemfiles/ar41.gemfile +0 -24
  86. data/gemfiles/ar42.gemfile +0 -24
  87. data/gemfiles/ar50.gemfile +0 -24
  88. data/gemfiles/ar51.gemfile +0 -24
@@ -1,9 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Makara::Strategies::RoundRobin do
4
- let(:proxy){ FakeProxy.new({:makara => pool_config.merge(makara_config).merge(:connections => [])}) }
4
+ let(:proxy){ FakeProxy.new({makara: pool_config.merge(makara_config).merge(connections: [])}) }
5
5
  let(:pool){ Makara::Pool.new('test', proxy) }
6
- let(:pool_config){ {:blacklist_duration => 5} }
6
+ let(:pool_config){ {blacklist_duration: 5} }
7
7
  let(:makara_config) { {} }
8
8
  let(:strategy) { pool.strategy }
9
9
 
@@ -14,7 +14,7 @@ describe Makara::Strategies::RoundRobin do
14
14
  end
15
15
 
16
16
  context 'bad config' do
17
- let(:makara_config) { { :test_strategy => 'SomethingElse::Here' } }
17
+ let(:makara_config) { { test_strategy: 'SomethingElse::Here' } }
18
18
  it 'should raise name error' do
19
19
  expect {
20
20
  pool
@@ -23,13 +23,12 @@ describe Makara::Strategies::RoundRobin do
23
23
  end
24
24
 
25
25
  context 'given in config' do
26
- let(:makara_config) { { :test_strategy => 'round_robin' } }
26
+ let(:makara_config) { { test_strategy: 'round_robin' } }
27
27
  it 'should use the strategy' do
28
28
  expect(pool.strategy).to be_instance_of(Makara::Strategies::RoundRobin)
29
29
  end
30
30
  end
31
31
 
32
-
33
32
  it 'should loop through with weights' do
34
33
  wrapper_a = pool.add(pool_config){ FakeConnection.new(something: 'a') }
35
34
  wrapper_b = pool.add(pool_config){ FakeConnection.new(something: 'b') }
@@ -61,7 +60,4 @@ describe Makara::Strategies::RoundRobin do
61
60
  expect(strategy.next.something).to eql('c')
62
61
  expect(strategy.next.something).to eql('b')
63
62
  end
64
-
65
-
66
-
67
63
  end
@@ -0,0 +1,218 @@
1
+ require 'spec_helper'
2
+
3
+ describe Makara::Strategies::ShardAware do
4
+ def with_shard(shard_id)
5
+ begin
6
+ Thread.current['makara_shard_id'] = shard_id
7
+ yield
8
+ ensure
9
+ Thread.current['makara_shard_id'] = nil
10
+ end
11
+ end
12
+
13
+ describe "failover strategy with shard awareness," do
14
+ let(:proxy){ FakeProxy.new({makara: pool_config.merge(makara_config).merge(connections: [])}) }
15
+ let(:pool){ Makara::Pool.new('master', proxy) }
16
+ let(:pool_config){ { blacklist_duration: 5} }
17
+ let(:makara_config) { {
18
+ master_strategy: 'failover',
19
+ master_shard_aware: true,
20
+ master_default_shard: 'shard2'
21
+ } }
22
+ let(:strategy) { pool.strategy }
23
+
24
+ it 'should use the strategy' do
25
+ expect(pool.strategy).to be_instance_of(Makara::Strategies::ShardAware)
26
+ end
27
+
28
+ it 'should take the top weight for a given shard' do
29
+ wrapper_a = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'a') }
30
+ wrapper_b = pool.add(pool_config.merge(shard_id: 'shard1', weight: 2)){ FakeConnection.new(something: 'b') }
31
+ wrapper_c = pool.add(pool_config.merge(weight: 2, shard_id: 'shard2')){ FakeConnection.new(something: 'c') }
32
+
33
+ # default shard
34
+ expect(strategy.current.something).to eql('c')
35
+ expect(strategy.next.something).to eql('c')
36
+ expect(strategy.next.something).to eql('c')
37
+
38
+ # shard1
39
+ with_shard('shard1') do
40
+ expect(strategy.current.something).to eql('b')
41
+ expect(strategy.next.something).to eql('b')
42
+ expect(strategy.next.something).to eql('b')
43
+ end
44
+
45
+ # shard2
46
+ with_shard('shard2') do
47
+ expect(strategy.current.something).to eql('c')
48
+ expect(strategy.next.something).to eql('c')
49
+ expect(strategy.next.something).to eql('c')
50
+ end
51
+ end
52
+
53
+ it 'should take given order within shard if no weights' do
54
+ wrapper_a = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'a') }
55
+ wrapper_b = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'b') }
56
+ wrapper_c = pool.add(pool_config.merge(shard_id: 'shard2')){ FakeConnection.new(something: 'c') }
57
+
58
+ # default shard
59
+ expect(strategy.current.something).to eql('c')
60
+ expect(strategy.next.something).to eql('c')
61
+ expect(strategy.next.something).to eql('c')
62
+
63
+ # shard1
64
+ with_shard('shard1') do
65
+ expect(strategy.current.something).to eql('a')
66
+ expect(strategy.next.something).to eql('a')
67
+ end
68
+
69
+ # shard2
70
+ with_shard('shard2') do
71
+ expect(strategy.current.something).to eql('c')
72
+ expect(strategy.next.something).to eql('c')
73
+ end
74
+ end
75
+
76
+ it 'should handle failover to next one within shard' do
77
+ wrapper_a = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'a') }
78
+ wrapper_b = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'b') }
79
+ wrapper_c = pool.add(pool_config.merge(shard_id: 'shard2')){ FakeConnection.new(something: 'c') }
80
+
81
+ # default shard
82
+ expect(strategy.current.something).to eql('c')
83
+ expect(strategy.next.something).to eql('c')
84
+ expect(strategy.next.something).to eql('c')
85
+
86
+ # skips a for shard1
87
+ with_shard('shard1') do
88
+ pool.provide do |connection|
89
+ if connection == wrapper_a
90
+ raise Makara::Errors::BlacklistConnection.new(wrapper_a, StandardError.new('failure'))
91
+ end
92
+ end
93
+ expect(strategy.current.something).to eql('b')
94
+ expect(strategy.next.something).to eql('b')
95
+ end
96
+
97
+ # shard2
98
+ with_shard('shard2') do
99
+ expect(strategy.current.something).to eql('c')
100
+ expect(strategy.next.something).to eql('c')
101
+ end
102
+ end
103
+ it 'raises error for invalid shard' do
104
+ with_shard('shard3') do
105
+ expect{strategy.current.something }.to raise_error(Makara::Errors::InvalidShard)
106
+ end
107
+ end
108
+ end
109
+
110
+ describe "round_robin strategy with shard awareness," do
111
+ let(:proxy){ FakeProxy.new({makara: pool_config.merge(makara_config).merge(connections: [])}) }
112
+ let(:pool){ Makara::Pool.new('master', proxy) }
113
+ let(:pool_config){ { blacklist_duration: 5} }
114
+ let(:makara_config) { {
115
+ master_strategy: 'round_robin',
116
+ master_shard_aware: true,
117
+ master_default_shard: 'shard2'
118
+ } }
119
+ let(:strategy) { pool.strategy }
120
+
121
+ it 'should use the strategy' do
122
+ expect(pool.strategy).to be_instance_of(Makara::Strategies::ShardAware)
123
+ end
124
+
125
+ it 'should loop through with weights within shard' do
126
+ wrapper_a = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'a') }
127
+ wrapper_b = pool.add(pool_config.merge(shard_id: 'shard1', weight: 2)){ FakeConnection.new(something: 'b') }
128
+ wrapper_c = pool.add(pool_config.merge(weight: 2, shard_id: 'shard2')){ FakeConnection.new(something: 'c') }
129
+
130
+ # default shard
131
+ expect(strategy.current.something).to eql('c')
132
+ expect(strategy.next.something).to eql('c')
133
+ expect(strategy.next.something).to eql('c')
134
+
135
+ # shard1
136
+ with_shard('shard1') do
137
+ expect(strategy.current.something).to eql('a')
138
+ expect(strategy.next.something).to eql('b')
139
+ expect(strategy.next.something).to eql('b')
140
+ expect(strategy.next.something).to eql('a')
141
+ end
142
+
143
+ # shard2
144
+ with_shard('shard2') do
145
+ expect(strategy.current.something).to eql('c')
146
+ expect(strategy.next.something).to eql('c')
147
+ expect(strategy.next.something).to eql('c')
148
+ end
149
+ end
150
+
151
+ it 'should handle failover to next one within shard' do
152
+ wrapper_a = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'a') }
153
+ wrapper_b = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'b') }
154
+ wrapper_c = pool.add(pool_config.merge(shard_id: 'shard2')){ FakeConnection.new(something: 'c') }
155
+
156
+ # default shard
157
+ expect(strategy.current.something).to eql('c')
158
+ expect(strategy.next.something).to eql('c')
159
+ expect(strategy.next.something).to eql('c')
160
+
161
+ # skips a for shard1
162
+ with_shard('shard1') do
163
+ pool.provide do |connection|
164
+ if connection == wrapper_a
165
+ raise Makara::Errors::BlacklistConnection.new(wrapper_a, StandardError.new('failure'))
166
+ end
167
+ end
168
+ expect(strategy.current.something).to eql('b')
169
+ expect(strategy.next.something).to eql('b')
170
+ expect(strategy.next.something).to eql('b')
171
+ end
172
+
173
+ # shard2
174
+ with_shard('shard2') do
175
+ expect(strategy.current.something).to eql('c')
176
+ expect(strategy.next.something).to eql('c')
177
+ expect(strategy.next.something).to eql('c')
178
+ end
179
+ end
180
+ it 'raises error for invalid shard' do
181
+ with_shard('shard3') do
182
+ expect{strategy.current.something }.to raise_error(Makara::Errors::InvalidShard)
183
+ end
184
+ end
185
+ end
186
+
187
+ describe "uses the configured failover strategy when shard_aware set to false," do
188
+ let(:proxy){ FakeProxy.new({makara: pool_config.merge(makara_config).merge(connections: [])}) }
189
+ let(:pool){ Makara::Pool.new('master', proxy) }
190
+ let(:pool_config){ { blacklist_duration: 5} }
191
+ let(:makara_config) { {
192
+ master_strategy: 'failover',
193
+ master_shard_aware: false,
194
+ master_default_shard: 'shard2'
195
+ } }
196
+ let(:strategy) { pool.strategy }
197
+
198
+ it 'should use the failover strategy' do
199
+ expect(pool.strategy).to be_instance_of(Makara::Strategies::PriorityFailover)
200
+ end
201
+ end
202
+
203
+ describe "uses the configured roundrobin strategy when shard_aware set to false," do
204
+ let(:proxy){ FakeProxy.new({makara: pool_config.merge(makara_config).merge(connections: [])}) }
205
+ let(:pool){ Makara::Pool.new('master', proxy) }
206
+ let(:pool_config){ { blacklist_duration: 5} }
207
+ let(:makara_config) { {
208
+ master_strategy: 'round_robin',
209
+ master_shard_aware: false,
210
+ master_default_shard: 'shard2'
211
+ } }
212
+ let(:strategy) { pool.strategy }
213
+
214
+ it 'should use the failover strategy' do
215
+ expect(pool.strategy).to be_instance_of(Makara::Strategies::RoundRobin)
216
+ end
217
+ end
218
+ end
@@ -9,4 +9,4 @@ unless Hash.respond_to?(:deep_dup)
9
9
  duplicate
10
10
  end
11
11
  end
12
- end
12
+ end
@@ -11,16 +11,16 @@ module SpecHelpers
11
11
 
12
12
  def config(masters = 1, slaves = 2)
13
13
  connections = []
14
- masters.times{ connections << {:role => 'master'} }
15
- slaves.times{ connections << {:role => 'slave'} }
14
+ masters.times{ connections << {role: 'master'} }
15
+ slaves.times{ connections << {role: 'slave'} }
16
16
  {
17
- :makara => {
17
+ makara: {
18
18
  # Defaults:
19
19
  # :master_ttl => 5,
20
20
  # :blacklist_duration => 30,
21
21
  # :sticky => true
22
- :id => 'mock_mysql',
23
- :connections => connections
22
+ id: 'mock_mysql',
23
+ connections: connections
24
24
  }
25
25
  }
26
26
  end
@@ -1,7 +1,6 @@
1
1
  require 'active_record/connection_adapters/makara_abstract_adapter'
2
2
 
3
3
  class FakeConnection < Struct.new(:config)
4
-
5
4
  def ping
6
5
  'ping!'
7
6
  end
@@ -18,6 +17,10 @@ class FakeConnection < Struct.new(:config)
18
17
  true
19
18
  end
20
19
 
20
+ def open_transactions
21
+ (config || {}).fetch(:open_transactions, 0)
22
+ end
23
+
21
24
  def disconnect!
22
25
  true
23
26
  end
@@ -28,7 +31,6 @@ class FakeConnection < Struct.new(:config)
28
31
  end
29
32
 
30
33
  class FakeDatabaseAdapter < Struct.new(:config)
31
-
32
34
  def execute(sql, name = nil)
33
35
  []
34
36
  end
@@ -44,11 +46,9 @@ class FakeDatabaseAdapter < Struct.new(:config)
44
46
  def active?
45
47
  true
46
48
  end
47
-
48
49
  end
49
50
 
50
51
  class FakeProxy < Makara::Proxy
51
-
52
52
  send_to_all :ping
53
53
  hijack_method :execute
54
54
 
@@ -58,6 +58,7 @@ class FakeProxy < Makara::Proxy
58
58
 
59
59
  def needs_master?(method_name, args)
60
60
  return false if args.first =~ /^select/
61
+
61
62
  true
62
63
  end
63
64
  end
@@ -1,9 +1,9 @@
1
1
  test:
2
2
  adapter: 'mysql2_makara'
3
3
  database: 'makara_test'
4
- username: 'root'
4
+ host: <%= ENV["MYSQL_HOST"] %>
5
+ username: root
5
6
  password: ''
6
-
7
7
  timeout: 5000
8
8
  connect_timeout: 1
9
9
  read_timeout: 1
@@ -1,9 +1,9 @@
1
1
  test:
2
2
  adapter: 'mysql2_makara'
3
3
  database: 'makara_test'
4
- username: 'root'
4
+ host: <%= ENV["MYSQL_HOST"] %>
5
+ username: root
5
6
  password: ''
6
-
7
7
  timeout: 5000
8
8
  connect_timeout: 1
9
9
  read_timeout: 1
@@ -1,5 +1,4 @@
1
1
  module PoolExtensions
2
-
3
2
  def connections
4
3
  @connections
5
4
  end
@@ -7,8 +6,6 @@ module PoolExtensions
7
6
  def connection_count
8
7
  @connections.length
9
8
  end
10
-
11
9
  end
12
10
 
13
-
14
11
  Makara::Pool.send(:include, PoolExtensions)
@@ -5,7 +5,7 @@ conn.execute "create extension if not exists postgis"
5
5
  if conn.table_exists? "towns"
6
6
  conn.execute("TRUNCATE TABLE towns")
7
7
  else
8
- conn.create_table "towns", :force => true do |t|
8
+ conn.create_table "towns", force: true do |t|
9
9
  t.st_point "location"
10
10
  end
11
11
  end
@@ -1,8 +1,6 @@
1
1
  test:
2
2
  adapter: 'postgresql_makara'
3
3
  database: 'makara_test'
4
- username: 'root'
5
- password: ''
6
4
 
7
5
  timeout: 5000
8
6
 
@@ -1,7 +1,6 @@
1
1
  module ProxyExtensions
2
-
3
2
  attr_reader :master_pool, :slave_pool, :id
4
-
3
+
5
4
  def master_for?(sql)
6
5
  pool_for(sql) == master_pool
7
6
  end
@@ -27,7 +26,6 @@ module ProxyExtensions
27
26
  def sticky=(s)
28
27
  @sticky = s
29
28
  end
30
-
31
29
  end
32
30
 
33
31
  Makara::Proxy.send(:include, ProxyExtensions)
@@ -4,6 +4,6 @@ if conn.table_exists? "users"
4
4
  conn.execute("TRUNCATE TABLE users")
5
5
  else
6
6
  conn.create_table "users" do |t|
7
- t.string "name"
7
+ t.string "name"
8
8
  end
9
9
  end
data/spec/support/user.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  module Test
2
2
  class User < ::ActiveRecord::Base
3
-
4
3
  end
5
- end
4
+ end