active_record_shards 4.0.0.beta4 → 4.0.0.beta5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2de0f94bb1444db60049ab7f90c2f984cd3d45ab
4
- data.tar.gz: 9bb7fb07f9299ef507306ce8ed589a26b1535df5
3
+ metadata.gz: 635a833192fb641e710ff4bf7b30380023936a1a
4
+ data.tar.gz: 8fa608a3b2f2f692b20b8445a4a21d879b88688f
5
5
  SHA512:
6
- metadata.gz: 3cd39f4712671a92461b1e6840146fd67e01051e7adc0597d8a0065008c4522b99769d06b0e828ca2b36098013403be3aaa0d041ff53100decc133fd38673a6a
7
- data.tar.gz: d668ee2c261bfda7b0461696899a2cc3d5a680f141988c633f1b9dff802db68c5d602037fc50cd66829e6eba3b6052aa5e7a0c2de181aa67c497f4860f7697fa
6
+ metadata.gz: 2ea40014c08f3670c4155e721d05abcab31eb366b6761a02fb1386710288d9f9ca59ea8f2580da0d71d4f502ede737775952f3f0a47140f7527e6a2eabf49716
7
+ data.tar.gz: 6343baabe62a1a3007a8cacd42eea95873e0a7f5713249e8d65c1b0ba16f6b0850eaf5429b06fa5ba7dc7f4e6641d38342fe3a30a57a4b00bd1f67cc0951ad8e
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
  require 'active_record'
3
3
  require 'active_record/base'
4
+ require 'active_record_shards/base_config'
5
+ require 'active_record_shards/connection_resolver'
4
6
  require 'active_record_shards/configuration_parser'
5
7
  require 'active_record_shards/model'
6
- require 'active_record_shards/sharded_model'
8
+ require 'active_record_shards/slave_db'
7
9
  require 'active_record_shards/shard_selection'
10
+ require 'active_record_shards/no_shard_selection'
8
11
  require 'active_record_shards/connection_switcher'
12
+ require 'active_record_shards/sharded_model'
9
13
  require 'active_record_shards/association_collection_connection_selection'
10
14
  require 'active_record_shards/default_slave_patches'
11
15
 
@@ -18,9 +22,10 @@ module ActiveRecordShards
18
22
  end
19
23
  end
20
24
 
25
+ ActiveRecord::Base.extend(ActiveRecordShards::BaseConfig)
21
26
  ActiveRecord::Base.extend(ActiveRecordShards::ConfigurationParser)
22
27
  ActiveRecord::Base.extend(ActiveRecordShards::Model)
23
- ActiveRecord::Base.extend(ActiveRecordShards::ConnectionSwitcher)
28
+ ActiveRecord::Base.extend(ActiveRecordShards::SlaveDb)
24
29
  ActiveRecord::Base.extend(ActiveRecordShards::DefaultSlavePatches)
25
30
  ActiveRecord::Relation.include(ActiveRecordShards::DefaultSlavePatches::ActiveRelationPatches)
26
31
  ActiveRecord::Associations::CollectionProxy.include(ActiveRecordShards::AssociationCollectionConnectionSelection)
@@ -0,0 +1,26 @@
1
+ module ActiveRecordShards
2
+ module BaseConfig
3
+ def connection_config
4
+ { shard: nil, sharded: false }
5
+ end
6
+
7
+ def connection_specification_name
8
+ name = connection_resolver.resolve_connection_name(connection_config)
9
+
10
+ unless configurations[name] || name == "primary"
11
+ raise ActiveRecord::AdapterNotSpecified, "No database defined by #{name} in your database config. (configurations: #{configurations.inspect})"
12
+ end
13
+
14
+ name
15
+ end
16
+
17
+ def connection_resolver
18
+ Thread.current[:connection_resolver] ||=
19
+ ActiveRecordShards::ConnectionResolver.new(configurations)
20
+ end
21
+
22
+ def reset_connection_resolver
23
+ Thread.current[:connection_resolver] = nil
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,31 @@
1
+ module ActiveRecordShards
2
+ class ConnectionResolver
3
+ attr_reader :configurations
4
+
5
+ def initialize(configurations)
6
+ @configurations = configurations
7
+ end
8
+
9
+ PRIMARY = "primary".freeze
10
+ def resolve_connection_name(slave:, shard:, sharded:)
11
+ resolved_shard = sharded ? shard : nil
12
+
13
+ if !resolved_shard && !slave
14
+ return PRIMARY
15
+ end
16
+
17
+ @shard_names ||= {}
18
+ @shard_names[ActiveRecordShards.rails_env] ||= {}
19
+ @shard_names[ActiveRecordShards.rails_env][resolved_shard] ||= {}
20
+ @shard_names[ActiveRecordShards.rails_env][resolved_shard][slave] ||= begin
21
+ s = ActiveRecordShards.rails_env.dup
22
+ s << "_shard_#{resolved_shard}" if resolved_shard
23
+
24
+ if slave && configurations["#{s}_slave"] # fall back to master connection
25
+ s << "_slave"
26
+ end
27
+ s
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,5 +1,5 @@
1
1
  module ActiveRecordShards
