activerecord-turntable 1.1.2 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -0
  3. data/.travis.yml +3 -5
  4. data/CHANGELOG.md +70 -0
  5. data/Guardfile +7 -5
  6. data/README.md +490 -0
  7. data/Rakefile +37 -22
  8. data/activerecord-turntable.gemspec +37 -34
  9. data/gemfiles/rails4_0.gemfile +6 -0
  10. data/gemfiles/rails4_1.gemfile +6 -0
  11. data/lib/active_record/turntable/active_record_ext/abstract_adapter.rb +14 -29
  12. data/lib/active_record/turntable/active_record_ext/activerecord_import_ext.rb +45 -0
  13. data/lib/active_record/turntable/active_record_ext/acts_as_archive_extension.rb +21 -0
  14. data/lib/active_record/turntable/active_record_ext/association.rb +85 -0
  15. data/lib/active_record/turntable/active_record_ext/association_preloader.rb +37 -0
  16. data/lib/active_record/turntable/active_record_ext/clever_load.rb +33 -76
  17. data/lib/active_record/turntable/active_record_ext/connection_handler_extension.rb +31 -0
  18. data/lib/active_record/turntable/active_record_ext/database_tasks.rb +81 -0
  19. data/lib/active_record/turntable/active_record_ext/fixtures.rb +54 -42
  20. data/lib/active_record/turntable/active_record_ext/locking_optimistic.rb +101 -0
  21. data/lib/active_record/turntable/active_record_ext/log_subscriber.rb +28 -46
  22. data/lib/active_record/turntable/active_record_ext/migration_proxy.rb +7 -0
  23. data/lib/active_record/turntable/active_record_ext/persistence.rb +96 -94
  24. data/lib/active_record/turntable/active_record_ext/relation.rb +31 -0
  25. data/lib/active_record/turntable/active_record_ext/schema_dumper.rb +18 -28
  26. data/lib/active_record/turntable/active_record_ext/transactions.rb +9 -3
  27. data/lib/active_record/turntable/active_record_ext.rb +26 -11
  28. data/lib/active_record/turntable/algorithm/base.rb +1 -1
  29. data/lib/active_record/turntable/algorithm/range_bsearch_algorithm.rb +1 -1
  30. data/lib/active_record/turntable/algorithm.rb +7 -3
  31. data/lib/active_record/turntable/base.rb +67 -14
  32. data/lib/active_record/turntable/cluster.rb +46 -2
  33. data/lib/active_record/turntable/config.rb +1 -1
  34. data/lib/active_record/turntable/connection_proxy/mixable.rb +7 -29
  35. data/lib/active_record/turntable/connection_proxy.rb +61 -72
  36. data/lib/active_record/turntable/error.rb +5 -6
  37. data/lib/active_record/turntable/helpers.rb +5 -1
  38. data/lib/active_record/turntable/migration.rb +9 -49
  39. data/lib/active_record/turntable/mixer/fader/calculate_shards_sum_result.rb +13 -2
  40. data/lib/active_record/turntable/mixer/fader/select_shards_merge_result.rb +17 -6
  41. data/lib/active_record/turntable/mixer/fader/specified_shard.rb +3 -1
  42. data/lib/active_record/turntable/mixer/fader.rb +12 -10
  43. data/lib/active_record/turntable/mixer.rb +59 -29
  44. data/lib/active_record/turntable/plugin.rb +6 -0
  45. data/lib/active_record/turntable/pool_proxy.rb +12 -19
  46. data/lib/active_record/turntable/rack/query_cache.rb +20 -23
  47. data/lib/active_record/turntable/rack.rb +4 -2
  48. data/lib/active_record/turntable/railtie.rb +4 -3
  49. data/lib/active_record/turntable/railties/databases.rake +81 -122
  50. data/lib/active_record/turntable/seq_shard.rb +1 -1
  51. data/lib/active_record/turntable/sequencer/api.rb +1 -1
  52. data/lib/active_record/turntable/sequencer/barrage.rb +28 -0
  53. data/lib/active_record/turntable/sequencer.rb +27 -9
  54. data/lib/active_record/turntable/shard.rb +2 -2
  55. data/lib/active_record/turntable/sql_tree_patch.rb +1 -1
  56. data/lib/active_record/turntable/version.rb +1 -1
  57. data/lib/active_record/turntable.rb +26 -16
  58. data/lib/generators/templates/turntable.yml +9 -7
  59. data/spec/active_record/turntable/active_record_ext/association_preloader_spec.rb +78 -0
  60. data/spec/active_record/turntable/active_record_ext/association_spec.rb +72 -0
  61. data/spec/active_record/turntable/active_record_ext/clever_load_spec.rb +25 -46
  62. data/spec/active_record/turntable/active_record_ext/locking_optimistic_spec.rb +28 -0
  63. data/spec/active_record/turntable/active_record_ext/persistence_spec.rb +46 -25
  64. data/spec/active_record/turntable/algorithm/range_algorithm_spec.rb +4 -4
  65. data/spec/active_record/turntable/algorithm/range_bsearch_algorithm_spec.rb +35 -0
  66. data/spec/active_record/turntable/algorithm_spec.rb +28 -12
  67. data/spec/active_record/turntable/base_spec.rb +1 -1
  68. data/spec/active_record/turntable/cluster_spec.rb +27 -5
  69. data/spec/active_record/turntable/config_spec.rb +2 -2
  70. data/spec/active_record/turntable/connection_proxy_spec.rb +112 -45
  71. data/spec/active_record/turntable/finder_spec.rb +24 -11
  72. data/spec/active_record/turntable/mixer_spec.rb +21 -21
  73. data/spec/active_record/turntable/rack/query_cache_spec.rb +19 -0
  74. data/spec/active_record/turntable/sequencer/api_spec.rb +38 -0
  75. data/spec/active_record/turntable/sequencer/barrage_spec.rb +22 -0
  76. data/spec/active_record/turntable/sequencer/mysql_spec.rb +22 -0
  77. data/spec/active_record/turntable/shard_spec.rb +1 -1
  78. data/spec/active_record/turntable/transaction_spec.rb +35 -0
  79. data/spec/active_record/turntable_spec.rb +4 -4
  80. data/spec/config/database.yml +24 -34
  81. data/spec/config/turntable.yml +18 -1
  82. data/spec/fabricators/turntable_fabricator.rb +0 -2
  83. data/spec/models/card.rb +3 -0
  84. data/spec/models/cards_user.rb +10 -0
  85. data/spec/models/cards_users_histories.rb +7 -0
  86. data/spec/models/events_users_history.rb +7 -0
  87. data/spec/models/user.rb +7 -0
  88. data/spec/models/user_status.rb +6 -0
  89. data/spec/spec_helper.rb +10 -4
  90. data/spec/support/matchers/be_saved_to.rb +6 -0
  91. data/spec/support/turntable_helper.rb +29 -0
  92. metadata +124 -74
  93. data/README.rdoc +0 -294
  94. data/gemfiles/rails3_0.gemfile +0 -7
  95. data/gemfiles/rails3_1.gemfile +0 -6
  96. data/gemfiles/rails3_2.gemfile +0 -6
  97. data/lib/active_record/turntable/compatible.rb +0 -19
  98. data/sample_app/.gitignore +0 -16
  99. data/sample_app/Gemfile +0 -41
  100. data/sample_app/README.rdoc +0 -261
  101. data/sample_app/Rakefile +0 -7
  102. data/sample_app/app/assets/images/rails.png +0 -0
  103. data/sample_app/app/assets/javascripts/application.js +0 -15
  104. data/sample_app/app/assets/stylesheets/application.css +0 -13
  105. data/sample_app/app/controllers/application_controller.rb +0 -3
  106. data/sample_app/app/helpers/application_helper.rb +0 -2
  107. data/sample_app/app/mailers/.gitkeep +0 -0
  108. data/sample_app/app/models/.gitkeep +0 -0
  109. data/sample_app/app/models/user.rb +0 -4
  110. data/sample_app/app/views/layouts/application.html.erb +0 -14
  111. data/sample_app/config/application.rb +0 -65
  112. data/sample_app/config/boot.rb +0 -6
  113. data/sample_app/config/database.yml +0 -70
  114. data/sample_app/config/environment.rb +0 -5
  115. data/sample_app/config/environments/development.rb +0 -37
  116. data/sample_app/config/environments/production.rb +0 -67
  117. data/sample_app/config/environments/test.rb +0 -37
  118. data/sample_app/config/initializers/backtrace_silencers.rb +0 -7
  119. data/sample_app/config/initializers/inflections.rb +0 -15
  120. data/sample_app/config/initializers/mime_types.rb +0 -5
  121. data/sample_app/config/initializers/secret_token.rb +0 -7
  122. data/sample_app/config/initializers/session_store.rb +0 -8
  123. data/sample_app/config/initializers/wrap_parameters.rb +0 -14
  124. data/sample_app/config/locales/en.yml +0 -5
  125. data/sample_app/config/routes.rb +0 -58
  126. data/sample_app/config/turntable.yml +0 -64
  127. data/sample_app/config.ru +0 -4
  128. data/sample_app/db/migrate/20120316073058_create_users.rb +0 -11
  129. data/sample_app/db/seeds.rb +0 -7
  130. data/sample_app/lib/assets/.gitkeep +0 -0
  131. data/sample_app/lib/tasks/.gitkeep +0 -0
  132. data/sample_app/log/.gitkeep +0 -0
  133. data/sample_app/public/404.html +0 -26
  134. data/sample_app/public/422.html +0 -26
  135. data/sample_app/public/500.html +0 -25
  136. data/sample_app/public/favicon.ico +0 -0
  137. data/sample_app/public/index.html +0 -241
  138. data/sample_app/public/robots.txt +0 -5
  139. data/sample_app/script/rails +0 -6
  140. data/sample_app/vendor/assets/javascripts/.gitkeep +0 -0
  141. data/sample_app/vendor/assets/stylesheets/.gitkeep +0 -0
  142. data/sample_app/vendor/plugins/.gitkeep +0 -0
  143. data/spec/test_models.rb +0 -27
  144. data/spec/turntable_helper.rb +0 -29
