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.
Files changed (130) hide show
  1. data/.document +5 -0
  2. data/.gitignore +25 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +25 -0
  5. data/Guardfile +9 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.rdoc +290 -0
  8. data/Rakefile +101 -0
  9. data/activerecord-turntable.gemspec +47 -0
  10. data/lib/active_record/turntable.rb +58 -0
  11. data/lib/active_record/turntable/active_record_ext.rb +26 -0
  12. data/lib/active_record/turntable/active_record_ext/.gitkeep +0 -0
  13. data/lib/active_record/turntable/active_record_ext/abstract_adapter.rb +50 -0
  14. data/lib/active_record/turntable/active_record_ext/clever_load.rb +90 -0
  15. data/lib/active_record/turntable/active_record_ext/fixtures.rb +131 -0
  16. data/lib/active_record/turntable/active_record_ext/log_subscriber.rb +64 -0
  17. data/lib/active_record/turntable/active_record_ext/persistence.rb +95 -0
  18. data/lib/active_record/turntable/active_record_ext/schema_dumper.rb +107 -0
  19. data/lib/active_record/turntable/active_record_ext/sequencer.rb +28 -0
  20. data/lib/active_record/turntable/active_record_ext/transactions.rb +33 -0
  21. data/lib/active_record/turntable/algorithm.rb +7 -0
  22. data/lib/active_record/turntable/algorithm/.gitkeep +0 -0
  23. data/lib/active_record/turntable/algorithm/base.rb +11 -0
  24. data/lib/active_record/turntable/algorithm/range_algorithm.rb +37 -0
  25. data/lib/active_record/turntable/algorithm/range_bsearch_algorithm.rb +41 -0
  26. data/lib/active_record/turntable/base.rb +130 -0
  27. data/lib/active_record/turntable/cluster.rb +70 -0
  28. data/lib/active_record/turntable/compatible.rb +19 -0
  29. data/lib/active_record/turntable/config.rb +24 -0
  30. data/lib/active_record/turntable/connection_proxy.rb +218 -0
  31. data/lib/active_record/turntable/connection_proxy/mixable.rb +39 -0
  32. data/lib/active_record/turntable/error.rb +8 -0
  33. data/lib/active_record/turntable/helpers.rb +5 -0
  34. data/lib/active_record/turntable/helpers/test_helper.rb +25 -0
  35. data/lib/active_record/turntable/master_shard.rb +28 -0
  36. data/lib/active_record/turntable/migration.rb +132 -0
  37. data/lib/active_record/turntable/mixer.rb +203 -0
  38. data/lib/active_record/turntable/mixer/fader.rb +34 -0
  39. data/lib/active_record/turntable/mixer/fader/calculate_shards_sum_result.rb +15 -0
  40. data/lib/active_record/turntable/mixer/fader/insert_shards_merge_result.rb +24 -0
  41. data/lib/active_record/turntable/mixer/fader/select_shards_merge_result.rb +22 -0
  42. data/lib/active_record/turntable/mixer/fader/specified_shard.rb +12 -0
  43. data/lib/active_record/turntable/mixer/fader/update_shards_merge_result.rb +17 -0
  44. data/lib/active_record/turntable/pool_proxy.rb +56 -0
  45. data/lib/active_record/turntable/rack.rb +5 -0
  46. data/lib/active_record/turntable/rack/connection_management.rb +18 -0
  47. data/lib/active_record/turntable/railtie.rb +14 -0
  48. data/lib/active_record/turntable/railties/databases.rake +205 -0
  49. data/lib/active_record/turntable/seq_shard.rb +14 -0
  50. data/lib/active_record/turntable/sequencer.rb +46 -0
  51. data/lib/active_record/turntable/sequencer/api.rb +36 -0
  52. data/lib/active_record/turntable/sequencer/mysql.rb +32 -0
  53. data/lib/active_record/turntable/shard.rb +48 -0
  54. data/lib/active_record/turntable/sql_tree_patch.rb +199 -0
  55. data/lib/active_record/turntable/version.rb +5 -0
  56. data/lib/activerecord-turntable.rb +2 -0
  57. data/lib/generators/active_record/turntable/install_generator.rb +14 -0
  58. data/lib/generators/templates/turntable.yml +40 -0
  59. data/sample_app/.gitignore +16 -0
  60. data/sample_app/Gemfile +41 -0
  61. data/sample_app/README.rdoc +261 -0
  62. data/sample_app/Rakefile +7 -0
  63. data/sample_app/app/assets/images/rails.png +0 -0
  64. data/sample_app/app/assets/javascripts/application.js +15 -0
  65. data/sample_app/app/assets/stylesheets/application.css +13 -0
  66. data/sample_app/app/controllers/application_controller.rb +3 -0
  67. data/sample_app/app/helpers/application_helper.rb +2 -0
  68. data/sample_app/app/mailers/.gitkeep +0 -0
  69. data/sample_app/app/models/.gitkeep +0 -0
  70. data/sample_app/app/models/user.rb +4 -0
  71. data/sample_app/app/views/layouts/application.html.erb +14 -0
  72. data/sample_app/config.ru +4 -0
  73. data/sample_app/config/application.rb +65 -0
  74. data/sample_app/config/boot.rb +6 -0
  75. data/sample_app/config/database.yml +70 -0
  76. data/sample_app/config/environment.rb +5 -0
  77. data/sample_app/config/environments/development.rb +37 -0
  78. data/sample_app/config/environments/production.rb +67 -0
  79. data/sample_app/config/environments/test.rb +37 -0
  80. data/sample_app/config/initializers/backtrace_silencers.rb +7 -0
  81. data/sample_app/config/initializers/inflections.rb +15 -0
  82. data/sample_app/config/initializers/mime_types.rb +5 -0
  83. data/sample_app/config/initializers/secret_token.rb +7 -0
  84. data/sample_app/config/initializers/session_store.rb +8 -0
  85. data/sample_app/config/initializers/wrap_parameters.rb +14 -0
  86. data/sample_app/config/locales/en.yml +5 -0
  87. data/sample_app/config/routes.rb +58 -0
  88. data/sample_app/config/turntable.yml +64 -0
  89. data/sample_app/db/migrate/20120316073058_create_users.rb +11 -0
  90. data/sample_app/db/seeds.rb +7 -0
  91. data/sample_app/lib/assets/.gitkeep +0 -0
  92. data/sample_app/lib/tasks/.gitkeep +0 -0
  93. data/sample_app/log/.gitkeep +0 -0
  94. data/sample_app/public/404.html +26 -0
  95. data/sample_app/public/422.html +26 -0
  96. data/sample_app/public/500.html +25 -0
  97. data/sample_app/public/favicon.ico +0 -0
  98. data/sample_app/public/index.html +241 -0
  99. data/sample_app/public/robots.txt +5 -0
  100. data/sample_app/script/rails +6 -0
  101. data/sample_app/vendor/assets/javascripts/.gitkeep +0 -0
  102. data/sample_app/vendor/assets/stylesheets/.gitkeep +0 -0
  103. data/sample_app/vendor/plugins/.gitkeep +0 -0
  104. data/script/performance/algorithm +32 -0
  105. data/spec/active_record/turntable/active_record_ext/clever_load_spec.rb +81 -0
  106. data/spec/active_record/turntable/active_record_ext/persistence_spec.rb +151 -0
  107. data/spec/active_record/turntable/algorithm/range_algorithm_spec.rb +35 -0
  108. data/spec/active_record/turntable/algorithm_spec.rb +69 -0
  109. data/spec/active_record/turntable/base_spec.rb +13 -0
  110. data/spec/active_record/turntable/cluster_spec.rb +18 -0
  111. data/spec/active_record/turntable/config_spec.rb +17 -0
  112. data/spec/active_record/turntable/connection_proxy_spec.rb +186 -0
  113. data/spec/active_record/turntable/finder_spec.rb +27 -0
  114. data/spec/active_record/turntable/mixer/fader_spec.rb +4 -0
  115. data/spec/active_record/turntable/mixer_spec.rb +114 -0
  116. data/spec/active_record/turntable/shard_spec.rb +21 -0
  117. data/spec/active_record/turntable_spec.rb +30 -0
  118. data/spec/config/database.yml +45 -0
  119. data/spec/config/turntable.yml +17 -0
  120. data/spec/fabricators/.gitkeep +0 -0
  121. data/spec/fabricators/turntable_fabricator.rb +14 -0
  122. data/spec/migrations/.gitkeep +0 -0
  123. data/spec/migrations/001_create_users.rb +16 -0
  124. data/spec/migrations/002_create_user_statuses.rb +16 -0
  125. data/spec/migrations/003_create_cards.rb +14 -0
  126. data/spec/migrations/004_create_cards_users.rb +15 -0
  127. data/spec/spec_helper.rb +23 -0
  128. data/spec/test_models.rb +27 -0
  129. data/spec/turntable_helper.rb +29 -0
  130. metadata +367 -0