2
- module ConnectionSwitcher
2
+ module BaseConfig
3
3
  private
4
4
 
5
5
  def ensure_shard_connection
@@ -1,5 +1,5 @@
1
1
  module ActiveRecordShards
2
- module ConnectionSwitcher
2
+ module BaseConfig
3
3
  private
4
4
 
5
5
  def ensure_shard_connection
@@ -14,11 +14,11 @@ module ActiveRecordShards
14
14
  end
15
15
 
16
16
  def on_shard(shard)
17
- old_options = current_shard_selection.options
18
- switch_connection(shard: shard) if supports_sharding?
17
+ old_selection = current_shard_selection
18
+ switch_connection(ShardSelection.new(shard)) if supports_sharding?
19
19
  yield
20
20
  ensure
21
- switch_connection(old_options)
21
+ switch_connection(old_selection)
22
22
  end
23
23
 
24
24
  def on_first_shard
@@ -31,101 +31,33 @@ module ActiveRecordShards
31
31
  end
32
32
 
33
33
  def on_all_shards
34
- old_options = current_shard_selection.options
34
+ old_selection = current_shard_selection
35
35
  if supports_sharding?
36
36
  shard_names.map do |shard|
37
- switch_connection(shard: shard)
37
+ switch_connection(ShardSelection.new(shard))
38
38
  yield(shard)
39
39
  end
40
40
  else
41
41
  [yield]
42
42
  end
43
43
  ensure
44
- switch_connection(old_options)
44
+ switch_connection(old_selection)
45
45
  end
46
46
 
47
- def on_slave_if(condition, &block)
48
- condition ? on_slave(&block) : yield
49
- end
50
-
51
- def on_slave_unless(condition, &block)
52
- on_slave_if(!condition, &block)
53
- end
54
-
55
- def on_master_if(condition, &block)
56
- condition ? on_master(&block) : yield
57
- end
58
-
59
- def on_master_unless(condition, &block)
60
- on_master_if(!condition, &block)
61
- end
62
-
63
- def on_master_or_slave(which, &block)
64
- if block_given?
65
- on_cx_switch_block(which, &block)
66
- else
67
- MasterSlaveProxy.new(self, which)
68
- end
69
- end
70
-
71
- # Executes queries using the slave database. Fails over to master if no slave is found.
72
- # if you want to execute a block of code on the slave you can go:
73
- # Account.on_slave do
74
- # Account.first
75
- # end
76
- # the first account will be found on the slave DB
77
- #
78
- # For one-liners you can simply do
79
- # Account.on_slave.first
80
- def on_slave(&block)
81
- on_master_or_slave(:slave, &block)
82
- end
83
-
84
- def on_master(&block)
85
- on_master_or_slave(:master, &block)
86
- end
87
-
88
- def on_cx_switch_block(which, force: false, construct_ro_scope: nil, &block)
89
- @disallow_slave ||= 0
90
- @disallow_slave += 1 if which == :master
91
-
92
- switch_to_slave = force || @disallow_slave.zero?
93
- old_options = current_shard_selection.options
94
-
95
- switch_connection(slave: switch_to_slave)
96
-
97
- # we avoid_readonly_scope to prevent some stack overflow problems, like when
98
- # .columns calls .with_scope which calls .columns and onward, endlessly.
99
- if self == ActiveRecord::Base || !switch_to_slave || construct_ro_scope == false
100
- yield
101
- else
102
- readonly.scoping(&block)
103
- end
104
- ensure
105
- @disallow_slave -= 1 if which == :master
106
- switch_connection(old_options) if old_options
107
- end
108
-
109
- def connection_specification_name
110
- name = current_shard_selection.resolve_connection_name(sharded: is_sharded?, configurations: configurations)
111
-
112
- unless configurations[name] || name == "primary"
113
- raise ActiveRecord::AdapterNotSpecified, "No database defined by #{name} in your database config. (configurations: #{configurations.inspect})"
114
- end
115
-
116
- name
47
+ def connection_config
48
+ super.merge(current_shard_selection.connection_config.merge(sharded: is_sharded?))
117
49
  end