@@ -11,49 +11,23 @@ module ActiveRecord::Turntable
11
11
  def initialize(cluster, options = {})
12
12
  @cluster = cluster
13
13
  @model_class = cluster.klass
14
- @current_shard = (cluster.master || cluster.shards.first[1])
15
- @fixed_shard = false
14
+ @default_current_shard = (cluster.master || cluster.shards.first[1])
16
15
  @mixer = ActiveRecord::Turntable::Mixer.new(self)
17
16
  end
18
17
 
19
- delegate :logger, :to => ActiveRecord::Base
18
+ delegate :logger, to: ActiveRecord::Base
19
+
20
+ delegate :shards_transaction, to: :cluster
20
21
 
21
22
  delegate :create_table, :rename_table, :drop_table, :add_column, :remove_colomn,
22
23
  :change_column, :change_column_default, :rename_column, :add_index,
23
24
  :remove_index, :initialize_schema_information,
24
- :dump_schema_information, :execute_ignore_duplicate, :to => :master_connection
25
+ :dump_schema_information, :execute_ignore_duplicate, to: :master_connection
25
26
 
26
27
  def transaction(options = {}, &block)
27
28
  connection.transaction(options, &block)
28
29
  end
29
30
 
30
- def shards_transaction(shards, options = {}, in_recursion = false, &block)
31
- shards = in_recursion ? shards : Array.wrap(shards).dup
32
- shard_or_object = shards.shift
33
- shard = to_shard(shard_or_object)
34
- if shards.present?
35
- shard.connection.transaction(options) do
36
- shards_transaction(shards, options, true, &block)
37
- end
38
- else
39
- shard.connection.transaction(options) do
40
- block.call
41
- end
42
- end
43
- end
44
-
45
- def to_shard(shard_or_object)
46
- case shard_or_object
47
- when ActiveRecord::Turntable::Shard
48
- shard_or_object
49
- when ActiveRecord::Base
50
- shard_or_object.turntable_shard
51
- else
52
- raise ActiveRecord::Turntable::Error,
53
- "transaction cannot call to object: #{shard_or_object}"
54
- end
55
- end
56
-
57
31
  def cache
