data_fabric 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +2 -11
- data/Rakefile +40 -27
- data/data_fabric.gemspec +7 -27
- data/example/test/integration/account_figments_test.rb +7 -5
- data/lib/data_fabric.rb +23 -11
- data/lib/data_fabric/version.rb +1 -1
- data/test/{database.yml → database.yml.example} +9 -0
- data/test/database_test.rb +2 -3
- data/test/shard_test.rb +35 -1
- data/test/test_helper.rb +31 -14
- data/test/thread_test.rb +3 -4
- metadata +6 -26
- data/TODO +0 -1
- data/example/db/development.sqlite3 +0 -0
- data/example/db/s0_development.sqlite3 +0 -0
- data/example/db/s0_test.sqlite3 +0 -0
- data/example/db/s1_development.sqlite3 +0 -0
- data/example/db/s1_test.sqlite3 +0 -0
- data/example/db/test.sqlite3 +0 -0
- data/example/vendor/plugins/data_fabric/init.rb +0 -1
- data/example/vendor/plugins/data_fabric/lib/data_fabric.rb +0 -231
data/Manifest
CHANGED
@@ -19,15 +19,9 @@ example/config/initializers/inflections.rb
|
|
19
19
|
example/config/initializers/mime_types.rb
|
20
20
|
example/config/initializers/new_rails_defaults.rb
|
21
21
|
example/config/routes.rb
|
22
|
-
example/db/development.sqlite3
|
23
22
|
example/db/migrate/20080702154628_create_accounts.rb
|
24
23
|
example/db/migrate/20080702154820_create_figments.rb
|
25
|
-
example/db/s0_development.sqlite3
|
26
|
-
example/db/s0_test.sqlite3
|
27
|
-
example/db/s1_development.sqlite3
|
28
|
-
example/db/s1_test.sqlite3
|
29
24
|
example/db/schema.rb
|
30
|
-
example/db/test.sqlite3
|
31
25
|
example/public/404.html
|
32
26
|
example/public/422.html
|
33
27
|
example/public/500.html
|
@@ -61,19 +55,16 @@ example/test/fixtures/accounts.yml
|
|
61
55
|
example/test/functional/accounts_controller_test.rb
|
62
56
|
example/test/integration/account_figments_test.rb
|
63
57
|
example/test/test_helper.rb
|
64
|
-
example/vendor/plugins/data_fabric/init.rb
|
65
|
-
example/vendor/plugins/data_fabric/lib/data_fabric.rb
|
66
58
|
init.rb
|
67
59
|
lib/data_fabric/version.rb
|
68
60
|
lib/data_fabric.rb
|
61
|
+
Manifest
|
69
62
|
Rakefile
|
70
63
|
README.rdoc
|
71
64
|
test/connection_test.rb
|
72
|
-
test/database.yml
|
65
|
+
test/database.yml.example
|
73
66
|
test/database_test.rb
|
74
67
|
test/shard_test.rb
|
75
68
|
test/test_helper.rb
|
76
69
|
test/thread_test.rb
|
77
70
|
TESTING.rdoc
|
78
|
-
TODO
|
79
|
-
Manifest
|
data/Rakefile
CHANGED
@@ -4,57 +4,70 @@ require 'echoe'
|
|
4
4
|
require File.dirname(__FILE__) << "/lib/data_fabric/version"
|
5
5
|
|
6
6
|
Echoe.new 'data_fabric' do |p|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
7
|
+
p.version = DataFabric::Version::STRING
|
8
|
+
p.author = "Mike Perham"
|
9
|
+
p.email = 'mperham@gmail.com'
|
10
|
+
p.project = 'fiveruns'
|
11
|
+
p.summary = 'Sharding and replication support for ActiveRecord 2.x'
|
12
|
+
p.url = "http://github.com/fiveruns/data_fabric"
|
13
|
+
p.dependencies = ['activerecord >=2.0.2']
|
14
|
+
p.development_dependencies = []
|
15
|
+
p.rubygems_version = nil
|
16
|
+
p.include_rakefile = true
|
15
17
|
end
|
16
18
|
|
17
19
|
task :pretest do
|
18
|
-
|
20
|
+
setup(false)
|
19
21
|
end
|
20
22
|
|
21
23
|
task :create_db do
|
22
|
-
|
24
|
+
setup(true)
|
23
25
|
end
|
24
26
|
|
25
27
|
task :changelog do
|
26
|
-
|
28
|
+
`git log | grep -v git-svn-id > History.txt`
|
29
|
+
end
|
30
|
+
|
31
|
+
def load_database_yml
|
32
|
+
filename = "test/database.yml"
|
33
|
+
if !File.exist?(filename)
|
34
|
+
STDERR.puts "\n*** ERROR ***:\n" <<
|
35
|
+
"You must have a 'test/database.yml' file in order to create the test database. " <<
|
36
|
+
"An example is provided in 'test/database.yml.example'.\n\n"
|
37
|
+
exit 1
|
38
|
+
end
|
39
|
+
YAML::load(ERB.new(IO.read(filename)).result)
|
27
40
|
end
|
28
41
|
|
29
42
|
def setup_connection
|
30
43
|
require 'active_record'
|
31
|
-
|
32
|
-
|
33
|
-
ActiveRecord::Base.
|
34
|
-
ActiveRecord::Base.
|
44
|
+
ActiveRecord::Base.configurations = load_database_yml
|
45
|
+
ActiveRecord::Base.establish_connection('fiveruns_city_austin_test_master')
|
46
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
47
|
+
ActiveRecord::Base.logger.level = Logger::DEBUG
|
35
48
|
end
|
36
49
|
|
37
|
-
def using_connection(&block)
|
50
|
+
def using_connection(database_identifier, &block)
|
38
51
|
ActiveRecord::Base.connection.instance_eval(&block)
|
39
52
|
end
|
40
53
|
|
41
54
|
def setup(create = false)
|
42
55
|
setup_connection
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
execute "use #{
|
56
|
+
|
57
|
+
ActiveRecord::Base.configurations.each_pair do |identifier, config|
|
58
|
+
using_connection(identifier) do
|
59
|
+
db_name = config['database']
|
60
|
+
if create
|
61
|
+
execute "drop database if exists #{db_name}"
|
62
|
+
execute "create database #{db_name}"
|
63
|
+
end
|
64
|
+
execute "use #{db_name}"
|
52
65
|
execute "drop table if exists the_whole_burritos"
|
53
66
|
execute "drop table if exists enchiladas"
|
54
67
|
execute "create table enchiladas (id integer not null auto_increment, name varchar(30) not null, primary key(id))"
|
55
|
-
execute "insert into enchiladas (id, name) values (1, '#{
|
68
|
+
execute "insert into enchiladas (id, name) values (1, '#{db_name}')"
|
56
69
|
execute "create table the_whole_burritos (id integer not null auto_increment, name varchar(30) not null, primary key(id))"
|
57
|
-
execute "insert into the_whole_burritos (id, name) values (1, '#{
|
70
|
+
execute "insert into the_whole_burritos (id, name) values (1, '#{db_name}')"
|
58
71
|
end
|
59
72
|
end
|
60
73
|
end
|
data/data_fabric.gemspec
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
|
2
|
-
# Gem::Specification for Data_fabric-1.0.
|
2
|
+
# Gem::Specification for Data_fabric-1.0.2
|
3
3
|
# Originally generated by Echoe
|
4
4
|
|
5
5
|
--- !ruby/object:Gem::Specification
|
6
6
|
name: data_fabric
|
7
7
|
version: !ruby/object:Gem::Version
|
8
|
-
version: 1.0.
|
8
|
+
version: 1.0.2
|
9
9
|
platform: ruby
|
10
10
|
authors:
|
11
11
|
- Mike Perham
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
|
15
|
-
date: 2008-
|
15
|
+
date: 2008-09-30 00:00:00 -05:00
|
16
16
|
default_executable:
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
@@ -25,16 +25,6 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 2.0.2
|
27
27
|
version:
|
28
|
-
- !ruby/object:Gem::Dependency
|
29
|
-
name: echoe
|
30
|
-
type: :development
|
31
|
-
version_requirement:
|
32
|
-
version_requirements: !ruby/object:Gem::Requirement
|
33
|
-
requirements:
|
34
|
-
- - ">="
|
35
|
-
- !ruby/object:Gem::Version
|
36
|
-
version: "0"
|
37
|
-
version:
|
38
28
|
description: Sharding and replication support for ActiveRecord 2.x
|
39
29
|
email: mperham@gmail.com
|
40
30
|
executables: []
|
@@ -46,7 +36,6 @@ extra_rdoc_files:
|
|
46
36
|
- lib/data_fabric/version.rb
|
47
37
|
- lib/data_fabric.rb
|
48
38
|
- README.rdoc
|
49
|
-
- TODO
|
50
39
|
files:
|
51
40
|
- CHANGELOG
|
52
41
|
- example/app/controllers/accounts_controller.rb
|
@@ -69,15 +58,9 @@ files:
|
|
69
58
|
- example/config/initializers/mime_types.rb
|
70
59
|
- example/config/initializers/new_rails_defaults.rb
|
71
60
|
- example/config/routes.rb
|
72
|
-
- example/db/development.sqlite3
|
73
61
|
- example/db/migrate/20080702154628_create_accounts.rb
|
74
62
|
- example/db/migrate/20080702154820_create_figments.rb
|
75
|
-
- example/db/s0_development.sqlite3
|
76
|
-
- example/db/s0_test.sqlite3
|
77
|
-
- example/db/s1_development.sqlite3
|
78
|
-
- example/db/s1_test.sqlite3
|
79
63
|
- example/db/schema.rb
|
80
|
-
- example/db/test.sqlite3
|
81
64
|
- example/public/404.html
|
82
65
|
- example/public/422.html
|
83
66
|
- example/public/500.html
|
@@ -111,22 +94,19 @@ files:
|
|
111
94
|
- example/test/functional/accounts_controller_test.rb
|
112
95
|
- example/test/integration/account_figments_test.rb
|
113
96
|
- example/test/test_helper.rb
|
114
|
-
- example/vendor/plugins/data_fabric/init.rb
|
115
|
-
- example/vendor/plugins/data_fabric/lib/data_fabric.rb
|
116
97
|
- init.rb
|
117
98
|
- lib/data_fabric/version.rb
|
118
99
|
- lib/data_fabric.rb
|
100
|
+
- Manifest
|
119
101
|
- Rakefile
|
120
102
|
- README.rdoc
|
121
103
|
- test/connection_test.rb
|
122
|
-
- test/database.yml
|
104
|
+
- test/database.yml.example
|
123
105
|
- test/database_test.rb
|
124
106
|
- test/shard_test.rb
|
125
107
|
- test/test_helper.rb
|
126
108
|
- test/thread_test.rb
|
127
109
|
- TESTING.rdoc
|
128
|
-
- TODO
|
129
|
-
- Manifest
|
130
110
|
- data_fabric.gemspec
|
131
111
|
has_rdoc: true
|
132
112
|
homepage: http://github.com/fiveruns/data_fabric
|
@@ -148,9 +128,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
148
128
|
version:
|
149
129
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
130
|
requirements:
|
151
|
-
- - "
|
131
|
+
- - ">="
|
152
132
|
- !ruby/object:Gem::Version
|
153
|
-
version: "
|
133
|
+
version: "0"
|
154
134
|
version:
|
155
135
|
requirements: []
|
156
136
|
|
@@ -10,7 +10,7 @@ class AccountFigmentsTest < ActionController::IntegrationTest
|
|
10
10
|
assert_equal 0, conn0.count_for('figments')
|
11
11
|
assert_equal 0, conn1.count_for('figments')
|
12
12
|
|
13
|
-
new_session do |user|
|
13
|
+
new_session(0) do |user|
|
14
14
|
user.goes_home
|
15
15
|
mike = user.creates_account('mike', '0')
|
16
16
|
user.selects_account(mike)
|
@@ -25,7 +25,7 @@ class AccountFigmentsTest < ActionController::IntegrationTest
|
|
25
25
|
assert_equal 1, conn0.count_for('figments')
|
26
26
|
assert_equal 0, conn1.count_for('figments')
|
27
27
|
|
28
|
-
new_session do |user|
|
28
|
+
new_session(1) do |user|
|
29
29
|
user.goes_home
|
30
30
|
bob = user.creates_account('bob', '1')
|
31
31
|
user.selects_account(bob)
|
@@ -54,10 +54,12 @@ class AccountFigmentsTest < ActionController::IntegrationTest
|
|
54
54
|
conn
|
55
55
|
end
|
56
56
|
|
57
|
-
def new_session
|
57
|
+
def new_session(shard)
|
58
58
|
open_session do |sess|
|
59
|
-
|
60
|
-
|
59
|
+
DataFabric.activate_shard :shard => shard do
|
60
|
+
sess.extend(Operations)
|
61
|
+
yield sess if block_given?
|
62
|
+
end
|
61
63
|
end
|
62
64
|
end
|
63
65
|
|
data/lib/data_fabric.rb
CHANGED
@@ -36,29 +36,41 @@ require 'active_record/version'
|
|
36
36
|
# end
|
37
37
|
# end
|
38
38
|
module DataFabric
|
39
|
-
VERSION = "1.0.0"
|
40
39
|
|
41
40
|
def self.logger
|
42
41
|
ActiveRecord::Base.logger
|
43
42
|
end
|
44
43
|
|
45
44
|
def self.init
|
46
|
-
logger.info "Loading data_fabric #{
|
45
|
+
logger.info "Loading data_fabric #{DataFabric::Version::STRING} with ActiveRecord #{ActiveRecord::VERSION::STRING}"
|
47
46
|
ActiveRecord::Base.send(:include, self)
|
48
47
|
end
|
49
48
|
|
49
|
+
mattr_writer :debugging
|
50
|
+
@@debugging = false
|
51
|
+
|
52
|
+
def self.debugging?
|
53
|
+
if @@debugging.nil? && logger
|
54
|
+
logger.debug?
|
55
|
+
else
|
56
|
+
!!@@debugging
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
50
60
|
def self.activate_shard(shards, &block)
|
51
61
|
ensure_setup
|
52
|
-
|
62
|
+
|
63
|
+
# Save the old shard settings to handle nested activation
|
64
|
+
old = Thread.current[:shards].dup
|
65
|
+
|
66
|
+
shards.each_pair do |key, value|
|
53
67
|
Thread.current[:shards][key.to_s] = value.to_s
|
54
68
|
end
|
55
69
|
if block_given?
|
56
70
|
begin
|
57
71
|
yield
|
58
72
|
ensure
|
59
|
-
shards
|
60
|
-
Thread.current[:shards].delete(key.to_s)
|
61
|
-
end
|
73
|
+
Thread.current[:shards] = old
|
62
74
|
end
|
63
75
|
end
|
64
76
|
end
|
@@ -128,7 +140,7 @@ module DataFabric
|
|
128
140
|
|
129
141
|
delegate :insert, :update, :delete, :create_table, :rename_table, :drop_table, :add_column, :remove_column,
|
130
142
|
:change_column, :change_column_default, :rename_column, :add_index, :remove_index, :initialize_schema_information,
|
131
|
-
:dump_schema_information, :to => :master
|
143
|
+
:dump_schema_information, :execute, :to => :master
|
132
144
|
|
133
145
|
def transaction(start_db_transaction = true, &block)
|
134
146
|
with_master { raw_connection.transaction(start_db_transaction, &block) }
|
@@ -139,10 +151,10 @@ module DataFabric
|
|
139
151
|
raw_connection
|
140
152
|
@role_changed = false
|
141
153
|
end
|
142
|
-
if
|
154
|
+
if DataFabric.debugging?
|
143
155
|
logger.debug("Calling #{method} on #{@cached_connection}")
|
144
156
|
end
|
145
|
-
|
157
|
+
raw_connection.send(method, *args, &block)
|
146
158
|
end
|
147
159
|
|
148
160
|
def connection_name
|
@@ -184,8 +196,8 @@ module DataFabric
|
|
184
196
|
config = ActiveRecord::Base.configurations[conn_name]
|
185
197
|
raise ArgumentError, "Unknown database config: #{conn_name}, have #{ActiveRecord::Base.configurations.inspect}" unless config
|
186
198
|
@model_class.establish_connection config
|
187
|
-
if
|
188
|
-
logger.debug "Switching from #{@current_connection_name} to #{conn_name}"
|
199
|
+
if DataFabric.debugging?
|
200
|
+
logger.debug "Switching from #{@current_connection_name || "(none)"} to #{conn_name}"
|
189
201
|
end
|
190
202
|
@current_connection_name = conn_name
|
191
203
|
conn = @model_class.connection
|
data/lib/data_fabric/version.rb
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
# The unit tests make use of the data populated in these databases.
|
2
|
+
#
|
3
|
+
# Notes:
|
4
|
+
# - The database identifiers (e.g. "fiveruns_city_austin_test_master") MUST NOT
|
5
|
+
# be changed! Everything else may be changed.
|
6
|
+
# - The user defined for "fiveruns_city_austin_test_master" MUST have the
|
7
|
+
# privilege to create and drop databases and tables.
|
8
|
+
|
9
|
+
|
1
10
|
fiveruns_city_austin_test_master:
|
2
11
|
adapter: mysql
|
3
12
|
host: localhost
|
data/test/database_test.rb
CHANGED
@@ -9,8 +9,7 @@ end
|
|
9
9
|
class DatabaseTest < Test::Unit::TestCase
|
10
10
|
|
11
11
|
def setup
|
12
|
-
|
13
|
-
ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read(filename)).result)
|
12
|
+
ActiveRecord::Base.configurations = load_database_yml
|
14
13
|
end
|
15
14
|
|
16
15
|
def test_live_burrito
|
@@ -36,4 +35,4 @@ class DatabaseTest < Test::Unit::TestCase
|
|
36
35
|
end
|
37
36
|
end
|
38
37
|
end
|
39
|
-
end
|
38
|
+
end
|
data/test/shard_test.rb
CHANGED
@@ -21,4 +21,38 @@ class ShardTest < Test::Unit::TestCase
|
|
21
21
|
assert_equal 'dallas', DataFabric.active_shard(:city)
|
22
22
|
end.join
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
|
+
def test_activations_can_be_nested
|
26
|
+
DataFabric.activate_shard(:name => 'Belldandy') do
|
27
|
+
DataFabric.activate_shard(:technique => 'Thousand Years of Pain') do
|
28
|
+
DataFabric.activate_shard(:name => 'Lelouche') do
|
29
|
+
DataFabric.activate_shard(:technique => 'Shadow Replication') do
|
30
|
+
assert_equal 'Shadow Replication', DataFabric.active_shard(:technique)
|
31
|
+
assert_equal 'Lelouche', DataFabric.active_shard(:name)
|
32
|
+
end
|
33
|
+
assert_equal 'Thousand Years of Pain', DataFabric.active_shard(:technique)
|
34
|
+
assert_equal 'Lelouche', DataFabric.active_shard(:name)
|
35
|
+
end
|
36
|
+
assert_equal 'Thousand Years of Pain', DataFabric.active_shard(:technique)
|
37
|
+
assert_equal 'Belldandy', DataFabric.active_shard(:name)
|
38
|
+
end
|
39
|
+
assert_raises ArgumentError do
|
40
|
+
DataFabric.active_shard(:technique)
|
41
|
+
end
|
42
|
+
assert_equal 'Belldandy', DataFabric.active_shard(:name)
|
43
|
+
end
|
44
|
+
assert_raises ArgumentError do
|
45
|
+
DataFabric.active_shard(:technique)
|
46
|
+
end
|
47
|
+
assert_raises ArgumentError do
|
48
|
+
DataFabric.active_shard(:name)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_activate_shard_returns_result_of_block
|
53
|
+
result = DataFabric.activate_shard(:gundam => 'Exia') do
|
54
|
+
1234
|
55
|
+
end
|
56
|
+
assert_equal 1234, result
|
57
|
+
end
|
58
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -1,17 +1,34 @@
|
|
1
|
-
|
2
|
-
RAILS_ENV='test'
|
1
|
+
if !defined?(ROOT_PATH) # Don't evaluate this file twice.
|
2
|
+
ENV['RAILS_ENV'] = 'test'
|
3
|
+
RAILS_ENV = 'test'
|
4
|
+
ROOT_PATH = File.expand_path(File.join(File.dirname(__FILE__), ".."))
|
5
|
+
DATABASE_YML_PATH = File.join(ROOT_PATH, "test", "database.yml")
|
6
|
+
Dir.chdir(ROOT_PATH)
|
3
7
|
|
4
|
-
require 'rubygems'
|
5
|
-
require 'test/unit'
|
8
|
+
require 'rubygems'
|
9
|
+
require 'test/unit'
|
6
10
|
|
7
|
-
# Bootstrap AR
|
8
|
-
gem 'activerecord', '=2.0.2'
|
9
|
-
require 'active_record'
|
10
|
-
require 'active_record/version'
|
11
|
-
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
12
|
-
ActiveRecord::Base.logger.level = Logger::WARN
|
13
|
-
ActiveRecord::Base.allow_concurrency = false
|
11
|
+
# Bootstrap AR
|
12
|
+
gem 'activerecord', '=2.0.2'
|
13
|
+
require 'active_record'
|
14
|
+
require 'active_record/version'
|
15
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
16
|
+
ActiveRecord::Base.logger.level = Logger::WARN
|
17
|
+
ActiveRecord::Base.allow_concurrency = false
|
14
18
|
|
15
|
-
# Bootstrap DF
|
16
|
-
Dependencies.load_paths << File.join(File.dirname(__FILE__), '../lib')
|
17
|
-
require 'init'
|
19
|
+
# Bootstrap DF
|
20
|
+
Dependencies.load_paths << File.join(File.dirname(__FILE__), '../lib')
|
21
|
+
require 'init'
|
22
|
+
|
23
|
+
def load_database_yml
|
24
|
+
filename = DATABASE_YML_PATH
|
25
|
+
YAML::load(ERB.new(IO.read(filename)).result)
|
26
|
+
end
|
27
|
+
|
28
|
+
if !File.exist?(DATABASE_YML_PATH)
|
29
|
+
STDERR.puts "\n*** ERROR ***:\n" <<
|
30
|
+
"You must have a 'test/database.yml' file in order to run the unit tests. " <<
|
31
|
+
"An example is provided in 'test/database.yml.example'.\n\n"
|
32
|
+
exit 1
|
33
|
+
end
|
34
|
+
end
|
data/test/thread_test.rb
CHANGED
@@ -18,8 +18,7 @@ class ThreadTest < Test::Unit::TestCase
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def xtest_class_and_instance_connections
|
21
|
-
|
22
|
-
ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read(filename)).result)
|
21
|
+
ActiveRecord::Base.configurations = load_database_yml
|
23
22
|
|
24
23
|
cconn = ThreadedEnchilada.connection
|
25
24
|
iconn = ThreadedEnchilada.new.connection
|
@@ -30,7 +29,7 @@ class ThreadTest < Test::Unit::TestCase
|
|
30
29
|
clear_databases
|
31
30
|
|
32
31
|
filename = File.join(File.dirname(__FILE__), "database.yml")
|
33
|
-
ActiveRecord::Base.configurations =
|
32
|
+
ActiveRecord::Base.configurations = load_database_yml
|
34
33
|
|
35
34
|
counts = {:austin => 0, :dallas => 0}
|
36
35
|
threads = []
|
@@ -88,4 +87,4 @@ class ThreadTest < Test::Unit::TestCase
|
|
88
87
|
def using_connection(&block)
|
89
88
|
ActiveRecord::Base.connection.instance_eval(&block)
|
90
89
|
end
|
91
|
-
end
|
90
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: data_fabric
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Perham
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-09-30 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -22,16 +22,6 @@ dependencies:
|
|
22
22
|
- !ruby/object:Gem::Version
|
23
23
|
version: 2.0.2
|
24
24
|
version:
|
25
|
-
- !ruby/object:Gem::Dependency
|
26
|
-
name: echoe
|
27
|
-
type: :development
|
28
|
-
version_requirement:
|
29
|
-
version_requirements: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: "0"
|
34
|
-
version:
|
35
25
|
description: Sharding and replication support for ActiveRecord 2.x
|
36
26
|
email: mperham@gmail.com
|
37
27
|
executables: []
|
@@ -43,7 +33,6 @@ extra_rdoc_files:
|
|
43
33
|
- lib/data_fabric/version.rb
|
44
34
|
- lib/data_fabric.rb
|
45
35
|
- README.rdoc
|
46
|
-
- TODO
|
47
36
|
files:
|
48
37
|
- CHANGELOG
|
49
38
|
- example/app/controllers/accounts_controller.rb
|
@@ -66,15 +55,9 @@ files:
|
|
66
55
|
- example/config/initializers/mime_types.rb
|
67
56
|
- example/config/initializers/new_rails_defaults.rb
|
68
57
|
- example/config/routes.rb
|
69
|
-
- example/db/development.sqlite3
|
70
58
|
- example/db/migrate/20080702154628_create_accounts.rb
|
71
59
|
- example/db/migrate/20080702154820_create_figments.rb
|
72
|
-
- example/db/s0_development.sqlite3
|
73
|
-
- example/db/s0_test.sqlite3
|
74
|
-
- example/db/s1_development.sqlite3
|
75
|
-
- example/db/s1_test.sqlite3
|
76
60
|
- example/db/schema.rb
|
77
|
-
- example/db/test.sqlite3
|
78
61
|
- example/public/404.html
|
79
62
|
- example/public/422.html
|
80
63
|
- example/public/500.html
|
@@ -108,22 +91,19 @@ files:
|
|
108
91
|
- example/test/functional/accounts_controller_test.rb
|
109
92
|
- example/test/integration/account_figments_test.rb
|
110
93
|
- example/test/test_helper.rb
|
111
|
-
- example/vendor/plugins/data_fabric/init.rb
|
112
|
-
- example/vendor/plugins/data_fabric/lib/data_fabric.rb
|
113
94
|
- init.rb
|
114
95
|
- lib/data_fabric/version.rb
|
115
96
|
- lib/data_fabric.rb
|
97
|
+
- Manifest
|
116
98
|
- Rakefile
|
117
99
|
- README.rdoc
|
118
100
|
- test/connection_test.rb
|
119
|
-
- test/database.yml
|
101
|
+
- test/database.yml.example
|
120
102
|
- test/database_test.rb
|
121
103
|
- test/shard_test.rb
|
122
104
|
- test/test_helper.rb
|
123
105
|
- test/thread_test.rb
|
124
106
|
- TESTING.rdoc
|
125
|
-
- TODO
|
126
|
-
- Manifest
|
127
107
|
- data_fabric.gemspec
|
128
108
|
has_rdoc: true
|
129
109
|
homepage: http://github.com/fiveruns/data_fabric
|
@@ -145,9 +125,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
145
125
|
version:
|
146
126
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
147
127
|
requirements:
|
148
|
-
- - "
|
128
|
+
- - ">="
|
149
129
|
- !ruby/object:Gem::Version
|
150
|
-
version: "
|
130
|
+
version: "0"
|
151
131
|
version:
|
152
132
|
requirements: []
|
153
133
|
|
data/TODO
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
Process or Documentation for how to migrate existing data to shards
|
Binary file
|
Binary file
|
data/example/db/s0_test.sqlite3
DELETED
Binary file
|
Binary file
|
data/example/db/s1_test.sqlite3
DELETED
Binary file
|
data/example/db/test.sqlite3
DELETED
Binary file
|
@@ -1 +0,0 @@
|
|
1
|
-
DataFabric.init
|
@@ -1,231 +0,0 @@
|
|
1
|
-
require 'active_record'
|
2
|
-
require 'active_record/version'
|
3
|
-
|
4
|
-
# DataFabric adds a new level of flexibility to ActiveRecord connection handling.
|
5
|
-
# You need to describe the topology for your database infrastructure in your model(s). As with ActiveRecord normally, different models can use different topologies.
|
6
|
-
#
|
7
|
-
# class MyHugeVolumeOfDataModel < ActiveRecord::Base
|
8
|
-
# connection_topology :replicated => true, :shard_by => :city
|
9
|
-
# end
|
10
|
-
#
|
11
|
-
# There are four supported modes of operation, depending on the options given to the connection_topology method. The plugin will look for connections in your config/database.yml with the following convention:
|
12
|
-
#
|
13
|
-
# No connection topology:
|
14
|
-
# #{environment} - this is the default, as with ActiveRecord, e.g. "production"
|
15
|
-
#
|
16
|
-
# connection_topology :replicated => true
|
17
|
-
# #{environment}_#{role} - no sharding, just replication, where role is "master" or "slave", e.g. "production_master"
|
18
|
-
#
|
19
|
-
# connection_topology :shard_by => :city
|
20
|
-
# #{group}_#{shard}_#{environment} - sharding, no replication, e.g. "city_austin_production"
|
21
|
-
#
|
22
|
-
# connection_topology :replicated => true, :shard_by => :city
|
23
|
-
# #{group}_#{shard}_#{environment}_#{role} - sharding with replication, e.g. "city_austin_production_master"
|
24
|
-
#
|
25
|
-
#
|
26
|
-
# When marked as replicated, all write and transactional operations for the model go to the master, whereas read operations go to the slave.
|
27
|
-
#
|
28
|
-
# Since sharding is an application-level concern, your application must set the shard to use based on the current request or environment. The current shard for a group is set on a thread local variable. For example, you can set the shard in an ActionController around_filter based on the user as follows:
|
29
|
-
#
|
30
|
-
# class ApplicationController < ActionController::Base
|
31
|
-
# around_filter :select_shard
|
32
|
-
#
|
33
|
-
# private
|
34
|
-
# def select_shard(&action_block)
|
35
|
-
# DataFabric.activate_shard(:city => @current_user.city, &action_block)
|
36
|
-
# end
|
37
|
-
# end
|
38
|
-
module DataFabric
|
39
|
-
VERSION = "1.0.0"
|
40
|
-
|
41
|
-
def self.logger
|
42
|
-
ActiveRecord::Base.logger
|
43
|
-
end
|
44
|
-
|
45
|
-
def self.init
|
46
|
-
logger.info "Loading data_fabric #{VERSION} with ActiveRecord #{ActiveRecord::VERSION::STRING}"
|
47
|
-
ActiveRecord::Base.send(:include, self)
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.activate_shard(shards, &block)
|
51
|
-
ensure_setup
|
52
|
-
shards.each do |key, value|
|
53
|
-
Thread.current[:shards][key.to_s] = value.to_s
|
54
|
-
end
|
55
|
-
if block_given?
|
56
|
-
begin
|
57
|
-
yield
|
58
|
-
ensure
|
59
|
-
shards.each do |key, value|
|
60
|
-
Thread.current[:shards].delete(key.to_s)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
# For cases where you can't pass a block to activate_shards, you can
|
67
|
-
# clean up the thread local settings by calling this method at the
|
68
|
-
# end of processing
|
69
|
-
def self.deactivate_shard(shards)
|
70
|
-
ensure_setup
|
71
|
-
shards.each do |key, value|
|
72
|
-
Thread.current[:shards].delete(key.to_s)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def self.active_shard(group)
|
77
|
-
raise ArgumentError, 'No shard has been activated' unless Thread.current[:shards]
|
78
|
-
|
79
|
-
returning(Thread.current[:shards][group.to_s]) do |shard|
|
80
|
-
raise ArgumentError, "No active shard for #{group}" unless shard
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def self.included(model)
|
85
|
-
# Wire up ActiveRecord::Base
|
86
|
-
model.extend ClassMethods
|
87
|
-
end
|
88
|
-
|
89
|
-
def self.ensure_setup
|
90
|
-
Thread.current[:shards] = {} unless Thread.current[:shards]
|
91
|
-
end
|
92
|
-
|
93
|
-
# Class methods injected into ActiveRecord::Base
|
94
|
-
module ClassMethods
|
95
|
-
def connection_topology(options)
|
96
|
-
proxy = DataFabric::ConnectionProxy.new(self, options)
|
97
|
-
ActiveRecord::Base.active_connections[name] = proxy
|
98
|
-
|
99
|
-
raise ArgumentError, "data_fabric does not support ActiveRecord's allow_concurrency = true" if allow_concurrency
|
100
|
-
DataFabric.logger.info "Creating data_fabric proxy for class #{name}"
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
class StringProxy
|
105
|
-
def initialize(&block)
|
106
|
-
@proc = block
|
107
|
-
end
|
108
|
-
def to_s
|
109
|
-
@proc.call
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
class ConnectionProxy
|
114
|
-
def initialize(model_class, options)
|
115
|
-
@model_class = model_class
|
116
|
-
@replicated = options[:replicated]
|
117
|
-
@shard_group = options[:shard_by]
|
118
|
-
@prefix = options[:prefix]
|
119
|
-
@current_role = 'slave' if @replicated
|
120
|
-
@current_connection_name_builder = connection_name_builder
|
121
|
-
@cached_connection = nil
|
122
|
-
@current_connection_name = nil
|
123
|
-
@role_changed = false
|
124
|
-
|
125
|
-
@model_class.send :include, ActiveRecordConnectionMethods if @replicated
|
126
|
-
end
|
127
|
-
|
128
|
-
delegate :insert, :update, :delete, :create_table, :rename_table, :drop_table, :add_column, :remove_column,
|
129
|
-
:change_column, :change_column_default, :rename_column, :add_index, :remove_index, :initialize_schema_information,
|
130
|
-
:dump_schema_information, :to => :master
|
131
|
-
|
132
|
-
def transaction(start_db_transaction = true, &block)
|
133
|
-
with_master { raw_connection.transaction(start_db_transaction, &block) }
|
134
|
-
end
|
135
|
-
|
136
|
-
def method_missing(method, *args, &block)
|
137
|
-
unless @cached_connection and !@role_changed
|
138
|
-
raw_connection
|
139
|
-
@role_changed = false
|
140
|
-
end
|
141
|
-
if logger.debug?
|
142
|
-
logger.debug("Calling #{method} on #{@cached_connection}")
|
143
|
-
end
|
144
|
-
@cached_connection.send(method, *args, &block)
|
145
|
-
end
|
146
|
-
|
147
|
-
def connection_name
|
148
|
-
@current_connection_name_builder.join('_')
|
149
|
-
end
|
150
|
-
|
151
|
-
def disconnect!
|
152
|
-
@cached_connection.disconnect! if @cached_connection
|
153
|
-
@cached_connection = nil
|
154
|
-
end
|
155
|
-
|
156
|
-
def verify!(arg)
|
157
|
-
@cached_connection.verify!(0) if @cached_connection
|
158
|
-
end
|
159
|
-
|
160
|
-
def with_master
|
161
|
-
set_role('master')
|
162
|
-
yield
|
163
|
-
ensure
|
164
|
-
set_role('slave')
|
165
|
-
end
|
166
|
-
|
167
|
-
private
|
168
|
-
|
169
|
-
def connection_name_builder
|
170
|
-
clauses = []
|
171
|
-
clauses << @prefix if @prefix
|
172
|
-
clauses << @shard_group if @shard_group
|
173
|
-
clauses << StringProxy.new { DataFabric.active_shard(@shard_group) } if @shard_group
|
174
|
-
clauses << RAILS_ENV
|
175
|
-
clauses << StringProxy.new { @current_role } if @replicated
|
176
|
-
clauses
|
177
|
-
end
|
178
|
-
|
179
|
-
def raw_connection
|
180
|
-
conn_name = connection_name
|
181
|
-
unless already_connected_to? conn_name
|
182
|
-
@cached_connection = begin
|
183
|
-
config = ActiveRecord::Base.configurations[conn_name]
|
184
|
-
raise ArgumentError, "Unknown database config: #{conn_name}, have #{ActiveRecord::Base.configurations.inspect}" unless config
|
185
|
-
@model_class.establish_connection config
|
186
|
-
if logger.debug?
|
187
|
-
logger.debug "Switching from #{@current_connection_name} to #{conn_name}"
|
188
|
-
end
|
189
|
-
@current_connection_name = conn_name
|
190
|
-
conn = @model_class.connection
|
191
|
-
conn.verify! 0
|
192
|
-
conn
|
193
|
-
end
|
194
|
-
@model_class.active_connections[@model_class.name] = self
|
195
|
-
end
|
196
|
-
@cached_connection
|
197
|
-
end
|
198
|
-
|
199
|
-
def already_connected_to?(conn_name)
|
200
|
-
conn_name == @current_connection_name and @cached_connection
|
201
|
-
end
|
202
|
-
|
203
|
-
def set_role(role)
|
204
|
-
if @replicated and @current_role != role
|
205
|
-
@current_role = role
|
206
|
-
@role_changed = true
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
def master
|
211
|
-
set_role('master')
|
212
|
-
return raw_connection
|
213
|
-
ensure
|
214
|
-
set_role('slave')
|
215
|
-
end
|
216
|
-
|
217
|
-
def logger
|
218
|
-
DataFabric.logger
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
module ActiveRecordConnectionMethods
|
223
|
-
def self.included(base)
|
224
|
-
base.alias_method_chain :reload, :master
|
225
|
-
end
|
226
|
-
|
227
|
-
def reload_with_master(*args, &block)
|
228
|
-
connection.with_master { reload_without_master }
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|