data_fabric 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|