58
32
  enable_query_cache!
59
33
  yield
@@ -92,13 +66,8 @@ module ActiveRecord::Turntable
92
66
  end
93
67
  end
94
68
 
95
- # for 3.2.2
96
69
  def to_sql(arel, binds = [])
97
- if master.connection.method(:to_sql).arity < 0
98
- master.connection.to_sql(arel, binds)
99
- else
100
- master.connection.to_sql(arel)
101
- end
70
+ master.connection.to_sql(arel, binds)
102
71
  end
103
72
 
104
73
  def cluster
@@ -114,7 +83,11 @@ module ActiveRecord::Turntable
114
83
  end
115
84
 
116
85
  def fixed_shard
117
- @fixed_shard
86
+ fixed_shard_entry[object_id]
87
+ end
88
+
89
+ def fixed_shard=(shard)
90
+ fixed_shard_entry[object_id] = shard
118
91
  end
119
92
 
120
93
  def master
@@ -130,29 +103,31 @@ module ActiveRecord::Turntable
130
103
  end
131
104
 
132
105
  def current_shard
133
- @current_shard
106
+ current_shard_entry[object_id] ||= @default_current_shard
134
107
  end
135
108
 
136
109
  def current_shard=(shard)
137
- logger.debug { "Chainging #{@model_class}'s shard to #{shard.name}"}
138
- @current_shard = shard
110
+ logger.debug { "Changing #{@model_class}'s shard to #{shard.name}"}
111
+ current_shard_entry[object_id] = shard
139
112
  end
140
113
 
141
114
  def connection
142
- @current_shard.connection
115
+ current_shard.connection
143
116
  end
144
117
 
145
118
  def connection_pool