118
50
 
119
51
  def supports_sharding?
120
52
  shard_names.any?
121
53
  end
122
54
 
123
- def on_slave?
124
- current_shard_selection.on_slave?
55
+ def current_shard_selection
56
+ Thread.current[:shard_selection] ||= NoShardSelection.new
125
57
  end
126
58
 
127
- def current_shard_selection
128
- Thread.current[:shard_selection] ||= ShardSelection.new(shard_names.first)
59
+ def current_shard_selection=(shard_selection)
60
+ Thread.current[:shard_selection] = shard_selection
129
61
  end
130
62
 
131
63
  def current_shard_id
@@ -145,25 +77,25 @@ module ActiveRecordShards
145
77
  config[SHARD_NAMES_CONFIG_KEY]
146
78
  end
147
79
 
80
+ def table_exists_with_default_shard?
81
+ with_default_shard { table_exists_without_default_shard? }
82
+ end
83
+
148
84
  private
149
85
 
150
- def switch_connection(options)
151
- if options.any?
152
- if options.key?(:shard)
153
- unless config = configurations[shard_env]
154
- raise "Did not find #{shard_env} in configurations, did you forget to add it to your database config? (configurations: #{configurations.inspect})"
155
- end
156
- unless config['shard_names'].include?(options[:shard])
157
- raise "Did not find shard #{options[:shard]} in configurations"
158
- end
159
- current_shard_selection.shard = options[:shard]
86
+ def switch_connection(selection)
87
+ if selection.is_a?(ShardSelection)
88
+ unless config = configurations[shard_env]
89
+ raise "Did not find #{shard_env} in configurations, did you forget to add it to your database config? (configurations: #{configurations.inspect})"
160
90
  end
161
-
162
- if options.key?(:slave)
163
- current_shard_selection.on_slave = options[:slave]
91
+ unless config['shard_names'].include?(selection.shard)
92
+ raise "Did not find shard #{selection.shard} in configurations"
164
93
  end
94
+ self.current_shard_selection = selection
165
95
 
166
96
  ensure_shard_connection
97
+ else
98
+ self.current_shard_selection = selection
167
99
  end
168
100
  end
169
101
 
@@ -183,10 +115,6 @@ module ActiveRecordShards
183
115
  with_default_shard { load_schema_without_default_shard! }
184
116
  end
185
117
 
186
- def table_exists_with_default_shard?
187
- with_default_shard { table_exists_without_default_shard? }
188
- end
189
-
190
118
  class MasterSlaveProxy
191
119
  def initialize(target, which)
192
120
  @target = target
@@ -26,13 +26,13 @@ module ActiveRecordShards
26
26
  end
27
27
 
28
28
  def columns_with_force_slave(*args, &block)
29
- on_cx_switch_block(:slave, construct_ro_scope: false, force: true) do
29
+ force_cx_switch_slave_block do
30
30
  columns_without_force_slave(*args, &block)
31
31
  end
32
32
  end
33
33
 
34
34
  def table_exists_with_force_slave?(*args, &block)
35
- on_cx_switch_block(:slave, construct_ro_scope: false, force: true) do
35
+ force_cx_switch_slave_block do
36
36
  table_exists_without_force_slave?(*args, &block)
37
37
  end
38
38
  end
@@ -38,23 +38,18 @@ module ActiveRecordShards
38
38
  end
39
39
 
40
40
  module InstanceMethods
41
- def initialize_shard_and_slave
42
- @from_slave = !!self.class.current_shard_selection.options[:slave]
43
- @from_shard = self.class.current_shard_selection.options[:shard]
41
+ def initialize_slave
42
+ @from_slave = !!self.class.current_slave_selection
44
43
  end
45
44
 
46
45
  def from_slave?
47
46
  @from_slave
48
47
  end
49
-
50
- def from_shard
51
- @from_shard
52
- end
53
48
  end
