activerecord-turntable 1.0.0
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.
- data/.document +5 -0
- data/.gitignore +25 -0
- data/.rspec +3 -0
- data/Gemfile +25 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +290 -0
- data/Rakefile +101 -0
- data/activerecord-turntable.gemspec +47 -0
- data/lib/active_record/turntable.rb +58 -0
- data/lib/active_record/turntable/active_record_ext.rb +26 -0
- data/lib/active_record/turntable/active_record_ext/.gitkeep +0 -0
- data/lib/active_record/turntable/active_record_ext/abstract_adapter.rb +50 -0
- data/lib/active_record/turntable/active_record_ext/clever_load.rb +90 -0
- data/lib/active_record/turntable/active_record_ext/fixtures.rb +131 -0
- data/lib/active_record/turntable/active_record_ext/log_subscriber.rb +64 -0
- data/lib/active_record/turntable/active_record_ext/persistence.rb +95 -0
- data/lib/active_record/turntable/active_record_ext/schema_dumper.rb +107 -0
- data/lib/active_record/turntable/active_record_ext/sequencer.rb +28 -0
- data/lib/active_record/turntable/active_record_ext/transactions.rb +33 -0
- data/lib/active_record/turntable/algorithm.rb +7 -0
- data/lib/active_record/turntable/algorithm/.gitkeep +0 -0
- data/lib/active_record/turntable/algorithm/base.rb +11 -0
- data/lib/active_record/turntable/algorithm/range_algorithm.rb +37 -0
- data/lib/active_record/turntable/algorithm/range_bsearch_algorithm.rb +41 -0
- data/lib/active_record/turntable/base.rb +130 -0
- data/lib/active_record/turntable/cluster.rb +70 -0
- data/lib/active_record/turntable/compatible.rb +19 -0
- data/lib/active_record/turntable/config.rb +24 -0
- data/lib/active_record/turntable/connection_proxy.rb +218 -0
- data/lib/active_record/turntable/connection_proxy/mixable.rb +39 -0
- data/lib/active_record/turntable/error.rb +8 -0
- data/lib/active_record/turntable/helpers.rb +5 -0
- data/lib/active_record/turntable/helpers/test_helper.rb +25 -0
- data/lib/active_record/turntable/master_shard.rb +28 -0
- data/lib/active_record/turntable/migration.rb +132 -0
- data/lib/active_record/turntable/mixer.rb +203 -0
- data/lib/active_record/turntable/mixer/fader.rb +34 -0
- data/lib/active_record/turntable/mixer/fader/calculate_shards_sum_result.rb +15 -0
- data/lib/active_record/turntable/mixer/fader/insert_shards_merge_result.rb +24 -0
- data/lib/active_record/turntable/mixer/fader/select_shards_merge_result.rb +22 -0
- data/lib/active_record/turntable/mixer/fader/specified_shard.rb +12 -0
- data/lib/active_record/turntable/mixer/fader/update_shards_merge_result.rb +17 -0
- data/lib/active_record/turntable/pool_proxy.rb +56 -0
- data/lib/active_record/turntable/rack.rb +5 -0
- data/lib/active_record/turntable/rack/connection_management.rb +18 -0
- data/lib/active_record/turntable/railtie.rb +14 -0
- data/lib/active_record/turntable/railties/databases.rake +205 -0
- data/lib/active_record/turntable/seq_shard.rb +14 -0
- data/lib/active_record/turntable/sequencer.rb +46 -0
- data/lib/active_record/turntable/sequencer/api.rb +36 -0
- data/lib/active_record/turntable/sequencer/mysql.rb +32 -0
- data/lib/active_record/turntable/shard.rb +48 -0
- data/lib/active_record/turntable/sql_tree_patch.rb +199 -0
- data/lib/active_record/turntable/version.rb +5 -0
- data/lib/activerecord-turntable.rb +2 -0
- data/lib/generators/active_record/turntable/install_generator.rb +14 -0
- data/lib/generators/templates/turntable.yml +40 -0
- data/sample_app/.gitignore +16 -0
- data/sample_app/Gemfile +41 -0
- data/sample_app/README.rdoc +261 -0
- data/sample_app/Rakefile +7 -0
- data/sample_app/app/assets/images/rails.png +0 -0
- data/sample_app/app/assets/javascripts/application.js +15 -0
- data/sample_app/app/assets/stylesheets/application.css +13 -0
- data/sample_app/app/controllers/application_controller.rb +3 -0
- data/sample_app/app/helpers/application_helper.rb +2 -0
- data/sample_app/app/mailers/.gitkeep +0 -0
- data/sample_app/app/models/.gitkeep +0 -0
- data/sample_app/app/models/user.rb +4 -0
- data/sample_app/app/views/layouts/application.html.erb +14 -0
- data/sample_app/config.ru +4 -0
- data/sample_app/config/application.rb +65 -0
- data/sample_app/config/boot.rb +6 -0
- data/sample_app/config/database.yml +70 -0
- data/sample_app/config/environment.rb +5 -0
- data/sample_app/config/environments/development.rb +37 -0
- data/sample_app/config/environments/production.rb +67 -0
- data/sample_app/config/environments/test.rb +37 -0
- data/sample_app/config/initializers/backtrace_silencers.rb +7 -0
- data/sample_app/config/initializers/inflections.rb +15 -0
- data/sample_app/config/initializers/mime_types.rb +5 -0
- data/sample_app/config/initializers/secret_token.rb +7 -0
- data/sample_app/config/initializers/session_store.rb +8 -0
- data/sample_app/config/initializers/wrap_parameters.rb +14 -0
- data/sample_app/config/locales/en.yml +5 -0
- data/sample_app/config/routes.rb +58 -0
- data/sample_app/config/turntable.yml +64 -0
- data/sample_app/db/migrate/20120316073058_create_users.rb +11 -0
- data/sample_app/db/seeds.rb +7 -0
- data/sample_app/lib/assets/.gitkeep +0 -0
- data/sample_app/lib/tasks/.gitkeep +0 -0
- data/sample_app/log/.gitkeep +0 -0
- data/sample_app/public/404.html +26 -0
- data/sample_app/public/422.html +26 -0
- data/sample_app/public/500.html +25 -0
- data/sample_app/public/favicon.ico +0 -0
- data/sample_app/public/index.html +241 -0
- data/sample_app/public/robots.txt +5 -0
- data/sample_app/script/rails +6 -0
- data/sample_app/vendor/assets/javascripts/.gitkeep +0 -0
- data/sample_app/vendor/assets/stylesheets/.gitkeep +0 -0
- data/sample_app/vendor/plugins/.gitkeep +0 -0
- data/script/performance/algorithm +32 -0
- data/spec/active_record/turntable/active_record_ext/clever_load_spec.rb +81 -0
- data/spec/active_record/turntable/active_record_ext/persistence_spec.rb +151 -0
- data/spec/active_record/turntable/algorithm/range_algorithm_spec.rb +35 -0
- data/spec/active_record/turntable/algorithm_spec.rb +69 -0
- data/spec/active_record/turntable/base_spec.rb +13 -0
- data/spec/active_record/turntable/cluster_spec.rb +18 -0
- data/spec/active_record/turntable/config_spec.rb +17 -0
- data/spec/active_record/turntable/connection_proxy_spec.rb +186 -0
- data/spec/active_record/turntable/finder_spec.rb +27 -0
- data/spec/active_record/turntable/mixer/fader_spec.rb +4 -0
- data/spec/active_record/turntable/mixer_spec.rb +114 -0
- data/spec/active_record/turntable/shard_spec.rb +21 -0
- data/spec/active_record/turntable_spec.rb +30 -0
- data/spec/config/database.yml +45 -0
- data/spec/config/turntable.yml +17 -0
- data/spec/fabricators/.gitkeep +0 -0
- data/spec/fabricators/turntable_fabricator.rb +14 -0
- data/spec/migrations/.gitkeep +0 -0
- data/spec/migrations/001_create_users.rb +16 -0
- data/spec/migrations/002_create_user_statuses.rb +16 -0
- data/spec/migrations/003_create_cards.rb +14 -0
- data/spec/migrations/004_create_cards_users.rb +15 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/test_models.rb +27 -0
- data/spec/turntable_helper.rb +29 -0
- metadata +367 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module ActiveRecord::Turntable
|
|
2
|
+
class Cluster
|
|
3
|
+
|
|
4
|
+
DEFAULT_CONFIG = {
|
|
5
|
+
"shards" => [],
|
|
6
|
+
"algorithm" => "range",
|
|
7
|
+
}.with_indifferent_access
|
|
8
|
+
|
|
9
|
+
def initialize(klass, cluster_spec, options = {})
|
|
10
|
+
@klass = klass
|
|
11
|
+
@config = DEFAULT_CONFIG.merge(cluster_spec)
|
|
12
|
+
@options = options.with_indifferent_access
|
|
13
|
+
@shards = {}.with_indifferent_access
|
|
14
|
+
|
|
15
|
+
# setup master shard
|
|
16
|
+
@master_shard = MasterShard.new(klass)
|
|
17
|
+
|
|
18
|
+
# setup sequencer
|
|
19
|
+
if (seq = (@options[:seq] || @config[:seq]))
|
|
20
|
+
@seq_shard = SeqShard.new(seq)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# setup shards
|
|
24
|
+
@config[:shards].each do |spec|
|
|
25
|
+
@shards[spec["connection"]] ||= Shard.new(spec)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# setup algorithm
|
|
29
|
+
alg_name = "ActiveRecord::Turntable::Algorithm::#{@config["algorithm"].camelize}Algorithm"
|
|
30
|
+
@algorithm = alg_name.constantize.new(@config)
|
|
31
|
+
|
|
32
|
+
# setup proxy
|
|
33
|
+
@connection_proxy = ConnectionProxy.new(self, cluster_spec)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def klass
|
|
37
|
+
@klass
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def master
|
|
41
|
+
@master_shard
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def seq
|
|
45
|
+
@seq_shard || @master_shard
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def shards
|
|
49
|
+
@shards
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def connection_proxy
|
|
53
|
+
@connection_proxy
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def select_shard(key)
|
|
57
|
+
@shards[@algorithm.calculate(key)]
|
|
58
|
+
rescue
|
|
59
|
+
raise ActiveRecord::Turntable::CannotSpecifyShardError,
|
|
60
|
+
"[#{klass}] cannot select_shard for key:#{key}"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def weighted_shards(key = nil)
|
|
64
|
+
key ||= @klass.current_sequence
|
|
65
|
+
Hash[@algorithm.calculate_used_shards_with_weight(key).map do |k,v|
|
|
66
|
+
[@shards[k], v]
|
|
67
|
+
end]
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#
|
|
2
|
+
#=ActiveRecord::Turntable::Compatible
|
|
3
|
+
#
|
|
4
|
+
# for ActiveRecord versions compatibility
|
|
5
|
+
#
|
|
6
|
+
module ActiveRecord::Turntable
|
|
7
|
+
module Compatible
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
# class_attributes
|
|
12
|
+
unless respond_to?(:class_attribute)
|
|
13
|
+
class << self
|
|
14
|
+
alias_method :class_attribute, :class_inheritable_accessor
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
|
2
|
+
|
|
3
|
+
module ActiveRecord::Turntable
|
|
4
|
+
class Config
|
|
5
|
+
include Singleton
|
|
6
|
+
|
|
7
|
+
def self.[](key)
|
|
8
|
+
instance[key]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def [](key)
|
|
12
|
+
self.class.load!(ActiveRecord::Base.turntable_config_file) unless @config
|
|
13
|
+
@config[key]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.load!(config_file, env = (defined?(Rails) ? Rails.env : 'development'))
|
|
17
|
+
instance.load!(config_file, env)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def load!(config_file, env)
|
|
21
|
+
@config = YAML.load_file(config_file).with_indifferent_access[env]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
require 'active_record/turntable/connection_proxy/mixable'
|
|
2
|
+
module ActiveRecord::Turntable
|
|
3
|
+
class ConnectionProxy
|
|
4
|
+
include Mixable
|
|
5
|
+
|
|
6
|
+
attr_writer :spec
|
|
7
|
+
|
|
8
|
+
def initialize(cluster, options = {})
|
|
9
|
+
@cluster = cluster
|
|
10
|
+
@model_class = cluster.klass
|
|
11
|
+
@current_shard = (cluster.master || cluster.shards.first[1])
|
|
12
|
+
@fixed_shard = false
|
|
13
|
+
@mixer = ActiveRecord::Turntable::Mixer.new(self)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
delegate :logger, :to => ActiveRecord::Base
|
|
17
|
+
|
|
18
|
+
delegate :create_table, :rename_table, :drop_table, :add_column, :remove_colomn,
|
|
19
|
+
:change_column, :change_column_default, :rename_column, :add_index,
|
|
20
|
+
:remove_index, :initialize_schema_information,
|
|
21
|
+
:dump_schema_information, :execute_ignore_duplicate, :to => :master_connection
|
|
22
|
+
|
|
23
|
+
# delegate :insert_many, :to => :master # ar-extensions bulk insert support
|
|
24
|
+
|
|
25
|
+
def transaction(options = {}, &block)
|
|
26
|
+
connection.transaction(options, &block)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def shards_transaction(shards, options = {}, in_recursion = false, &block)
|
|
30
|
+
shards = in_recursion ? shards : Array.wrap(shards).dup
|
|
31
|
+
shard_or_object = shards.shift
|
|
32
|
+
shard = to_shard(shard_or_object)
|
|
33
|
+
if shards.present?
|
|
34
|
+
shard.connection.transaction(options) do
|
|
35
|
+
shards_transaction(shards, options, true, &block)
|
|
36
|
+
end
|
|
37
|
+
else
|
|
38
|
+
shard.connection.transaction(options) do
|
|
39
|
+
block.call
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def to_shard(shard_or_object)
|
|
45
|
+
case shard_or_object
|
|
46
|
+
when ActiveRecord::Turntable::Shard
|
|
47
|
+
shard_or_object
|
|
48
|
+
when ActiveRecord::Base
|
|
49
|
+
shard_or_object.turntable_shard
|
|
50
|
+
else
|
|
51
|
+
raise ActiveRecord::Turntable::Error,
|
|
52
|
+
"transaction cannot call to object: #{shard_or_object}"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def method_missing(method, *args, &block)
|
|
57
|
+
if shard_fixed?
|
|
58
|
+
connection.send(method, *args, &block)
|
|
59
|
+
elsif mixable?(method, *args)
|
|
60
|
+
fader = @mixer.build_fader(method, *args, &block)
|
|
61
|
+
logger.debug { "[ActiveRecord::Turntable] Sending method: #{method}, " +
|
|
62
|
+
"sql: #{args.first}, " +
|
|
63
|
+
"shards: #{fader.shards_query_hash.keys.map(&:name)}" }
|
|
64
|
+
fader.execute
|
|
65
|
+
else
|
|
66
|
+
connection.send(method, *args, &block)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# for 3.2.2
|
|
71
|
+
def to_sql(arel, binds = [])
|
|
72
|
+
if master.connection.method(:to_sql).arity < 0
|
|
73
|
+
master.connection.to_sql(arel, binds)
|
|
74
|
+
else
|
|
75
|
+
master.connection.to_sql(arel)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def cluster
|
|
80
|
+
@cluster
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def shards
|
|
84
|
+
@cluster.shards
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def shard_fixed?
|
|
88
|
+
!!fixed_shard
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def fixed_shard
|
|
92
|
+
@fixed_shard
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def master
|
|
96
|
+
@cluster.master
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def master_connection
|
|
100
|
+
master.connection
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def seq
|
|
104
|
+
@cluster.seq
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def current_shard
|
|
108
|
+
@current_shard
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def current_shard=(shard)
|
|
112
|
+
logger.debug { "Chainging #{@model_class}'s shard to #{shard.name}"}
|
|
113
|
+
@current_shard = shard
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def connection
|
|
117
|
+
@current_shard.connection
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def connection_pool
|
|
121
|
+
@current_shard.connection_pool
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def with_shard(shard)
|
|
125
|
+
old_shard, old_fixed = current_shard, fixed_shard
|
|
126
|
+
self.current_shard = shard
|
|
127
|
+
@fixed_shard = shard
|
|
128
|
+
yield
|
|
129
|
+
ensure
|
|
130
|
+
@fixed_shard = old_fixed
|
|
131
|
+
self.current_shard = old_shard
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def with_recursive_shards(connection_name, *klasses, &block)
|
|
135
|
+
with_shard(shards[connection_name]) do
|
|
136
|
+
if klasses.blank?
|
|
137
|
+
yield
|
|
138
|
+
else
|
|
139
|
+
current_klass = klasses.shift
|
|
140
|
+
current_klass.connection.with_recursive_shards(connection_name, *klasses, &block)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def with_all(continue_on_error = false)
|
|
146
|
+
@cluster.shards.values.map do |shard|
|
|
147
|
+
begin
|
|
148
|
+
with_shard(shard) {
|
|
149
|
+
yield
|
|
150
|
+
}
|
|
151
|
+
rescue Exception => err
|
|
152
|
+
unless continue_on_error
|
|
153
|
+
raise err
|
|
154
|
+
end
|
|
155
|
+
err
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def with_master(&block)
|
|
161
|
+
with_shard(@cluster.master) do
|
|
162
|
+
yield
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def connected?
|
|
167
|
+
connection_pool.connected?
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
if ActiveRecord::VERSION::STRING > '3.1'
|
|
171
|
+
%w(columns columns_hash column_defaults primary_keys).each do |name|
|
|
172
|
+
define_method(name.to_sym) do
|
|
173
|
+
master.connection_pool.send(name.to_sym)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
if ActiveRecord::VERSION::STRING < '3.2'
|
|
178
|
+
%w(table_exists?).each do |name|
|
|
179
|
+
define_method(name.to_sym) do |*args|
|
|
180
|
+
master.connection_pool.send(name.to_sym, *args)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
else
|
|
184
|
+
%w(table_exists?).each do |name|
|
|
185
|
+
define_method(name.to_sym) do |*args|
|
|
186
|
+
master.connection_pool.with_connection do |c|
|
|
187
|
+
c.schema_cache.send(name.to_sym, *args)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def columns(*args)
|
|
194
|
+
if args.size > 0
|
|
195
|
+
master.connection_pool.columns[*args]
|
|
196
|
+
else
|
|
197
|
+
master.connection_pool.columns
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def pk_and_sequence_for(*args)
|
|
203
|
+
master.connection.send(:pk_and_sequence_for, *args)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def primary_key(*args)
|
|
207
|
+
master.connection.send(:primary_key, *args)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def supports_views?(*args)
|
|
211
|
+
master.connection.send(:supports_views?, *args)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def spec
|
|
215
|
+
@spec ||= master.connection_pool.spec
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module ActiveRecord::Turntable
|
|
2
|
+
class ConnectionProxy
|
|
3
|
+
module Mixable
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
if ActiveRecord::VERSION::STRING < '3.1'
|
|
8
|
+
include Rails30
|
|
9
|
+
else
|
|
10
|
+
include Rails3x
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module Rails3x
|
|
15
|
+
METHODS_REGEXP = /\A(insert|select|update|delete|exec_)/
|
|
16
|
+
EXCLUDE_QUERY_REGEXP = /\A\s*SHOW/i
|
|
17
|
+
QUERY_REGEXP = /\A\s*(INSERT|DELETE|UPDATE|SELECT)/i
|
|
18
|
+
|
|
19
|
+
def mixable?(method, *args)
|
|
20
|
+
(method.to_s =~ METHODS_REGEXP &&
|
|
21
|
+
args.first !~ EXCLUDE_QUERY_REGEXP) ||
|
|
22
|
+
(method.to_s == 'execute' && args.first =~ QUERY_REGEXP)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
module Rails30
|
|
27
|
+
METHODS_REGEXP = /\A(insert|select|update|delete)/
|
|
28
|
+
EXCLUDE_QUERY_REGEXP = /\A\s*SHOW/i
|
|
29
|
+
QUERY_REGEXP = /\A\s*(INSERT|DELETE|UPDATE|SELECT)/i
|
|
30
|
+
|
|
31
|
+
def mixable?(method, *args)
|
|
32
|
+
(method.to_s =~ METHODS_REGEXP &&
|
|
33
|
+
args.first !~ EXCLUDE_QUERY_REGEXP) ||
|
|
34
|
+
(method.to_s == 'execute' && args.first =~ QUERY_REGEXP)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
module ActiveRecord::Turntable
|
|
2
|
+
class Error < StandardError; end
|
|
3
|
+
class NotImplementedError < Error; end
|
|
4
|
+
class SequenceNotFoundError < Error; end
|
|
5
|
+
class CannotSpecifyShardError < Error; end
|
|
6
|
+
class MasterShardNotConnected < Error; end
|
|
7
|
+
class UnknownOperatorError < Error; end
|
|
8
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module ActiveRecord::Turntable
|
|
2
|
+
module Helpers
|
|
3
|
+
module TestHelper
|
|
4
|
+
# all shards
|
|
5
|
+
def FabricateAll(name, overrides={}, &block)
|
|
6
|
+
obj = Fabrication::Fabricator.generate(name, {
|
|
7
|
+
:save => true
|
|
8
|
+
}, overrides, &block)
|
|
9
|
+
|
|
10
|
+
default_pool = obj.class.connection_pool
|
|
11
|
+
connection_pools = obj.class.connection_handler.instance_variable_get(:@connection_pools)
|
|
12
|
+
|
|
13
|
+
ActiveRecord::Base.turntable_connections.each do |conn_name, conn|
|
|
14
|
+
new_obj = obj.dup
|
|
15
|
+
connection_pools[new_obj.class.name] = conn
|
|
16
|
+
new_obj.id = obj.id
|
|
17
|
+
new_obj.send(:create)
|
|
18
|
+
end
|
|
19
|
+
obj
|
|
20
|
+
ensure
|
|
21
|
+
connection_pools[obj.class.name] = default_pool
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module ActiveRecord::Turntable
|
|
2
|
+
class MasterShard < Shard
|
|
3
|
+
def initialize(klass)
|
|
4
|
+
(klass and klass.connection_pool) or
|
|
5
|
+
raise MasterShardNotConnected, "connection_pool is nil"
|
|
6
|
+
@klass = klass
|
|
7
|
+
@name = 'master'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def connection_pool
|
|
11
|
+
if ActiveRecord::Base == @klass
|
|
12
|
+
ActiveRecord::Base.connection_pool
|
|
13
|
+
else
|
|
14
|
+
# use parentclass connection which is turntable disabled
|
|
15
|
+
klass = @klass.superclass
|
|
16
|
+
candidate_connection_pool = nil
|
|
17
|
+
while !candidate_connection_pool
|
|
18
|
+
if klass == ActiveRecord::Base or !klass.turntable_enabled?
|
|
19
|
+
candidate_connection_pool = klass.connection_pool
|
|
20
|
+
else
|
|
21
|
+
klass = klass.superclass
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
candidate_connection_pool
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|