146
- @current_shard.connection_pool
119
+ current_shard.connection_pool
147
120
  end
148
121
 
149
122
  def with_shard(shard)
123
+ shard = cluster.to_shard(shard)
124
+
150
125
  old_shard, old_fixed = current_shard, fixed_shard
151
126
  self.current_shard = shard
152
- @fixed_shard = shard
127
+ self.fixed_shard = shard
153
128
  yield
154
129
  ensure
155
- @fixed_shard = old_fixed
130
+ self.fixed_shard = old_fixed
156
131
  self.current_shard = old_shard
157
132
  end
158
133
 
@@ -182,45 +157,49 @@ module ActiveRecord::Turntable
182
157
  end
183
158
  end
184
159
 
160
+ def with_master_and_all(continue_on_error = false)
161
+ ([@cluster.master] + @cluster.shards.values).map do |shard|
162
+ begin
163
+ with_shard(shard) {
164
+ yield
165
+ }
166
+ rescue Exception => err
167
+ unless continue_on_error
168
+ raise err
169
+ end
170
+ err
171
+ end
172
+ end
173
+ end
174
+
185
175
  def with_master(&block)
186
176
  with_shard(@cluster.master) do
187
177
  yield
188
178
  end
189
179
  end
190
180
 
191
- def connected?
192
- connection_pool.connected?
193
- end
181
+ delegate :connected?, :automatic_reconnect, :automatic_reconnect=, :checkout_timeout, :dead_connection_timeout,
182
+ :spec, :connections, :size, :reaper, :table_exists?, to: :connection_pool
194
183
 
195
- if ActiveRecord::VERSION::STRING > '3.1'
196
- %w(columns columns_hash column_defaults primary_keys).each do |name|
197
- define_method(name.to_sym) do
198
- master.connection_pool.send(name.to_sym)
199
- end
184
+ %w(columns columns_hash column_defaults primary_keys).each do |name|
185
+ define_method(name.to_sym) do
186
+ master.connection_pool.send(name.to_sym)
200
187
  end
188
+ end
201
189
 
202
- if ActiveRecord::VERSION::STRING < '3.2'
203
- %w(table_exists?).each do |name|
204
- define_method(name.to_sym) do |*args|
205
- master.connection_pool.send(name.to_sym, *args)
206
- end
207
- end
208
- else
209
- %w(table_exists?).each do |name|
210
- define_method(name.to_sym) do |*args|
211
- master.connection_pool.with_connection do |c|
212
- c.schema_cache.send(name.to_sym, *args)
213
- end
214
- end
190
+ %w(table_exists?).each do |name|
191
+ define_method(name.to_sym) do |*args|
192
+ master.connection_pool.with_connection do |c|
193
+ c.schema_cache.send(name.to_sym, *args)
215
194
  end
216
195
  end
196
+ end
217
197
 
218
- def columns(*args)
219
- if args.size > 0
220
- master.connection_pool.columns[*args]
221
- else
222
- master.connection_pool.columns
223
- end
198
+ def columns(*args)
199
+ if args.size > 0
200
+ master.connection_pool.columns[*args]
201
+ else
202
+ master.connection_pool.columns
224
203
  end
225
204
  end
226
205
 
@@ -239,5 +218,15 @@ module ActiveRecord::Turntable
239
218
  def spec
240
219
  @spec ||= master.connection_pool.spec
241
220
  end
221
+
222
+ private
223
+
224
+ def fixed_shard_entry
225
+ Thread.current[:turntable_fixed_shard] ||= ThreadSafe::Cache.new
226
+ end
227
+
228
+ def current_shard_entry
229
+ Thread.current[:turntable_current_shard] ||= ThreadSafe::Cache.new
230
+ end
242
231
  end
243
232
  end
@@ -1,8 +1,7 @@
1
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
2
+ class TurntableError < StandardError; end
3
+ class SequenceNotFoundError < TurntableError; end
4
+ class CannotSpecifyShardError < TurntableError; end
5
+ class MasterShardNotConnected < TurntableError; end
6
+ class UnknownOperatorError < TurntableError; end
8
7
  end
@@ -1,5 +1,9 @@
1
1
  module ActiveRecord::Turntable
2
2
  module Helpers
3
- autoload :TestHelper, 'active_record/turntable/helpers/test_helper'
3
+ extend ActiveSupport::Autoload
4
+
5
+ eager_autoload do
6
+ autoload :TestHelper
7
+ end
4
8
  end
5
9
  end
@@ -1,38 +1,15 @@
1
1
  module ActiveRecord::Turntable::Migration
2
2
  extend ActiveSupport::Concern
3
3
 