54
49
 
55
50
  def self.extended(base)
56
51
  base.send(:include, InstanceMethods)
57
- base.after_initialize :initialize_shard_and_slave
52
+ base.after_initialize :initialize_slave
58
53
  end
59
54
  end
60
55
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ module ActiveRecordShards
3
+ class NoShardSelection
4
+ class NoShardSelected < RuntimeError; end
5
+
6
+ def shard
7
+ raise NoShardSelected, "Missing shard information on connection"
8
+ end
9
+
10
+ def connection_config
11
+ raise NoShardSelected, "No shard selected, can't connect"
12
+ end
13
+ end
14
+ end
@@ -1,52 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
  module ActiveRecordShards
3
3
  class ShardSelection
4
- def initialize(shard)
5
- @on_slave = false
6
- self.shard = shard
7
- end
8
-
9
- def shard
10
- raise "Missing shard information on connection" unless @shard
11
- @shard
12
- end
13
-
14
- PRIMARY = "primary".freeze
15
- def resolve_connection_name(sharded:, configurations:)
16
- resolved_shard = sharded ? shard : nil
17
-
18
- if !resolved_shard && !@on_slave
19
- return PRIMARY
20
- end
21
-
22
- @shard_names ||= {}
23
- @shard_names[ActiveRecordShards.rails_env] ||= {}
24
- @shard_names[ActiveRecordShards.rails_env][resolved_shard] ||= {}
25
- @shard_names[ActiveRecordShards.rails_env][resolved_shard][@on_slave] ||= begin
26
- s = ActiveRecordShards.rails_env.dup
27
- s << "_shard_#{resolved_shard}" if resolved_shard
4
+ attr_reader :shard
28
5
 
29
- if @on_slave && configurations["#{s}_slave"] # fall back to master connection
30
- s << "_slave"
31
- end
32
- s
33
- end
34
- end
35
-
36
- def shard=(new_shard)
37
- @shard = Integer(new_shard)
38
- end
39
-
40
- def on_slave?
41
- @on_slave
42
- end
43
-
44
- def on_slave=(new_slave)
45
- @on_slave = (new_slave == true)
6
+ def initialize(shard)
7
+ @shard = Integer(shard)
46
8
  end
47
9
 
48
- def options
49
- { shard: @shard, slave: @on_slave }
10
+ def connection_config
11
+ { shard: shard }
50
12
  end
51
13
  end
52
14
  end
@@ -5,7 +5,7 @@ module ActiveRecordShards
5
5
  include Enumerable
6
6
 
7
7
  def each(&block)
8
- ActiveRecord::Base.on_all_shards(&block)
8
+ ActiveRecordShards::ShardedModel.on_all_shards(&block)
9
9
  end
10
10
  end
11
11
 
@@ -1,9 +1,25 @@
1
1
  module ActiveRecordShards
2
2
  module Ext
3
3
  module ShardedModel
4
+ def self.extended(base)
5
+ base.extend(ActiveRecordShards::ConnectionSwitcher)
6
+ base.send(:include, InstanceMethods)
7
+ base.after_initialize :initialize_shard
8
+ end
9
+
4
10
  def is_sharded? # rubocop:disable Naming/PredicateName
5
11
  true
6
12
  end
13
+
14
+ module InstanceMethods
15
+ def initialize_shard
16
+ @from_shard = self.class.current_shard_selection.shard
17
+ end
18
+
19
+ def from_shard
20
+ @from_shard
21
+ end
22
+ end
7
23
  end
8
24
  end
9
25
 
