data_fabric 1.0.2 → 1.1.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.
- data/CHANGELOG +13 -0
- data/Manifest +2 -1
- data/Rakefile +33 -17
- data/TESTING.rdoc +5 -7
- data/data_fabric.gemspec +28 -142
- data/lib/data_fabric/version.rb +1 -1
- data/lib/data_fabric.rb +70 -83
- data/test/connection_test.rb +4 -4
- data/test/database.yml +24 -0
- data/test/{database.yml.example → database.yml.mysql} +0 -0
- data/test/database_test.rb +4 -4
- data/test/thread_test.rb +1 -1
- metadata +9 -17
data/CHANGELOG
CHANGED
@@ -1 +1,14 @@
|
|
1
|
+
DataFabric changelog
|
2
|
+
|
3
|
+
v1.1.0 - 2008-11-22
|
4
|
+
|
5
|
+
- Cache connections so we don't have to reconnect constantly. (Justin Balthrop, Geni)
|
6
|
+
- Clean up logging API (not backwards-compatible)
|
7
|
+
- Verify wrapped connections automatically so cached mysql connections older than 8 hours
|
8
|
+
don't die due to timeout.
|
9
|
+
- Please note this version is not Rails 2.2-compatible. DataFabric 2.0 will be.
|
10
|
+
|
11
|
+
|
12
|
+
Detailed commit history:
|
13
|
+
|
1
14
|
http://github.com/fiveruns/data_fabric/commits/master
|
data/Manifest
CHANGED
data/Rakefile
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'echoe'
|
3
3
|
|
4
|
+
#gem 'rails', '=2.0.2'
|
5
|
+
|
4
6
|
require File.dirname(__FILE__) << "/lib/data_fabric/version"
|
5
7
|
|
6
8
|
Echoe.new 'data_fabric' do |p|
|
@@ -8,14 +10,16 @@ Echoe.new 'data_fabric' do |p|
|
|
8
10
|
p.author = "Mike Perham"
|
9
11
|
p.email = 'mperham@gmail.com'
|
10
12
|
p.project = 'fiveruns'
|
11
|
-
p.summary = 'Sharding and replication support for ActiveRecord 2.
|
13
|
+
p.summary = 'Sharding and replication support for ActiveRecord 2.0 and 2.1'
|
12
14
|
p.url = "http://github.com/fiveruns/data_fabric"
|
13
|
-
p.dependencies = ['activerecord >=2.0.2']
|
15
|
+
# p.dependencies = ['activerecord >=2.0.2']
|
14
16
|
p.development_dependencies = []
|
15
17
|
p.rubygems_version = nil
|
16
18
|
p.include_rakefile = true
|
17
19
|
end
|
18
20
|
|
21
|
+
task :test => [:pretest]
|
22
|
+
|
19
23
|
task :pretest do
|
20
24
|
setup(false)
|
21
25
|
end
|
@@ -33,7 +37,7 @@ def load_database_yml
|
|
33
37
|
if !File.exist?(filename)
|
34
38
|
STDERR.puts "\n*** ERROR ***:\n" <<
|
35
39
|
"You must have a 'test/database.yml' file in order to create the test database. " <<
|
36
|
-
"An example is provided in 'test/database.yml.
|
40
|
+
"An example is provided in 'test/database.yml.mysql'.\n\n"
|
37
41
|
exit 1
|
38
42
|
end
|
39
43
|
YAML::load(ERB.new(IO.read(filename)).result)
|
@@ -42,12 +46,12 @@ end
|
|
42
46
|
def setup_connection
|
43
47
|
require 'active_record'
|
44
48
|
ActiveRecord::Base.configurations = load_database_yml
|
45
|
-
ActiveRecord::Base.establish_connection('fiveruns_city_austin_test_master')
|
46
49
|
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
47
|
-
ActiveRecord::Base.logger.level = Logger::
|
50
|
+
ActiveRecord::Base.logger.level = Logger::INFO
|
48
51
|
end
|
49
52
|
|
50
53
|
def using_connection(database_identifier, &block)
|
54
|
+
ActiveRecord::Base.establish_connection(database_identifier)
|
51
55
|
ActiveRecord::Base.connection.instance_eval(&block)
|
52
56
|
end
|
53
57
|
|
@@ -56,18 +60,30 @@ def setup(create = false)
|
|
56
60
|
|
57
61
|
ActiveRecord::Base.configurations.each_pair do |identifier, config|
|
58
62
|
using_connection(identifier) do
|
59
|
-
|
60
|
-
if create
|
61
|
-
execute "drop database if exists #{db_name}"
|
62
|
-
execute "create database #{db_name}"
|
63
|
-
end
|
64
|
-
execute "use #{db_name}"
|
65
|
-
execute "drop table if exists the_whole_burritos"
|
66
|
-
execute "drop table if exists enchiladas"
|
67
|
-
execute "create table enchiladas (id integer not null auto_increment, name varchar(30) not null, primary key(id))"
|
68
|
-
execute "insert into enchiladas (id, name) values (1, '#{db_name}')"
|
69
|
-
execute "create table the_whole_burritos (id integer not null auto_increment, name varchar(30) not null, primary key(id))"
|
70
|
-
execute "insert into the_whole_burritos (id, name) values (1, '#{db_name}')"
|
63
|
+
send("create_#{config['adapter']}", create, config['database'])
|
71
64
|
end
|
72
65
|
end
|
73
66
|
end
|
67
|
+
|
68
|
+
def create_sqlite3(create, db_name)
|
69
|
+
execute "drop table if exists the_whole_burritos"
|
70
|
+
execute "drop table if exists enchiladas"
|
71
|
+
execute "create table enchiladas (id integer not null primary key, name varchar(30) not null)"
|
72
|
+
execute "insert into enchiladas (id, name) values (1, '#{db_name}')"
|
73
|
+
execute "create table the_whole_burritos (id integer not null primary key, name varchar(30) not null)"
|
74
|
+
execute "insert into the_whole_burritos (id, name) values (1, '#{db_name}')"
|
75
|
+
end
|
76
|
+
|
77
|
+
def create_mysql(create, db_name)
|
78
|
+
if create
|
79
|
+
execute "drop database if exists #{db_name}"
|
80
|
+
execute "create database #{db_name}"
|
81
|
+
end
|
82
|
+
execute "use #{db_name}"
|
83
|
+
execute "drop table if exists the_whole_burritos"
|
84
|
+
execute "drop table if exists enchiladas"
|
85
|
+
execute "create table enchiladas (id integer not null auto_increment, name varchar(30) not null, primary key(id))"
|
86
|
+
execute "insert into enchiladas (id, name) values (1, '#{db_name}')"
|
87
|
+
execute "create table the_whole_burritos (id integer not null auto_increment, name varchar(30) not null, primary key(id))"
|
88
|
+
execute "insert into the_whole_burritos (id, name) values (1, '#{db_name}')"
|
89
|
+
end
|
data/TESTING.rdoc
CHANGED
@@ -4,10 +4,10 @@ data_fabric has two layers of tests: unit tests and integration tests.
|
|
4
4
|
== Running the Unit Tests
|
5
5
|
|
6
6
|
The unit tests test both with and without an actual database. test/database_test.rb
|
7
|
-
tests against a
|
8
|
-
required. You
|
9
|
-
|
10
|
-
|
7
|
+
tests against a database. The other unit tests mock AR so no actual database is
|
8
|
+
required. You can use the standard test/database.yml which tests against SQLite3
|
9
|
+
or customize the provided test/database.yml.mysql. The "rake create_db" task will
|
10
|
+
set up the necessary databases and tables.
|
11
11
|
|
12
12
|
|
13
13
|
== Running the Integration Tests
|
@@ -27,6 +27,4 @@ then be able to run the example application's tests.
|
|
27
27
|
|
28
28
|
== Submitting Bugs
|
29
29
|
|
30
|
-
If you think you've found a problem with data_fabric,
|
31
|
-
which reproduces the problem. This should make tracking down and fixing the bug relatively
|
32
|
-
easy and provides a regression test for future releases.
|
30
|
+
If you think you've found a problem with data_fabric, contact me at mperham AT gmail.com.
|
data/data_fabric.gemspec
CHANGED
@@ -1,146 +1,32 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
1
2
|
|
2
|
-
|
3
|
-
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{data_fabric}
|
5
|
+
s.version = "1.1.0"
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Mike Perham"]
|
9
|
+
s.date = %q{2008-11-22}
|
10
|
+
s.description = %q{Sharding and replication support for ActiveRecord 2.0 and 2.1}
|
11
|
+
s.email = %q{mperham@gmail.com}
|
12
|
+
s.extra_rdoc_files = ["CHANGELOG", "lib/data_fabric/version.rb", "lib/data_fabric.rb", "README.rdoc"]
|
13
|
+
s.files = ["CHANGELOG", "example/app/controllers/accounts_controller.rb", "example/app/controllers/application.rb", "example/app/controllers/figments_controller.rb", "example/app/helpers/accounts_helper.rb", "example/app/helpers/application_helper.rb", "example/app/helpers/figments_helper.rb", "example/app/models/account.rb", "example/app/models/figment.rb", "example/app/views/accounts/index.html.erb", "example/app/views/layouts/application.html.erb", "example/config/boot.rb", "example/config/database.yml", "example/config/environment.rb", "example/config/environments/development.rb", "example/config/environments/production.rb", "example/config/environments/test.rb", "example/config/initializers/inflections.rb", "example/config/initializers/mime_types.rb", "example/config/initializers/new_rails_defaults.rb", "example/config/routes.rb", "example/db/migrate/20080702154628_create_accounts.rb", "example/db/migrate/20080702154820_create_figments.rb", "example/db/schema.rb", "example/public/404.html", "example/public/422.html", "example/public/500.html", "example/public/dispatch.cgi", "example/public/dispatch.fcgi", "example/public/dispatch.rb", "example/public/favicon.ico", "example/public/images/rails.png", "example/public/javascripts/application.js", "example/public/javascripts/controls.js", "example/public/javascripts/dragdrop.js", "example/public/javascripts/effects.js", "example/public/javascripts/prototype.js", "example/public/robots.txt", "example/Rakefile", "example/script/about", "example/script/console", "example/script/dbconsole", "example/script/destroy", "example/script/generate", "example/script/performance/benchmarker", "example/script/performance/profiler", "example/script/performance/request", "example/script/plugin", "example/script/process/inspector", "example/script/process/reaper", "example/script/process/spawner", "example/script/runner", "example/script/server", "example/test/fixtures/accounts.yml", "example/test/functional/accounts_controller_test.rb", "example/test/integration/account_figments_test.rb", "example/test/test_helper.rb", "init.rb", "lib/data_fabric/version.rb", "lib/data_fabric.rb", "Manifest", "Rakefile", "README.rdoc", "test/connection_test.rb", "test/database.yml", "test/database.yml.mysql", "test/database_test.rb", "test/shard_test.rb", "test/test_helper.rb", "test/thread_test.rb", "TESTING.rdoc", "data_fabric.gemspec"]
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.homepage = %q{http://github.com/fiveruns/data_fabric}
|
16
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Data_fabric", "--main", "README.rdoc"]
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.rubyforge_project = %q{fiveruns}
|
19
|
+
s.rubygems_version = %q{1.3.1}
|
20
|
+
s.summary = %q{Sharding and replication support for ActiveRecord 2.0 and 2.1}
|
21
|
+
s.test_files = ["test/connection_test.rb", "test/database_test.rb", "test/shard_test.rb", "test/test_helper.rb", "test/thread_test.rb"]
|
14
22
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
- !ruby/object:Gem::Dependency
|
19
|
-
name: activerecord
|
20
|
-
type: :runtime
|
21
|
-
version_requirement:
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 2.0.2
|
27
|
-
version:
|
28
|
-
description: Sharding and replication support for ActiveRecord 2.x
|
29
|
-
email: mperham@gmail.com
|
30
|
-
executables: []
|
23
|
+
if s.respond_to? :specification_version then
|
24
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
25
|
+
s.specification_version = 2
|
31
26
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
- README.rdoc
|
39
|
-
files:
|
40
|
-
- CHANGELOG
|
41
|
-
- example/app/controllers/accounts_controller.rb
|
42
|
-
- example/app/controllers/application.rb
|
43
|
-
- example/app/controllers/figments_controller.rb
|
44
|
-
- example/app/helpers/accounts_helper.rb
|
45
|
-
- example/app/helpers/application_helper.rb
|
46
|
-
- example/app/helpers/figments_helper.rb
|
47
|
-
- example/app/models/account.rb
|
48
|
-
- example/app/models/figment.rb
|
49
|
-
- example/app/views/accounts/index.html.erb
|
50
|
-
- example/app/views/layouts/application.html.erb
|
51
|
-
- example/config/boot.rb
|
52
|
-
- example/config/database.yml
|
53
|
-
- example/config/environment.rb
|
54
|
-
- example/config/environments/development.rb
|
55
|
-
- example/config/environments/production.rb
|
56
|
-
- example/config/environments/test.rb
|
57
|
-
- example/config/initializers/inflections.rb
|
58
|
-
- example/config/initializers/mime_types.rb
|
59
|
-
- example/config/initializers/new_rails_defaults.rb
|
60
|
-
- example/config/routes.rb
|
61
|
-
- example/db/migrate/20080702154628_create_accounts.rb
|
62
|
-
- example/db/migrate/20080702154820_create_figments.rb
|
63
|
-
- example/db/schema.rb
|
64
|
-
- example/public/404.html
|
65
|
-
- example/public/422.html
|
66
|
-
- example/public/500.html
|
67
|
-
- example/public/dispatch.cgi
|
68
|
-
- example/public/dispatch.fcgi
|
69
|
-
- example/public/dispatch.rb
|
70
|
-
- example/public/favicon.ico
|
71
|
-
- example/public/images/rails.png
|
72
|
-
- example/public/javascripts/application.js
|
73
|
-
- example/public/javascripts/controls.js
|
74
|
-
- example/public/javascripts/dragdrop.js
|
75
|
-
- example/public/javascripts/effects.js
|
76
|
-
- example/public/javascripts/prototype.js
|
77
|
-
- example/public/robots.txt
|
78
|
-
- example/Rakefile
|
79
|
-
- example/script/about
|
80
|
-
- example/script/console
|
81
|
-
- example/script/dbconsole
|
82
|
-
- example/script/destroy
|
83
|
-
- example/script/generate
|
84
|
-
- example/script/performance/benchmarker
|
85
|
-
- example/script/performance/profiler
|
86
|
-
- example/script/performance/request
|
87
|
-
- example/script/plugin
|
88
|
-
- example/script/process/inspector
|
89
|
-
- example/script/process/reaper
|
90
|
-
- example/script/process/spawner
|
91
|
-
- example/script/runner
|
92
|
-
- example/script/server
|
93
|
-
- example/test/fixtures/accounts.yml
|
94
|
-
- example/test/functional/accounts_controller_test.rb
|
95
|
-
- example/test/integration/account_figments_test.rb
|
96
|
-
- example/test/test_helper.rb
|
97
|
-
- init.rb
|
98
|
-
- lib/data_fabric/version.rb
|
99
|
-
- lib/data_fabric.rb
|
100
|
-
- Manifest
|
101
|
-
- Rakefile
|
102
|
-
- README.rdoc
|
103
|
-
- test/connection_test.rb
|
104
|
-
- test/database.yml.example
|
105
|
-
- test/database_test.rb
|
106
|
-
- test/shard_test.rb
|
107
|
-
- test/test_helper.rb
|
108
|
-
- test/thread_test.rb
|
109
|
-
- TESTING.rdoc
|
110
|
-
- data_fabric.gemspec
|
111
|
-
has_rdoc: true
|
112
|
-
homepage: http://github.com/fiveruns/data_fabric
|
113
|
-
post_install_message:
|
114
|
-
rdoc_options:
|
115
|
-
- --line-numbers
|
116
|
-
- --inline-source
|
117
|
-
- --title
|
118
|
-
- Data_fabric
|
119
|
-
- --main
|
120
|
-
- README.rdoc
|
121
|
-
require_paths:
|
122
|
-
- lib
|
123
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
124
|
-
requirements:
|
125
|
-
- - ">="
|
126
|
-
- !ruby/object:Gem::Version
|
127
|
-
version: "0"
|
128
|
-
version:
|
129
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
-
requirements:
|
131
|
-
- - ">="
|
132
|
-
- !ruby/object:Gem::Version
|
133
|
-
version: "0"
|
134
|
-
version:
|
135
|
-
requirements: []
|
136
|
-
|
137
|
-
rubyforge_project: fiveruns
|
138
|
-
rubygems_version: 1.2.0
|
139
|
-
specification_version: 2
|
140
|
-
summary: Sharding and replication support for ActiveRecord 2.x
|
141
|
-
test_files:
|
142
|
-
- test/connection_test.rb
|
143
|
-
- test/database_test.rb
|
144
|
-
- test/shard_test.rb
|
145
|
-
- test/test_helper.rb
|
146
|
-
- test/thread_test.rb
|
27
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
28
|
+
else
|
29
|
+
end
|
30
|
+
else
|
31
|
+
end
|
32
|
+
end
|
data/lib/data_fabric/version.rb
CHANGED
data/lib/data_fabric.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
require 'active_record/version'
|
3
|
+
require 'data_fabric/version'
|
3
4
|
|
4
5
|
# DataFabric adds a new level of flexibility to ActiveRecord connection handling.
|
5
6
|
# You need to describe the topology for your database infrastructure in your model(s). As with ActiveRecord normally, different models can use different topologies.
|
@@ -36,27 +37,17 @@ require 'active_record/version'
|
|
36
37
|
# end
|
37
38
|
# end
|
38
39
|
module DataFabric
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
|
41
|
+
# Set this logger to log DataFabric operations.
|
42
|
+
# The logger should quack like a standard Ruby Logger.
|
43
|
+
mattr_accessor :logger
|
43
44
|
|
44
45
|
def self.init
|
45
|
-
logger
|
46
|
+
logger = ActiveRecord::Base.logger unless logger
|
47
|
+
log { "Loading data_fabric #{DataFabric::Version::STRING} with ActiveRecord #{ActiveRecord::VERSION::STRING}" }
|
46
48
|
ActiveRecord::Base.send(:include, self)
|
47
49
|
end
|
48
50
|
|
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
|
-
|
60
51
|
def self.activate_shard(shards, &block)
|
61
52
|
ensure_setup
|
62
53
|
|
@@ -93,6 +84,11 @@ module DataFabric
|
|
93
84
|
end
|
94
85
|
end
|
95
86
|
|
87
|
+
def self.shard_active_for?(group)
|
88
|
+
return true unless group
|
89
|
+
Thread.current[:shards] and Thread.current[:shards][group.to_s]
|
90
|
+
end
|
91
|
+
|
96
92
|
def self.included(model)
|
97
93
|
# Wire up ActiveRecord::Base
|
98
94
|
model.extend ClassMethods
|
@@ -101,7 +97,11 @@ module DataFabric
|
|
101
97
|
def self.ensure_setup
|
102
98
|
Thread.current[:shards] = {} unless Thread.current[:shards]
|
103
99
|
end
|
104
|
-
|
100
|
+
|
101
|
+
def self.log(level=Logger::INFO, &block)
|
102
|
+
logger && logger.add(level, &block)
|
103
|
+
end
|
104
|
+
|
105
105
|
# Class methods injected into ActiveRecord::Base
|
106
106
|
module ClassMethods
|
107
107
|
def data_fabric(options)
|
@@ -109,9 +109,8 @@ module DataFabric
|
|
109
109
|
ActiveRecord::Base.active_connections[name] = proxy
|
110
110
|
|
111
111
|
raise ArgumentError, "data_fabric does not support ActiveRecord's allow_concurrency = true" if allow_concurrency
|
112
|
-
DataFabric.
|
112
|
+
DataFabric.log { "Creating data_fabric proxy for class #{name}" }
|
113
113
|
end
|
114
|
-
alias :connection_topology :data_fabric # legacy
|
115
114
|
end
|
116
115
|
|
117
116
|
class StringProxy
|
@@ -126,109 +125,97 @@ module DataFabric
|
|
126
125
|
class ConnectionProxy
|
127
126
|
def initialize(model_class, options)
|
128
127
|
@model_class = model_class
|
129
|
-
@replicated
|
128
|
+
@replicated = options[:replicated]
|
130
129
|
@shard_group = options[:shard_by]
|
131
|
-
@prefix
|
132
|
-
@
|
133
|
-
@current_connection_name_builder = connection_name_builder
|
134
|
-
@cached_connection = nil
|
135
|
-
@current_connection_name = nil
|
136
|
-
@role_changed = false
|
130
|
+
@prefix = options[:prefix]
|
131
|
+
@role = 'slave' if @replicated
|
137
132
|
|
138
133
|
@model_class.send :include, ActiveRecordConnectionMethods if @replicated
|
139
134
|
end
|
140
|
-
|
135
|
+
|
141
136
|
delegate :insert, :update, :delete, :create_table, :rename_table, :drop_table, :add_column, :remove_column,
|
142
137
|
:change_column, :change_column_default, :rename_column, :add_index, :remove_index, :initialize_schema_information,
|
143
|
-
:dump_schema_information, :execute, :to => :master
|
138
|
+
:dump_schema_information, :execute, :execute_ignore_duplicate, :to => :master
|
144
139
|
|
140
|
+
def cache(&block)
|
141
|
+
connection.cache(&block)
|
142
|
+
end
|
143
|
+
|
145
144
|
def transaction(start_db_transaction = true, &block)
|
146
|
-
with_master {
|
145
|
+
with_master { connection.transaction(start_db_transaction, &block) }
|
147
146
|
end
|
148
147
|
|
149
148
|
def method_missing(method, *args, &block)
|
150
|
-
|
151
|
-
|
152
|
-
@role_changed = false
|
153
|
-
end
|
154
|
-
if DataFabric.debugging?
|
155
|
-
logger.debug("Calling #{method} on #{@cached_connection}")
|
156
|
-
end
|
157
|
-
raw_connection.send(method, *args, &block)
|
149
|
+
DataFabric.log(Logger::DEBUG) { "Calling #{method} on #{connection}" }
|
150
|
+
connection.send(method, *args, &block)
|
158
151
|
end
|
159
152
|
|
160
153
|
def connection_name
|
161
|
-
|
154
|
+
connection_name_builder.join('_')
|
162
155
|
end
|
163
156
|
|
164
157
|
def disconnect!
|
165
|
-
|
166
|
-
|
158
|
+
if connected?
|
159
|
+
connection.disconnect!
|
160
|
+
cached_connections[connection_name] = nil
|
161
|
+
end
|
167
162
|
end
|
168
163
|
|
169
164
|
def verify!(arg)
|
170
|
-
|
165
|
+
connection.verify!(arg) if connected?
|
171
166
|
end
|
172
167
|
|
173
168
|
def with_master
|
169
|
+
# Allow nesting of with_master.
|
170
|
+
old_role = @role
|
174
171
|
set_role('master')
|
175
172
|
yield
|
176
173
|
ensure
|
177
|
-
set_role(
|
174
|
+
set_role(old_role)
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def cached_connections
|
180
|
+
@cached_connections ||= {}
|
178
181
|
end
|
179
182
|
|
180
|
-
private
|
181
|
-
|
182
183
|
def connection_name_builder
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
184
|
+
@connection_name_builder ||= begin
|
185
|
+
clauses = []
|
186
|
+
clauses << @prefix if @prefix
|
187
|
+
clauses << @shard_group if @shard_group
|
188
|
+
clauses << StringProxy.new { DataFabric.active_shard(@shard_group) } if @shard_group
|
189
|
+
clauses << RAILS_ENV
|
190
|
+
clauses << StringProxy.new { @role } if @replicated
|
191
|
+
clauses
|
192
|
+
end
|
190
193
|
end
|
191
194
|
|
192
|
-
def
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
logger.debug "Switching from #{@current_connection_name || "(none)"} to #{conn_name}"
|
201
|
-
end
|
202
|
-
@current_connection_name = conn_name
|
203
|
-
conn = @model_class.connection
|
204
|
-
conn.verify! 0
|
205
|
-
conn
|
206
|
-
end
|
195
|
+
def connection
|
196
|
+
name = connection_name
|
197
|
+
if not connected?
|
198
|
+
config = ActiveRecord::Base.configurations[name]
|
199
|
+
raise ArgumentError, "Unknown database config: #{name}, have #{ActiveRecord::Base.configurations.inspect}" unless config
|
200
|
+
DataFabric.log { "Connecting to #{name}" }
|
201
|
+
@model_class.establish_connection(config)
|
202
|
+
cached_connections[name] = @model_class.connection
|
207
203
|
@model_class.active_connections[@model_class.name] = self
|
208
204
|
end
|
209
|
-
|
205
|
+
cached_connections[name].verify!(3600)
|
206
|
+
cached_connections[name]
|
210
207
|
end
|
211
|
-
|
212
|
-
def
|
213
|
-
|
208
|
+
|
209
|
+
def connected?
|
210
|
+
DataFabric.shard_active_for?(@shard_group) and cached_connections[connection_name]
|
214
211
|
end
|
215
|
-
|
212
|
+
|
216
213
|
def set_role(role)
|
217
|
-
|
218
|
-
@current_role = role
|
219
|
-
@role_changed = true
|
220
|
-
end
|
214
|
+
@role = role if @replicated
|
221
215
|
end
|
222
216
|
|
223
217
|
def master
|
224
|
-
|
225
|
-
return raw_connection
|
226
|
-
ensure
|
227
|
-
set_role('slave')
|
228
|
-
end
|
229
|
-
|
230
|
-
def logger
|
231
|
-
DataFabric.logger
|
218
|
+
with_master { return connection }
|
232
219
|
end
|
233
220
|
end
|
234
221
|
|
data/test/connection_test.rb
CHANGED
@@ -2,15 +2,15 @@ require File.join(File.dirname(__FILE__), 'test_helper')
|
|
2
2
|
require 'flexmock/test_unit'
|
3
3
|
|
4
4
|
class PrefixModel < ActiveRecord::Base
|
5
|
-
|
5
|
+
data_fabric :prefix => 'prefix'
|
6
6
|
end
|
7
7
|
|
8
8
|
class ShardModel < ActiveRecord::Base
|
9
|
-
|
9
|
+
data_fabric :shard_by => :city
|
10
10
|
end
|
11
11
|
|
12
12
|
class TheWholeEnchilada < ActiveRecord::Base
|
13
|
-
|
13
|
+
data_fabric :prefix => 'fiveruns', :replicated => true, :shard_by => :city
|
14
14
|
end
|
15
15
|
|
16
16
|
class AdapterMock < ActiveRecord::ConnectionAdapters::AbstractAdapter
|
@@ -43,7 +43,7 @@ end
|
|
43
43
|
class ConnectionTest < Test::Unit::TestCase
|
44
44
|
|
45
45
|
def test_should_install_into_arbase
|
46
|
-
assert PrefixModel.methods.include?('
|
46
|
+
assert PrefixModel.methods.include?('data_fabric')
|
47
47
|
end
|
48
48
|
|
49
49
|
def test_prefix_connection_name
|
data/test/database.yml
ADDED
@@ -0,0 +1,24 @@
|
|
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
|
+
|
10
|
+
fiveruns_city_austin_test_master:
|
11
|
+
adapter: sqlite3
|
12
|
+
database: test/vr_austin_master.db
|
13
|
+
|
14
|
+
fiveruns_city_austin_test_slave:
|
15
|
+
adapter: sqlite3
|
16
|
+
database: test/vr_austin_slave.db
|
17
|
+
|
18
|
+
fiveruns_city_dallas_test_master:
|
19
|
+
adapter: sqlite3
|
20
|
+
database: test/vr_dallas_master.db
|
21
|
+
|
22
|
+
fiveruns_city_dallas_test_slave:
|
23
|
+
adapter: sqlite3
|
24
|
+
database: test/vr_dallas_slave.db
|
File without changes
|
data/test/database_test.rb
CHANGED
@@ -3,7 +3,7 @@ require 'flexmock/test_unit'
|
|
3
3
|
require 'erb'
|
4
4
|
|
5
5
|
class TheWholeBurrito < ActiveRecord::Base
|
6
|
-
|
6
|
+
data_fabric :prefix => 'fiveruns', :replicated => true, :shard_by => :city
|
7
7
|
end
|
8
8
|
|
9
9
|
class DatabaseTest < Test::Unit::TestCase
|
@@ -18,11 +18,11 @@ class DatabaseTest < Test::Unit::TestCase
|
|
18
18
|
|
19
19
|
# Should use the slave
|
20
20
|
burrito = TheWholeBurrito.find(1)
|
21
|
-
|
21
|
+
assert_match 'vr_dallas_slave', burrito.name
|
22
22
|
|
23
23
|
# Should use the master
|
24
24
|
burrito.reload
|
25
|
-
|
25
|
+
assert_match 'vr_dallas_master', burrito.name
|
26
26
|
|
27
27
|
# ...but immediately set it back to default to the slave
|
28
28
|
assert_equal 'fiveruns_city_dallas_test_slave', TheWholeBurrito.connection.connection_name
|
@@ -30,7 +30,7 @@ class DatabaseTest < Test::Unit::TestCase
|
|
30
30
|
# Should use the master
|
31
31
|
TheWholeBurrito.transaction do
|
32
32
|
burrito = TheWholeBurrito.find(1)
|
33
|
-
|
33
|
+
assert_match 'vr_dallas_master', burrito.name
|
34
34
|
burrito.save!
|
35
35
|
end
|
36
36
|
end
|
data/test/thread_test.rb
CHANGED
@@ -11,7 +11,7 @@ class ThreadTest < Test::Unit::TestCase
|
|
11
11
|
class ThreadedEnchilada < ActiveRecord::Base
|
12
12
|
self.allow_concurrency = true
|
13
13
|
set_table_name :enchiladas
|
14
|
-
|
14
|
+
data_fabric :prefix => 'fiveruns', :replicated => true, :shard_by => :city
|
15
15
|
end
|
16
16
|
}
|
17
17
|
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.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Perham
|
@@ -9,20 +9,11 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-11-22 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
16
|
-
|
17
|
-
type: :runtime
|
18
|
-
version_requirement:
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 2.0.2
|
24
|
-
version:
|
25
|
-
description: Sharding and replication support for ActiveRecord 2.x
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Sharding and replication support for ActiveRecord 2.0 and 2.1
|
26
17
|
email: mperham@gmail.com
|
27
18
|
executables: []
|
28
19
|
|
@@ -98,7 +89,8 @@ files:
|
|
98
89
|
- Rakefile
|
99
90
|
- README.rdoc
|
100
91
|
- test/connection_test.rb
|
101
|
-
- test/database.yml
|
92
|
+
- test/database.yml
|
93
|
+
- test/database.yml.mysql
|
102
94
|
- test/database_test.rb
|
103
95
|
- test/shard_test.rb
|
104
96
|
- test/test_helper.rb
|
@@ -132,10 +124,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
132
124
|
requirements: []
|
133
125
|
|
134
126
|
rubyforge_project: fiveruns
|
135
|
-
rubygems_version: 1.
|
127
|
+
rubygems_version: 1.3.1
|
136
128
|
signing_key:
|
137
129
|
specification_version: 2
|
138
|
-
summary: Sharding and replication support for ActiveRecord 2.
|
130
|
+
summary: Sharding and replication support for ActiveRecord 2.0 and 2.1
|
139
131
|
test_files:
|
140
132
|
- test/connection_test.rb
|
141
133
|
- test/database_test.rb
|