4
- # AR < 3.1
5
- def self.extended(base)
6
- class << base
7
- def announce_with_turntable(message)
8
- announce_without_turntable("#{message} - #{get_current_shard}")
9
- end
10
-
11
- alias_method_chain :migrate, :turntable
12
- alias_method_chain :announce, :turntable
13
- include ShardDefinition
14
- end
15
- base.class_eval do
16
- class_inheritable_accessor :target_shards
17
- end
18
- ::ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, SchemaStatementsExt)
19
- end
20
-
21
- # AR >= 3.1
22
4
  included do
23
5
  extend ShardDefinition
24
- class_attribute :target_shards
25
- def announce_with_turntable(message)
26
- announce_without_turntable("#{message} - #{get_current_shard}")
27
- end
28
-
29
- alias_method_chain :migrate, :turntable
6
+ class_attribute :target_shards, :current_shard
30
7
  alias_method_chain :announce, :turntable
8
+ alias_method_chain :exec_migration, :turntable
31
9
  ::ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, SchemaStatementsExt)
32
10
  ::ActiveRecord::Migration::CommandRecorder.send(:include, CommandRecorder)
33
11
  end
34
12
 
35
- # for all
36
13
  module ShardDefinition
37
14
  def clusters(*cluster_names)
38
15
  config = ActiveRecord::Base.turntable_config
@@ -55,32 +32,16 @@ module ActiveRecord::Turntable::Migration
55
32
  end
56
33
  end
57
34
 
58
- def get_current_shard
59
- "Shard: #{@@current_shard}" if @@current_shard
35
+ def target_shard?(shard_name)
36
+ target_shards.blank? or target_shards.include?(shard_name)
60
37
  end
61
38
 
62
- def migrate_with_turntable(direction)
63
- config = ActiveRecord::Base.configurations
64
- @@current_shard = nil
65
- shards = (self.class.target_shards||=[]).flatten.uniq.compact
66
- if self.class.target_shards.blank?
67
- return migrate_without_turntable(direction)
68
- end
39
+ def announce_with_turntable(message)
40
+ announce_without_turntable("#{message} - Shard: #{current_shard}")
41
+ end
69
42
 
70
- shards_conf = shards.map do |shard|
71
- config[Rails.env||"development"]["shards"][shard]
72
- end
73
- seqs = config[Rails.env||"development"]["seq"]
74
- shards_conf += seqs.values
75
- shards_conf << config[Rails.env||"development"]
76
- shards_conf.each_with_index do |conf, idx|
77
- @@current_shard = (shards[idx] || seqs.keys[idx - shards.size] || "master")
78
- ActiveRecord::Base.establish_connection(conf)
79
- if !ActiveRecord::Base.connection.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name())
80
- ActiveRecord::Base.connection.initialize_schema_migrations_table
81
- end
82
- migrate_without_turntable(direction)
83
- end
43
+ def exec_migration_with_turntable(*args)
44
+ exec_migration_without_turntable(*args) if target_shard?(current_shard)
84
45
  end
85
46
 
86
47
  module SchemaStatementsExt
@@ -132,4 +93,3 @@ module ActiveRecord::Turntable::Migration
132
93
  end
133
94
 
134
95
  end
135
-
@@ -3,11 +3,22 @@ module ActiveRecord::Turntable
3
3
  class Fader
4
4
  class CalculateShardsSumResult < Fader
5
5
  def execute
6
- @shards_query_hash.map do |shard, query|
6
+ results = @shards_query_hash.map do |shard, query|
7
7
  args = @args.dup
8
8
  args[1] = args[1].dup if args[1].present?
9
9
  shard.connection.send(@called_method, query, *@args, &@block)
10
- end.inject(&:+)
10
+ end
11
+ merge_results(results)
12
+ end
13
+
14
+ private
15
+
16
+ def merge_results(results)
17
+ ActiveRecord::Result.new(
18
+ results.first.columns,
19
+ results[0].rows.zip(*results[1..-1].map{|r| r.rows}).map {|r| [r.map {|v| v.first}.inject(&:+)]},
20
+ results.first.column_types
21
+ )
11
22
  end
12
23
  end
13
24
  end
@@ -3,17 +3,28 @@ module ActiveRecord::Turntable
3
3
  class Fader
4
4
  class SelectShardsMergeResult < Fader
5
5
  def execute
6
- res = @shards_query_hash.map do |shard, query|
6
+ results = @shards_query_hash.map do |shard, query|
7
7
  args = @args.dup
8
8
  args[1] = args[1].dup if args[1].present?
9
9
  shard.connection.send(@called_method, query, *args, &@block)
10
- end.flatten(1).compact
10
+ end
11
+ merge_results(results)
12
+ end
13
+
14
+ private
15
+
16
+ def merge_results(results)
17
+ if results.any? {|r| r.is_a?(ActiveRecord::Result) }
18
+ first_result = results.find {|r| r.present? }
19
+ return results.first unless first_result
11
20
 