@@ -0,0 +1,105 @@
1
+ module ActiveRecordShards
2
+ module SlaveDb
3
+ def on_slave_if(condition, &block)
4
+ condition ? on_slave(&block) : yield
5
+ end
6
+
7
+ def on_slave_unless(condition, &block)
8
+ on_slave_if(!condition, &block)
9
+ end
10
+
11
+ def on_master_if(condition, &block)
12
+ condition ? on_master(&block) : yield
13
+ end
14
+
15
+ def on_master_unless(condition, &block)
16
+ on_master_if(!condition, &block)
17
+ end
18
+
19
+ def on_master_or_slave(which, &block)
20
+ if block_given?
21
+ on_cx_switch_block(which, &block)
22
+ else
23
+ MasterSlaveProxy.new(self, which)
24
+ end
25
+ end
26
+
27
+ def on_slave?
28
+ current_slave_selection
29
+ end
30
+
31
+ # Executes queries using the slave database. Fails over to master if no slave is found.
32
+ # if you want to execute a block of code on the slave you can go:
33
+ # Account.on_slave do
34
+ # Account.first
35
+ # end
36
+ # the first account will be found on the slave DB
37
+ #
38
+ # For one-liners you can simply do
39
+ # Account.on_slave.first
40
+ def on_slave(&block)
41
+ on_master_or_slave(:slave, &block)
42
+ end
43
+
44
+ def on_master(&block)
45
+ on_master_or_slave(:master, &block)
46
+ end
47
+
48
+ def force_cx_switch_slave_block
49
+ old_options = current_slave_selection
50
+ switch_slave_connection(slave: true)
51
+ yield
52
+ ensure
53
+ switch_slave_connection(slave: old_options)
54
+ end
55
+
56
+ def on_cx_switch_block(which, &block)
57
+ @disallow_slave ||= 0
58
+ @disallow_slave += 1 if which == :master
59
+
60
+ switch_to_slave = @disallow_slave.zero?
61
+ old_options = current_slave_selection
62
+
63
+ switch_slave_connection(slave: switch_to_slave)
64
+
65
+ # we avoid_readonly_scope to prevent some stack overflow problems, like when
66
+ # .columns calls .with_scope which calls .columns and onward, endlessly.
67
+ if self == ActiveRecord::Base || !switch_to_slave
68
+ yield
69
+ else
70
+ readonly.scoping(&block)
71
+ end
72
+ ensure
73
+ @disallow_slave -= 1 if which == :master
74
+ switch_slave_connection(slave: old_options)
75
+ end
76
+
77
+ def current_slave_selection=(on_slave)
78
+ Thread.current[:slave_selection] = on_slave
79
+ end
80
+
81
+ def current_slave_selection
82
+ Thread.current[:slave_selection] ||= false
83
+ end
84
+
85
+ def connection_config
86
+ super.merge(slave: current_slave_selection)
87
+ end
88
+
89
+ def switch_slave_connection(options)
90
+ self.current_slave_selection = options[:slave]
91
+ ensure_shard_connection
92
+ end
93
+
94
+ class MasterSlaveProxy
95
+ def initialize(target, which)
96
+ @target = target
97
+ @which = which
98
+ end
99
+
100
+ def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissing
101
+ @target.on_master_or_slave(@which) { @target.send(method, *args, &block) }
102
+ end
103
+ end
104
+ end
105
+ end
@@ -3,7 +3,7 @@ module ActiveRecordShards
3
3
  module SqlComments
4
4
  module Methods
5
5
  def execute(query, name = nil)
6
- slave = ActiveRecord::Base.current_shard_selection.on_slave?
6
+ slave = ActiveRecord::Base.current_slave_selection
7
7
  query += " /* #{slave ? 'slave' : 'master'} */"
8
8
  super(query, name)
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_shards
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0.beta4
4
+ version: 4.0.0.beta5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mick Staugaard
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2017-10-31 00:00:00.000000000 Z
13
+ date: 2017-11-07 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -178,15 +178,19 @@ files:
178
178
  - README.md
179
179
  - lib/active_record_shards.rb
180
180
  - lib/active_record_shards/association_collection_connection_selection.rb
181
+ - lib/active_record_shards/base_config.rb
181
182
  - lib/active_record_shards/configuration_parser.rb
183
+ - lib/active_record_shards/connection_resolver.rb
182
184
  - lib/active_record_shards/connection_switcher-5-0.rb
183
185
  - lib/active_record_shards/connection_switcher-5-1.rb
184
186
  - lib/active_record_shards/connection_switcher.rb
185
187
  - lib/active_record_shards/default_slave_patches.rb
186
188
  - lib/active_record_shards/model.rb
189
+ - lib/active_record_shards/no_shard_selection.rb
187
190
  - lib/active_record_shards/shard_selection.rb
188
191
  - lib/active_record_shards/shard_support.rb
189
192
  - lib/active_record_shards/sharded_model.rb
193
+ - lib/active_record_shards/slave_db.rb
190
194
  - lib/active_record_shards/sql_comments.rb
191
195
  - lib/active_record_shards/tasks.rb
192
196
  homepage: https://github.com/zendesk/active_record_shards