@@ -0,0 +1,132 @@
1
+ module ActiveRecord::Turntable::Migration
2
+ extend ActiveSupport::Concern
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
+ included do
23
+ 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
30
+ alias_method_chain :announce, :turntable
31
+ ::ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, SchemaStatementsExt)
32
+ ::ActiveRecord::Migration::CommandRecorder.send(:include, CommandRecorder)
33
+ end
34
+
35
+ # for all
36
+ module ShardDefinition
37
+ def clusters(*cluster_names)
38
+ config = ActiveRecord::Base.turntable_config
39
+ (self.target_shards ||= []) <<
40
+ if cluster_names.first == :all
41
+ config['clusters'].map do |name, cluster_conf|
42
+ cluster_conf["shards"].map {|shard| shard["connection"]}
43
+ end
44
+ else
45
+ cluster_names.map do |cluster_name|
46
+ config['clusters'][cluster_name]["shards"].map do |shard|
47
+ shard["connection"]
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ def shards(*connection_names)
54
+ (self.target_shards ||= []) << connection_names
55
+ end
56
+ end
57
+
58
+ def get_current_shard
59
+ "Shard: #{@@current_shard}" if @@current_shard
60
+ end
61
+
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
69
+
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
84
+ end
85
+
86
+ module SchemaStatementsExt
87
+ def create_sequence_for(table_name, options = { })
88
+ # TODO: pkname should be pulled from table definitions
89
+ pkname = "id"
90
+ sequence_table_name = ActiveRecord::Turntable::Sequencer.sequence_name(table_name, "id")
91
+ create_table(sequence_table_name, options)
92
+ execute "ALTER TABLE #{quote_table_name(sequence_table_name)} MODIFY id bigint(20) DEFAULT NULL auto_increment NOT NULL;"
93
+ execute "INSERT INTO #{quote_table_name(sequence_table_name)} (`id`) VALUES (0)"
94
+ end
95
+
96
+ def drop_sequence_for(table_name, options = { })
97
+ # TODO: pkname should be pulled from table definitions
98
+ pkname = "id"
99
+ sequence_table_name = ActiveRecord::Turntable::Sequencer.sequence_name(table_name, "id")
100
+ drop_table(sequence_table_name)
101
+ end
102
+
103
+ def rename_sequence_for(table_name, new_name)
104
+ # TODO: pkname should pulled from table definitions
105
+ seq_table_name = ActiveRecord::Turntable::Sequencer.sequence_name(table_name, "id")
106
+ new_seq_name = ActiveRecord::Turntable::Sequencer.sequence_name(new_name, "id")
107
+ rename_table(seq_table_name, new_seq_name)
108
+ end
109
+ end
110
+
111
+ module CommandRecorder
112
+ def create_sequence_for(*args)
113
+ record(:create_sequence_for, args)
114
+ end
115
+
116
+ def rename_sequence_for(*args)
117
+ record(:rename_sequence_for, args)
118
+ end
119
+
120
+ private
121
+
122
+ def invert_create_sequence_for(args)
123
+ [:drop_sequence_for, args]
124
+ end
125
+
126
+ def invert_rename_sequence_for(args)
127
+ [:rename_sequence_for, args.reverse]
128
+ end
129
+ end
130
+
131
+ end
132
+
@@ -0,0 +1,203 @@
1
+ require 'active_support/core_ext/object/try'
2
+ require 'active_record/turntable/sql_tree_patch'
3
+
4
+ module ActiveRecord::Turntable
5
+ class Mixer
6
+ autoload :Fader, "active_record/turntable/mixer/fader"
7
+ delegate :logger, :to => ActiveRecord::Base
8
+
9
+ NOT_USED_FOR_SHARDING_OPERATORS_REGEXP = /\A(NOT IN|IS|IS NOT|BETWEEN|LIKE|\!\=|<<|>>|<>|>\=|<=|[\*\+\-\/\%\|\&><])\z/
10
+
11
+ def initialize(proxy)
12
+ @proxy = proxy
13
+ end
14
+
15
+ def build_fader(method_name, query, *args, &block)
16
+ method = method_name.to_s
17
+ if @proxy.shard_fixed?
18
+ return SpecifiedShard.new(@proxy,
19
+ { @proxy.fixed_shard => query },
20
+ method, query, *args, &block)
21
+ end
22
+ binds = (method == 'insert') ? args[4] : args[1]
23
+ binded_query = bind_sql(query, binds)
24
+
25
+ begin
26
+ tree = SQLTree[binded_query]
27
+ rescue Exception => err
28
+ logger.warn { "[ActiveRecord::Turntable][BUG] Error on Parsing SQL: #{binded_query}, on_method: #{method_name}" }
29
+ raise err
30
+ end
31
+
32
+ case tree
33
+ when SQLTree::Node::SelectQuery
34
+ build_select_fader(tree, method, query, *args, &block)
35
+ when SQLTree::Node::UpdateQuery, SQLTree::Node::DeleteQuery
36
+ build_update_fader(tree, method, query, *args, &block)
37
+ when SQLTree::Node::InsertQuery
38
+ build_insert_fader(tree, method, query, *args, &block)
39
+ else
40
+ # send to master shard
41
+ Fader::SpecifiedShard.new(@proxy,
42
+ { @proxy.master => query },
43
+ method, query, *args, &block)
44
+ end
45
+ rescue Exception => err
46
+ logger.warn { "[ActiveRecord::Turntable][BUG] Error on Building Fader: #{binded_query}, on_method: #{method_name}" }
47
+ raise err
48
+ end
49
+
50
+ def find_shard_keys(tree, table_name, shard_key)
51
+ return [] unless tree.respond_to?(:operator)
52
+
53
+ case tree.operator
54
+ when "OR"
55
+ lkeys = find_shard_keys(tree.lhs, table_name, shard_key)
56
+ rkeys = find_shard_keys(tree.rhs, table_name, shard_key)
57
+ if lkeys.present? and rkeys.present?
58
+ lkeys + rkeys
59
+ else
60
+ []
61
+ end
62
+ when "AND"
63
+ lkeys = find_shard_keys(tree.lhs, table_name, shard_key)
64
+ rkeys = find_shard_keys(tree.rhs, table_name, shard_key)
65
+ if lkeys.present? or rkeys.present?
66
+ lkeys + rkeys
67
+ else
68
+ []
69
+ end
70
+ when "IN", "=", "=="
71
+ field = tree.lhs.respond_to?(:table) ? tree.lhs : nil
72
+ if tree.rhs.is_a?(SQLTree::Node::SubQuery)
73
+ if field.try(:table) == table_name and field.name == shard_key
74
+ find_shard_keys(tree.rhs.where, table_name, shard_key)
75
+ else
76
+ []
77
+ end
78
+ else
79
+ values = Array(tree.rhs)
80
+ if field.try(:table) == table_name and field.name == shard_key and
81
+ !tree.rhs.is_a?(SQLTree::Node::SubQuery)
82
+ values.map(&:value).compact
83
+ else
84
+ []
85
+ end
86
+ end
87
+ when NOT_USED_FOR_SHARDING_OPERATORS_REGEXP
88
+ []
89
+ else
90
+ raise ActiveRecord::Turntable::UnknownOperatorError,
91
+ "[ActiveRecord::Turntable][BUG] Found Unknown SQL Operator:'#{tree.operator if tree.respond_to?(:operaor)}', Please report this bug."
92
+ end
93
+ end
94
+
95
+ private
96
+
97
+ def divide_insert_values(tree, shard_key_name)
98
+ idx = tree.fields.find_index {|f| f.name == shard_key_name.to_s }
99
+ result = {}
100
+ tree.values.each do |val|
101
+ (result[val[idx].value] ||= []) << val
102
+ end
103
+ return result
104
+ end
105
+
106
+ def build_shards_with_same_query(shards, query)
107
+ Hash[shards.map {|s| [s, query] }]
108
+ end
109
+
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
125
+ end
126
+
127
+ def build_select_fader(tree, method, query, *args, &block)
128
+ shard_keys = if !tree.where and tree.from.size == 1 and SQLTree::Node::SubQuery === tree.from.first.table_reference.table
129
+ find_shard_keys(tree.from.first.table_reference.table.where,
130
+ @proxy.cluster.klass.table_name,
131
+ @proxy.cluster.klass.turntable_shard_key.to_s)
132
+ else
133
+ find_shard_keys(tree.where,
134
+ @proxy.cluster.klass.table_name,
135
+ @proxy.cluster.klass.turntable_shard_key.to_s)
136
+ end
137
+
138
+ if shard_keys.size == 1 # shard
139
+ return Fader::SpecifiedShard.new(@proxy,
140
+ { @proxy.cluster.select_shard(shard_keys.first) => query },
141
+ 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}"
144
+ elsif shard_keys.present?
145
+ if SQLTree::Node::SelectDeclaration === tree.select.first and
146
+ SQLTree::Node::CountAggregrate === tree.select.first.expression
147
+ return Fader::CalculateShardsSumResult.new(@proxy,
148
+ build_shards_with_same_query(@proxy.shards.values, query),
149
+ method, query, *args, &block)
150
+ else
151
+ return Fader::SelectShardsMergeResult.new(@proxy,
152
+ Hash[shard_keys.map {|k| [@proxy.cluster.select_shard(k), query] }],
153
+ method, query, *args, &block
154
+ )
155
+ end
156
+ else # scan all shards
157
+ if SQLTree::Node::SelectDeclaration === tree.select.first and
158
+ SQLTree::Node::CountAggregrate === tree.select.first.expression
159
+ return Fader::CalculateShardsSumResult.new(@proxy,
160
+ build_shards_with_same_query(@proxy.shards.values, query),
161
+ method, query, *args, &block)
162
+ elsif SQLTree::Node::AllFieldsDeclaration === tree.select.first or
163
+ SQLTree::Node::Expression::Value === tree.select.first.expression or
164
+ SQLTree::Node::Expression::Variable === tree.select.first.expression
165
+
166
+ return Fader::SelectShardsMergeResult.new(@proxy,
167
+ build_shards_with_same_query(@proxy.shards.values, query),
168
+ method, query, *args, &block
169
+ )
170
+ else
171
+ raise CannotSpecifyShardError, "cannot specify shard for query: #{binded_query}"
172
+ end
173
+ end
174
+ end
175
+
176
+ def build_update_fader(tree, method, query, *args, &block)
177
+ shard_keys = find_shard_keys(tree.where, @proxy.cluster.klass.table_name, @proxy.cluster.klass.turntable_shard_key.to_s)
178
+ shards_with_query = if shard_keys.present?
179
+ build_shards_with_same_query(shard_keys.map {|k| @proxy.cluster.select_shard(k) }, query)
180
+ else
181
+ build_shards_with_same_query(@proxy.shards.values, query)
182
+ end
183
+ Fader::UpdateShardsMergeResult.new(@proxy,
184
+ shards_with_query,
185
+ method, query, *args, &block)
186
+ end
187
+
188
+ def build_insert_fader(tree, method, query, *args, &block)
189
+ values_hash = divide_insert_values(tree, @proxy.cluster.klass.turntable_shard_key)
190
+ shards_with_query = {}
191
+ values_hash.each do |k,vs|
192
+ tree.values = [[SQLTree::Node::Expression::Variable.new("\\0")]]
193
+ sql = tree.to_sql
194
+ value_sql = vs.map do |val|
195
+ "(#{val.map { |v| @proxy.connection.quote(v.value)}.join(', ')})"
196
+ end.join(', ')
197
+ sql.gsub!('("\0")') { value_sql }
198
+ shards_with_query[@proxy.cluster.select_shard(k)] = sql
199
+ end
200
+ Fader::InsertShardsMergeResult.new(@proxy, shards_with_query, method, query, *args, &block)
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,34 @@
1
+ # -*- coding: utf-8 -*-
2
+ module ActiveRecord::Turntable
3
+ class Mixer
4
+ class Fader
5
+ # 単数shard
6
+ autoload :SpecifiedShard, "active_record/turntable/mixer/fader/specified_shard"
7
+
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"
15
+
16
+ attr_reader :shards_query_hash
17
+ attr_reader :called_method
18
+ attr_reader :query
19
+
20
+ def initialize(proxy, shards_query_hash, called_method, query, *args, &block)
21
+ @proxy = proxy
22
+ @shards_query_hash = shards_query_hash
23
+ @called_method = called_method
24
+ @query = query
25
+ @args = args
26
+ @block = block
27
+ end
28
+
29
+ def execute
30
+ raise ActiveRecord::Turntable::NotImplementedError, "Called abstract method"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,15 @@
1
+ module ActiveRecord::Turntable
2
+ class Mixer
3
+ class Fader
4
+ class CalculateShardsSumResult < Fader
5
+ def execute
6
+ @shards_query_hash.map do |shard, query|
7
+ args = @args.dup
8
+ args[1] = args[1].dup if args[1].present?
9
+ shard.connection.send(@called_method, query, *@args, &@block)
10
+ end.inject(&:+)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ module ActiveRecord::Turntable
2
+ class Mixer
3
+ class Fader
4
+ class InsertShardsMergeResult < Fader
5
+ def execute
6
+ if @shards_query_hash.size == 1
7
+ @proxy.shards_transaction(@shards_query_hash.keys) do
8
+ shard, query = @shards_query_hash.first
9
+ shard.connection.send(@called_method, query, *@args, &@block)
10
+ end
11
+ else
12
+ @proxy.shards_transaction(@shards_query_hash.keys) do
13
+ @shards_query_hash.each do |shard, query|
14
+ args = @args.dup
15
+ args[4] = args[4].dup if args[4].present?
16
+ shard.connection.send(@called_method, query, *args, &@block)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ module ActiveRecord::Turntable
2
+ class Mixer
3
+ class Fader
4
+ class SelectShardsMergeResult < Fader
5
+ def execute
6
+ res = @shards_query_hash.map do |shard, query|
7
+ args = @args.dup
8
+ args[1] = args[1].dup if args[1].present?
9
+ shard.connection.send(@called_method, query, *args, &@block)
10
+ end.flatten(1).compact
11
+
12
+ case @called_method
13
+ when "select_value", "select_one"
14
+ res.first if res
15
+ else
16
+ res
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ module ActiveRecord::Turntable
2
+ class Mixer
3
+ class Fader
4
+ class SpecifiedShard < Fader
5
+ def execute
6
+ shard, query = @shards_query_hash.first
7
+ shard.connection.send(@called_method, query, *@args, &@block)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end