12
- case @called_method
13
- when "select_value", "select_one"
14
- res.first if res
21
+ ActiveRecord::Result.new(
22
+ first_result.columns,
23
+ results.map {|r| r.rows}.flatten(1),
24
+ first_result.column_types
25
+ )
15
26
  else
16
- res
27
+ results.compact.inject(&:+)
17
28
  end
18
29
  end
19
30
  end
@@ -4,7 +4,9 @@ module ActiveRecord::Turntable
4
4
  class SpecifiedShard < Fader
5
5
  def execute
6
6
  shard, query = @shards_query_hash.first
7
- shard.connection.send(@called_method, query, *@args, &@block)
7
+ @proxy.with_shard(shard) do
8
+ shard.connection.send(@called_method, query, *@args, &@block)
9
+ end
8
10
  end
9
11
  end
10
12
  end
@@ -2,16 +2,18 @@
2
2
  module ActiveRecord::Turntable
3
3
  class Mixer
4
4
  class Fader
5
- # 単数shard
6
- autoload :SpecifiedShard, "active_record/turntable/mixer/fader/specified_shard"
5
+ extend ActiveSupport::Autoload
7
6
 
8
- # 複数shard
9
- autoload :SelectShardsMergeResult, "active_record/turntable/mixer/fader/select_shards_merge_result"
10
- autoload :InsertShardsMergeResult, "active_record/turntable/mixer/fader/insert_shards_merge_result"
11
- autoload :UpdateShardsMergeResult, "active_record/turntable/mixer/fader/update_shards_merge_result"
12
-
13
- # count
14
- autoload :CalculateShardsSumResult, "active_record/turntable/mixer/fader/calculate_shards_sum_result"
7
+ eager_autoload do
8
+ # single shard
9
+ autoload :SpecifiedShard
10
+ # multiple shard merging
11
+ autoload :SelectShardsMergeResult
12
+ autoload :InsertShardsMergeResult
13
+ autoload :UpdateShardsMergeResult
14
+ # calcurations
15
+ autoload :CalculateShardsSumResult
16
+ end
15
17
 
16
18
  attr_reader :shards_query_hash
17
19
  attr_reader :called_method
@@ -27,7 +29,7 @@ module ActiveRecord::Turntable
27
29
  end
28
30
 
29
31
  def execute
30
- raise ActiveRecord::Turntable::NotImplementedError, "Called abstract method"
32
+ raise NotImplementedError, "Called abstract method"
31
33
  end
32
34
  end
33
35
  end
@@ -3,7 +3,12 @@ require 'active_record/turntable/sql_tree_patch'
3
3
 
4
4
  module ActiveRecord::Turntable
5
5
  class Mixer
6
- autoload :Fader, "active_record/turntable/mixer/fader"
6
+ extend ActiveSupport::Autoload
7
+
8
+ eager_autoload do
9
+ autoload :Fader
10
+ end
11
+
7
12
  delegate :logger, :to => ActiveRecord::Base
8
13
 
9
14
  NOT_USED_FOR_SHARDING_OPERATORS_REGEXP = /\A(NOT IN|IS|IS NOT|BETWEEN|LIKE|\!\=|<<|>>|<>|>\=|<=|[\*\+\-\/\%\|\&><])\z/
@@ -25,7 +30,7 @@ module ActiveRecord::Turntable
25
30
  begin
26
31
  tree = SQLTree[binded_query]
27
32
  rescue Exception => err
28
- logger.warn { "[ActiveRecord::Turntable][BUG] Error on Parsing SQL: #{binded_query}, on_method: #{method_name}" }
33
+ logger.warn { "[ActiveRecord::Turntable] Error on Parsing SQL: #{binded_query}, on_method: #{method_name}" }
29
34
  raise err
30
35
  end
31
36
 
@@ -43,7 +48,7 @@ module ActiveRecord::Turntable
43
48
  method, query, *args, &block)
44
49
  end
45
50
  rescue Exception => err
46
- logger.warn { "[ActiveRecord::Turntable][BUG] Error on Building Fader: #{binded_query}, on_method: #{method_name}" }
51
+ logger.warn { "[ActiveRecord::Turntable] Error on Building Fader: #{binded_query}, on_method: #{method_name}" }
47
52
  raise err
48
53
  end
49
54
 
@@ -88,7 +93,7 @@ module ActiveRecord::Turntable
88
93
  []
89
94
  else
90
95
  raise ActiveRecord::Turntable::UnknownOperatorError,
