activerecord-turntable 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,17 @@
|
|
1
|
+
module ActiveRecord::Turntable
|
2
|
+
class Mixer
|
3
|
+
class Fader
|
4
|
+
class UpdateShardsMergeResult < Fader
|
5
|
+
def execute
|
6
|
+
@proxy.shards_transaction(@shards_query_hash.keys) do
|
7
|
+
@shards_query_hash.map do |shard, query|
|
8
|
+
args = @args.dup
|
9
|
+
args[1] = args[1].dup if args[1].present?
|
10
|
+
shard.connection.send(@called_method, query, *@args, &@block)
|
11
|
+
end.inject(&:+)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module ActiveRecord::Turntable
|
2
|
+
module ActiveRecordConnectionMethods
|
3
|
+
def self.included(base)
|
4
|
+
base.alias_method_chain :reload, :master
|
5
|
+
end
|
6
|
+
|
7
|
+
def reload_with_master(*args, &block)
|
8
|
+
connection.with_master { reload_without_master }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class PoolProxy
|
13
|
+
def initialize(proxy)
|
14
|
+
@proxy = proxy
|
15
|
+
end
|
16
|
+
|
17
|
+
def connection
|
18
|
+
@proxy
|
19
|
+
end
|
20
|
+
|
21
|
+
def spec
|
22
|
+
@proxy.spec
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def with_connection
|
27
|
+
yield @proxy
|
28
|
+
end
|
29
|
+
|
30
|
+
def connected?
|
31
|
+
@proxy.connected?
|
32
|
+
end
|
33
|
+
|
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
|
39
|
+
end
|
40
|
+
|
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
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
%w(disconnect! release_connection clear_all_connections! clear_reloadable_connections! clear_stale_cached_connections! verify_active_connections!).each do |name|
|
49
|
+
define_method(name.to_sym) do
|
50
|
+
@proxy.shards.values.each do |pool|
|
51
|
+
pool.connection_pool.send(name.to_sym)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ActiveRecord::Turntable
|
2
|
+
module Rack
|
3
|
+
class ConnectionManagement
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
@app.call(env)
|
10
|
+
ensure
|
11
|
+
unless env.key?("rack.test")
|
12
|
+
ActiveRecord::Base.connection_handler.clear_all_connections!
|
13
|
+
ActiveRecord::Base.clear_all_connections!
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ActiveRecord::Turntable
|
2
|
+
class Railtie < Rails::Railtie
|
3
|
+
rake_tasks do
|
4
|
+
load "active_record/turntable/railties/databases.rake"
|
5
|
+
end
|
6
|
+
|
7
|
+
# rails loading hook
|
8
|
+
ActiveSupport.on_load(:before_initialize) do
|
9
|
+
ActiveSupport.on_load(:active_record) do
|
10
|
+
ActiveRecord::Base.send(:include, ActiveRecord::Turntable)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
require 'active_record/turntable'
|
2
|
+
ActiveRecord::SchemaDumper.send(:include, ActiveRecord::Turntable::ActiveRecordExt::SchemaDumper)
|
3
|
+
|
4
|
+
db_namespace = namespace :db do
|
5
|
+
task :create do
|
6
|
+
if Rails.env.development? && ActiveRecord::Base.configurations['test'] && ActiveRecord::Base.configurations["test"]["shards"]
|
7
|
+
dbs = ActiveRecord::Base.configurations["test"]["shards"].values
|
8
|
+
dbs += ActiveRecord::Base.configurations["test"]["seq"].values if ActiveRecord::Base.configurations["test"]["seq"]
|
9
|
+
dbs.each do |shard_config|
|
10
|
+
create_database(shard_config)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
if shard_configs = ActiveRecord::Base.configurations[Rails.env || 'development']["shards"]
|
14
|
+
dbs = shard_configs.values
|
15
|
+
dbs += ActiveRecord::Base.configurations[Rails.env || 'development']["seq"].values if ActiveRecord::Base.configurations[Rails.env || 'development']["seq"]
|
16
|
+
dbs.each do |shard_config|
|
17
|
+
create_database(shard_config)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
config = ActiveRecord::Base.configurations[Rails.env || 'development']
|
21
|
+
ActiveRecord::Base.establish_connection(config)
|
22
|
+
end
|
23
|
+
|
24
|
+
task :drop do
|
25
|
+
config = ActiveRecord::Base.configurations[Rails.env || 'development']
|
26
|
+
shard_configs = config["shards"]
|
27
|
+
if shard_configs
|
28
|
+
dbs = shard_configs.values
|
29
|
+
dbs += ActiveRecord::Base.configurations[Rails.env || 'development']["seq"].values if ActiveRecord::Base.configurations[Rails.env || 'development']["seq"]
|
30
|
+
dbs.each do |shard_config|
|
31
|
+
begin
|
32
|
+
drop_database(shard_config)
|
33
|
+
rescue Exception => e
|
34
|
+
$stderr.puts "Couldn't drop #{ config['database']} : #{e.inspect}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
ActiveRecord::Base.establish_connection(config)
|
39
|
+
end
|
40
|
+
|
41
|
+
namespace :schema do
|
42
|
+
task :dump do
|
43
|
+
require 'active_record/schema_dumper'
|
44
|
+
config = ActiveRecord::Base.configurations[Rails.env]
|
45
|
+
shard_configs = config["shards"]
|
46
|
+
shard_configs.merge!(config["seq"]) if config["seq"]
|
47
|
+
if shard_configs
|
48
|
+
shard_configs.each do |name, config|
|
49
|
+
filename = ENV['SCHEMA'] || "#{Rails.root}/db/schema-#{name}.rb"
|
50
|
+
File.open(filename, "w:utf-8") do |file|
|
51
|
+
ActiveRecord::Base.establish_connection(config)
|
52
|
+
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
ActiveRecord::Base.establish_connection(config)
|
57
|
+
db_namespace['schema:dump'].reenable
|
58
|
+
end
|
59
|
+
|
60
|
+
desc 'Load a schema.rb file into the database'
|
61
|
+
task :load => :environment do
|
62
|
+
config = ActiveRecord::Base.configurations[Rails.env]
|
63
|
+
shard_configs = config["shards"]
|
64
|
+
shard_configs.merge!(config["seq"]) if config["seq"]
|
65
|
+
if shard_configs
|
66
|
+
shard_configs.each do |name, config|
|
67
|
+
ActiveRecord::Base.establish_connection(config)
|
68
|
+
file = ENV['SCHEMA'] || "#{Rails.root}/db/schema-#{name}.rb"
|
69
|
+
if File.exists?(file)
|
70
|
+
load(file)
|
71
|
+
else
|
72
|
+
abort %{#{file} doesn't exist yet. Run "rake db:migrate" to create it then try again. If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded'}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
ActiveRecord::Base.establish_connection(config)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
namespace :structure do
|
81
|
+
desc 'Dump the database structure to an SQL file'
|
82
|
+
task :dump => :environment do
|
83
|
+
config = ActiveRecord::Base.configurations[Rails.env]
|
84
|
+
shard_configs = config["shards"]
|
85
|
+
shard_configs.merge!(config["seq"]) if config["seq"]
|
86
|
+
if shard_configs
|
87
|
+
shard_configs.each do |name, config|
|
88
|
+
case config['adapter']
|
89
|
+
when /mysql/, 'oci', 'oracle'
|
90
|
+
ActiveRecord::Base.establish_connection(config)
|
91
|
+
File.open("#{Rails.root}/db/#{Rails.env}_#{name}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump }
|
92
|
+
when /postgresql/
|
93
|
+
ENV['PGHOST'] = config['host'] if config['host']
|
94
|
+
ENV['PGPORT'] = config["port"].to_s if config['port']
|
95
|
+
ENV['PGPASSWORD'] = config['password'].to_s if config['password']
|
96
|
+
search_path = config['schema_search_path']
|
97
|
+
unless search_path.blank?
|
98
|
+
search_path = search_path.split(",").map{|search_path| "--schema=#{search_path.strip}" }.join(" ")
|
99
|
+
end
|
100
|
+
`pg_dump -i -U "#{config['username']}" -s -x -O -f db/#{Rails.env}_#{name}_structure.sql #{search_path} #{config['database']}`
|
101
|
+
raise 'Error dumping database' if $?.exitstatus == 1
|
102
|
+
when /sqlite/
|
103
|
+
dbfile = config['database'] || config['dbfile']
|
104
|
+
`sqlite3 #{dbfile} .schema > db/#{Rails.env}_#{name}_structure.sql`
|
105
|
+
when 'sqlserver'
|
106
|
+
`smoscript -s #{config['host']} -d #{config['database']} -u #{config['username']} -p #{config['password']} -f db\\#{Rails.env}_#{name}_structure.sql -A -U`
|
107
|
+
when "firebird"
|
108
|
+
set_firebird_env(config)
|
109
|
+
db_string = firebird_db_string(config)
|
110
|
+
sh "isql -a #{db_string} > #{Rails.root}/db/#{Rails.env}_#{name}_structure.sql"
|
111
|
+
else
|
112
|
+
raise "Task not supported by '#{config["adapter"]}'"
|
113
|
+
end
|
114
|
+
|
115
|
+
if ActiveRecord::Base.connection.supports_migrations?
|
116
|
+
File.open("#{Rails.root}/db/#{Rails.env}_#{name}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
ActiveRecord::Base.establish_connection(config)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
namespace :test do
|
126
|
+
# desc "Recreate the test databases from the development structure"
|
127
|
+
task :clone_structure do
|
128
|
+
config = ActiveRecord::Base.configurations[Rails.env]
|
129
|
+
shard_configs = config["shards"]
|
130
|
+
shard_configs.merge!(config["seq"]) if config["seq"]
|
131
|
+
if shard_configs
|
132
|
+
shard_configs.each do |name, config|
|
133
|
+
case config['adapter']
|
134
|
+
when /mysql/
|
135
|
+
ActiveRecord::Base.establish_connection(config)
|
136
|
+
ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0')
|
137
|
+
IO.readlines("#{Rails.root}/db/#{Rails.env}_#{name}_structure.sql").join.split("\n\n").each do |table|
|
138
|
+
ActiveRecord::Base.connection.execute(table)
|
139
|
+
end
|
140
|
+
when /postgresql/
|
141
|
+
ENV['PGHOST'] = config['host'] if config['host']
|
142
|
+
ENV['PGPORT'] = config['port'].to_s if config['port']
|
143
|
+
ENV['PGPASSWORD'] = config['password'].to_s if config['password']
|
144
|
+
`psql -U "#{config['username']}" -f "#{Rails.root}/db/#{Rails.env}#{name}_structure.sql" #{config['database']} #{config['template']}`
|
145
|
+
when /sqlite/
|
146
|
+
dbfile = config['database'] || config['dbfile']
|
147
|
+
`sqlite3 #{dbfile} < "#{Rails.root}/db/#{Rails.env}#{name}_structure.sql"`
|
148
|
+
when 'sqlserver'
|
149
|
+
`sqlcmd -S #{config['host']} -d #{config['database']} -U #{config['username']} -P #{config['password']} -i db\\#{Rails.env}#{name}_structure.sql`
|
150
|
+
when 'oci', 'oracle'
|
151
|
+
ActiveRecord::Base.establish_connection(config)
|
152
|
+
IO.readlines("#{Rails.root}/db/#{Rails.env}#{name}_structure.sql").join.split(";\n\n").each do |ddl|
|
153
|
+
ActiveRecord::Base.connection.execute(ddl)
|
154
|
+
end
|
155
|
+
when 'firebird'
|
156
|
+
set_firebird_env(config)
|
157
|
+
db_string = firebird_db_string(config)
|
158
|
+
sh "isql -i #{Rails.root}/db/#{Rails.env}#{name}_structure.sql #{db_string}"
|
159
|
+
else
|
160
|
+
raise "Task not supported by '#{config['adapter']}'"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
ActiveRecord::Base.establish_connection(config)
|
165
|
+
end
|
166
|
+
|
167
|
+
# desc "Empty the test database"
|
168
|
+
task :purge => :environment do
|
169
|
+
config = ActiveRecord::Base.configurations[Rails.env]
|
170
|
+
shard_configs = config["shards"]
|
171
|
+
shard_configs.merge!(config["seq"]) if config["seq"]
|
172
|
+
if shard_configs
|
173
|
+
shard_configs.each do |name, config|
|
174
|
+
case config['adapter']
|
175
|
+
when /mysql/
|
176
|
+
ActiveRecord::Base.establish_connection(config)
|
177
|
+
ActiveRecord::Base.connection.recreate_database(config['database'], mysql_creation_options(config))
|
178
|
+
when /postgresql/
|
179
|
+
ActiveRecord::Base.clear_active_connections!
|
180
|
+
drop_database(config)
|
181
|
+
create_database(config)
|
182
|
+
when /sqlite/
|
183
|
+
dbfile = config['database'] || config['dbfile']
|
184
|
+
File.delete(dbfile) if File.exist?(dbfile)
|
185
|
+
when 'sqlserver'
|
186
|
+
# TODO
|
187
|
+
when "oci", "oracle"
|
188
|
+
ActiveRecord::Base.establish_connection(config)
|
189
|
+
ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl|
|
190
|
+
ActiveRecord::Base.connection.execute(ddl)
|
191
|
+
end
|
192
|
+
when 'firebird'
|
193
|
+
ActiveRecord::Base.establish_connection(config)
|
194
|
+
ActiveRecord::Base.connection.recreate_database!
|
195
|
+
else
|
196
|
+
raise "Task not supported by '#{config['adapter']}'"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
ActiveRecord::Base.establish_connection(config)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ActiveRecord::Turntable
|
2
|
+
class SeqShard < Shard
|
3
|
+
private
|
4
|
+
def retrieve_connection_pool
|
5
|
+
ActiveRecord::Base.turntable_connections[name] ||=
|
6
|
+
begin
|
7
|
+
config = ActiveRecord::Base.configurations[Rails.env]["seq"][name]
|
8
|
+
raise ArgumentError, "Unknown database config: #{name}, have #{ActiveRecord::Base.configurations.inspect}" unless config
|
9
|
+
ActiveRecord::ConnectionAdapters::ConnectionPool.new(spec_for(config))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# 採番
|
4
|
+
#
|
5
|
+
|
6
|
+
module ActiveRecord::Turntable
|
7
|
+
class Sequencer
|
8
|
+
autoload :Api, "active_record/turntable/sequencer/api"
|
9
|
+
autoload :Mysql, "active_record/turntable/sequencer/mysql"
|
10
|
+
@@sequence_types = {
|
11
|
+
:api => Api,
|
12
|
+
:mysql => Mysql
|
13
|
+
}
|
14
|
+
|
15
|
+
@@sequences = {}
|
16
|
+
@@tables = {}
|
17
|
+
cattr_reader :sequences, :tables
|
18
|
+
|
19
|
+
def self.build(klass)
|
20
|
+
seq_config_name = ActiveRecord::Base.turntable_config["clusters"][klass.turntable_cluster_name.to_s]["seq"]["connection"]
|
21
|
+
seq_config = ActiveRecord::Base.configurations[Rails.env]["seq"][seq_config_name]
|
22
|
+
seq_type = (seq_config["seq_type"] ? seq_config["seq_type"].to_sym : :mysql)
|
23
|
+
@@tables[klass.table_name] ||= (@@sequences[sequence_name(klass.table_name, klass.primary_key)] ||= @@sequence_types[seq_type].new(klass, seq_config))
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.has_sequencer?(table_name)
|
27
|
+
!!@@tables[table_name]
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.sequence_name(table_name, pk)
|
31
|
+
"#{ table_name}_#{pk || 'id'}_seq"
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.table_name(seq_name)
|
35
|
+
seq_name.split('_').first
|
36
|
+
end
|
37
|
+
|
38
|
+
def next_sequence_value
|
39
|
+
raise ActiveRecord::Turntable::NotImplementedError
|
40
|
+
end
|
41
|
+
|
42
|
+
def current_sequence_value
|
43
|
+
raise ActiveRecord::Turntable::NotImplementedError
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# APIサーバを利用しての採番
|
4
|
+
#
|
5
|
+
require 'httpclient'
|
6
|
+
|
7
|
+
module ActiveRecord::Turntable
|
8
|
+
class Sequencer
|
9
|
+
class Api < Sequencer
|
10
|
+
API_ENDPOINT = '/sequences/'
|
11
|
+
NEXT_VALUE_ENDPOINT = '/new'
|
12
|
+
|
13
|
+
def initialize(klass, options = {})
|
14
|
+
@klass = klass
|
15
|
+
@options = options
|
16
|
+
@host = @options["api_host"]
|
17
|
+
@port = @options["api_port"]
|
18
|
+
@client = HTTPClient.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def next_sequence_value(sequence_name)
|
22
|
+
res = @client.get_content("http://#{@host}:#{@port}#{API_ENDPOINT}#{sequence_name}#{NEXT_VALUE_ENDPOINT}")
|
23
|
+
new_id = res.to_i
|
24
|
+
raise SequenceNotFoundError if new_id.zero?
|
25
|
+
return new_id
|
26
|
+
end
|
27
|
+
|
28
|
+
def current_sequence_value(sequence_name)
|
29
|
+
res = @client.get_content("http://#{@host}:#{@port}#{API_ENDPOINT}#{sequence_name}")
|
30
|
+
current_id = res.to_i
|
31
|
+
raise SequenceNotFoundError if current_id.zero?
|
32
|
+
return current_id
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# 採番
|
4
|
+
#
|
5
|
+
|
6
|
+
module ActiveRecord::Turntable
|
7
|
+
class Sequencer
|
8
|
+
class Mysql < Sequencer
|
9
|
+
def initialize(klass, options = {})
|
10
|
+
@klass = klass
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def next_sequence_value(sequence_name)
|
15
|
+
conn = @klass.connection.seq.connection
|
16
|
+
conn.execute "BEGIN"
|
17
|
+
conn.execute "UPDATE #{@klass.connection.quote_table_name(sequence_name)} SET id=LAST_INSERT_ID(id+1)"
|
18
|
+
conn.execute "COMMIT"
|
19
|
+
res = conn.execute("SELECT LAST_INSERT_ID()")
|
20
|
+
new_id = res.first.first.to_i
|
21
|
+
raise SequenceNotFoundError if new_id.zero?
|
22
|
+
return new_id
|
23
|
+
end
|
24
|
+
|
25
|
+
def current_sequence_value(sequence_name)
|
26
|
+
res = @klass.connection.seq.connection.execute("SELECT id FROM #{@klass.connection.quote_table_name(sequence_name)} LIMIT 1")
|
27
|
+
current_id = res.first.first.to_i
|
28
|
+
return current_id
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|