91
- "[ActiveRecord::Turntable][BUG] Found Unknown SQL Operator:'#{tree.operator if tree.respond_to?(:operaor)}', Please report this bug."
96
+ "[ActiveRecord::Turntable] Found Unknown SQL Operator:'#{tree.operator if tree.respond_to?(:operaor)}', Please report this bug."
92
97
  end
93
98
  end
94
99
 
@@ -107,21 +112,15 @@ module ActiveRecord::Turntable
107
112
  Hash[shards.map {|s| [s, query] }]
108
113
  end
109
114
 
110
- if ActiveRecord::VERSION::STRING < '3.1'
111
- def bind_sql(sql, binds)
112
- sql
113
- end
114
- else
115
- def bind_sql(sql, binds)
116
- # TODO: substitution value should be determined by adapter
117
- query = sql.is_a?(String) ? sql : @proxy.to_sql(sql, binds ? binds.dup : [])
118
- query = if query.include?("\0") and binds.is_a?(Array) and binds[0].is_a?(Array) and binds[0][0].is_a?(ActiveRecord::ConnectionAdapters::Column)
119
- binds = binds.dup
120
- query.gsub("\0") { @proxy.master.connection.quote(*binds.shift.reverse) }
121
- else
122
- query
123
- end
124
- end
115
+ def bind_sql(sql, binds)
116
+ # TODO: substitution value should be determined by adapter
117
+ query = sql.is_a?(String) ? sql : @proxy.to_sql(sql, binds ? binds.dup : [])
118
+ query = if query.include?("\0") and binds.is_a?(Array) and binds[0].is_a?(Array) and binds[0][0].is_a?(ActiveRecord::ConnectionAdapters::Column)
119
+ binds = binds.dup
120
+ query.gsub("\0") { @proxy.master.connection.quote(*binds.shift.reverse) }
121
+ else
122
+ query
123
+ end
125
124
  end
126
125
 
127
126
  def build_select_fader(tree, method, query, *args, &block)
@@ -137,10 +136,16 @@ module ActiveRecord::Turntable
137
136
 
138
137
  if shard_keys.size == 1 # shard
139
138
  return Fader::SpecifiedShard.new(@proxy,
140
- { @proxy.cluster.select_shard(shard_keys.first) => query },
139
+ { @proxy.cluster.shard_for(shard_keys.first) => query },
141
140
  method, query, *args, &block)
142
- elsif tree.group_by or tree.order_by or tree.limit.try(:value).to_i > 1
143
- raise CannotSpecifyShardError, "cannot specify shard for query: #{binded_query}"
141
+ elsif SQLTree::Node::SelectDeclaration === tree.select.first and
142
+ tree.select.first.to_sql == '1 AS "one"' # for `SELECT 1 AS one` (AR::Base.exists?)
143
+ return Fader::SelectShardsMergeResult.new(@proxy,
144
+ build_shards_with_same_query(@proxy.shards.values, query),
145
+ method, query, *args, &block
146
+ )
147
+ elsif tree.group_by or tree.order_by or tree.limit.try(:value).to_i > 0
148
+ raise CannotSpecifyShardError, "cannot specify shard for query: #{tree.to_sql}"
144
149
  elsif shard_keys.present?
145
150
  if SQLTree::Node::SelectDeclaration === tree.select.first and
146
151
  SQLTree::Node::CountAggregrate === tree.select.first.expression
@@ -149,13 +154,17 @@ module ActiveRecord::Turntable
149
154
  method, query, *args, &block)
150
155
  else
151
156
  return Fader::SelectShardsMergeResult.new(@proxy,
152
- Hash[shard_keys.map {|k| [@proxy.cluster.select_shard(k), query] }],
157
+ Hash[shard_keys.map {|k| [@proxy.cluster.shard_for(k), query] }],
153
158
  method, query, *args, &block
154
159
  )
155
160
  end
156
161
  else # scan all shards
157
162
  if SQLTree::Node::SelectDeclaration === tree.select.first and
158
163
  SQLTree::Node::CountAggregrate === tree.select.first.expression
164
+
165
+ if raise_on_not_specified_shard_query?
166
+ raise CannotSpecifyShardError, "[Performance Notice] PLEASE FIX: #{tree.to_sql}"
167
+ end
159
168
  return Fader::CalculateShardsSumResult.new(@proxy,
160
169
  build_shards_with_same_query(@proxy.shards.values, query),
161
170
  method, query, *args, &block)
@@ -163,12 +172,15 @@ module ActiveRecord::Turntable
163
172
  SQLTree::Node::Expression::Value === tree.select.first.expression or
164
173
  SQLTree::Node::Expression::Variable === tree.select.first.expression
165
174
 
175
+ if raise_on_not_specified_shard_query?
176
+ raise CannotSpecifyShardError, "[Performance Notice] PLEASE FIX: #{tree.to_sql}"
177
+ end
166
178
  return Fader::SelectShardsMergeResult.new(@proxy,
167
179
  build_shards_with_same_query(@proxy.shards.values, query),
168
180
  method, query, *args, &block
169
181
  )
170
182
  else
171
- raise CannotSpecifyShardError, "cannot specify shard for query: #{binded_query}"
183
+ raise CannotSpecifyShardError, "cannot specify shard for query: #{tree.to_sql}"
172
184
  end
173
185
  end
174
186
  end
@@ -176,13 +188,23 @@ module ActiveRecord::Turntable
176
188
  def build_update_fader(tree, method, query, *args, &block)
177
189
  shard_keys = find_shard_keys(tree.where, @proxy.cluster.klass.table_name, @proxy.cluster.klass.turntable_shard_key.to_s)
178
190
  shards_with_query = if shard_keys.present?
179
- build_shards_with_same_query(shard_keys.map {|k| @proxy.cluster.select_shard(k) }, query)
191
+ build_shards_with_same_query(shard_keys.map {|k| @proxy.cluster.shard_for(k) }, query)
180
192
  else
181
193
  build_shards_with_same_query(@proxy.shards.values, query)
182
194
  end
183
- Fader::UpdateShardsMergeResult.new(@proxy,
184
- shards_with_query,
185
- method, query, *args, &block)
195
+
196
+ if shards_with_query.size == 1
197
+ Fader::SpecifiedShard.new(@proxy,
198
+ shards_with_query,
199
+ method, query, *args, &block)
200
+ else
201
+ if raise_on_not_specified_shard_update?
202
+ raise CannotSpecifyShardError, "[Performance Notice] PLEASE FIX: #{tree.to_sql}"
203
+ end
204
+ Fader::UpdateShardsMergeResult.new(@proxy,
205
+ shards_with_query,
206
+ method, query, *args, &block)
207
+ end
186
208
  end
187
209
 
188
210
  def build_insert_fader(tree, method, query, *args, &block)
@@ -195,9 +217,17 @@ module ActiveRecord::Turntable
195
217
  "(#{val.map { |v| @proxy.connection.quote(v.value)}.join(', ')})"
196
218
  end.join(', ')
197
219
  sql.gsub!('("\0")') { value_sql }
198
- shards_with_query[@proxy.cluster.select_shard(k)] = sql
220
+ shards_with_query[@proxy.cluster.shard_for(k)] = sql
199
221
  end
200
222
  Fader::InsertShardsMergeResult.new(@proxy, shards_with_query, method, query, *args, &block)
201
223
  end
224
+
225
+ def raise_on_not_specified_shard_query?
226
+ ActiveRecord::Base.turntable_config[:raise_on_not_specified_shard_query]
227
+ end
228
+
229
+ def raise_on_not_specified_shard_update?
230
+ ActiveRecord::Base.turntable_config[:raise_on_not_specified_shard_update]
231
+ end
202
232
  end
203
233
  end
@@ -0,0 +1,6 @@
1
+ require 'active_record/turntable'
2
+
3
+ module ActiveRecord::Turntable
4
+ module Plugin
5
+ end
6
+ end
@@ -14,34 +14,27 @@ module ActiveRecord::Turntable
14
14
  @proxy = proxy
15
15
  end
16
16
 
17
- def connection
17
+ def proxy
18
18
  @proxy
19
19
  end
20
-
21
- def spec
22
- @proxy.spec
23
- end
24
-
20
+ alias_method :connection, :proxy
25
21
 
26
22
  def with_connection
27
- yield @proxy
23
+ yield proxy
28
24
  end
29
25
 
30
- def connected?
31
- @proxy.connected?
32
- end
26
+ delegate :connected?, :automatic_reconnect, :automatic_reconnect=, :checkout_timeout, :dead_connection_timeout,
27
+ :spec, :connections, :size, :reaper, :table_exists?, to: :proxy
33
28
 
34
- if ActiveRecord::VERSION::STRING > '3.1'
35
- %w(columns_hash column_defaults primary_keys).each do |name|
36
- define_method(name.to_sym) do
37
- @proxy.send(name.to_sym)
38
- end
29
+ %w(columns_hash column_defaults primary_keys).each do |name|
30
+ define_method(name.to_sym) do
31
+ @proxy.send(name.to_sym)
39
32
  end
33
+ end
40
34
 
41
- %w(table_exists? columns).each do |name|
42
- define_method(name.to_sym) do |*args|
43
- @proxy.send(name.to_sym, *args)
44
- end
35
+ %w(table_exists? columns).each do |name|
36
+ define_method(name.to_sym) do |*args|
37
+ @proxy.send(name.to_sym, *args)
45
38
  end
46
39
  end
47
40