data_fabric 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. data/CHANGELOG +4 -0
  2. data/Manifest +73 -0
  3. data/README.rdoc +1 -1
  4. data/Rakefile +34 -8
  5. data/data_fabric.gemspec +7 -7
  6. data/example/app/models/figment.rb +1 -1
  7. data/example/db/development.sqlite3 +0 -0
  8. data/example/db/s0_development.sqlite3 +0 -0
  9. data/example/db/s0_test.sqlite3 +0 -0
  10. data/example/db/s1_development.sqlite3 +0 -0
  11. data/example/db/s1_test.sqlite3 +0 -0
  12. data/example/db/test.sqlite3 +0 -0
  13. data/example/vendor/plugins/data_fabric/init.rb +1 -0
  14. data/example/vendor/plugins/data_fabric/lib/data_fabric.rb +106 -0
  15. data/example/vendor/plugins/data_fabric/lib/data_fabric/ar20.rb +135 -0
  16. data/example/vendor/plugins/data_fabric/lib/data_fabric/ar22.rb +172 -0
  17. data/example/vendor/plugins/data_fabric/lib/data_fabric/version.rb +5 -0
  18. data/example22/Rakefile +58 -0
  19. data/example22/app/controllers/accounts_controller.rb +22 -0
  20. data/example22/app/controllers/application.rb +39 -0
  21. data/example22/app/controllers/figments_controller.rb +8 -0
  22. data/example22/app/helpers/application_helper.rb +3 -0
  23. data/example22/app/models/account.rb +3 -0
  24. data/example22/app/models/figment.rb +4 -0
  25. data/example22/app/views/accounts/index.html.erb +47 -0
  26. data/example22/app/views/layouts/application.html.erb +8 -0
  27. data/example22/config/boot.rb +109 -0
  28. data/example22/config/database.yml +21 -0
  29. data/example22/config/environment.rb +76 -0
  30. data/example22/config/environments/development.rb +17 -0
  31. data/example22/config/environments/production.rb +24 -0
  32. data/example22/config/environments/test.rb +22 -0
  33. data/example22/config/initializers/inflections.rb +10 -0
  34. data/example22/config/initializers/mime_types.rb +5 -0
  35. data/example22/config/initializers/new_rails_defaults.rb +17 -0
  36. data/example22/config/locales/en.yml +5 -0
  37. data/example22/config/routes.rb +46 -0
  38. data/example22/db/migrate/20080702154628_create_accounts.rb +14 -0
  39. data/example22/db/migrate/20080702154820_create_figments.rb +14 -0
  40. data/example22/public/404.html +30 -0
  41. data/example22/public/422.html +30 -0
  42. data/example22/public/500.html +33 -0
  43. data/example22/public/dispatch.cgi +10 -0
  44. data/example22/public/dispatch.fcgi +24 -0
  45. data/example22/public/dispatch.rb +10 -0
  46. data/example22/public/favicon.ico +0 -0
  47. data/example22/public/images/rails.png +0 -0
  48. data/example22/public/index.html +274 -0
  49. data/example22/public/javascripts/application.js +2 -0
  50. data/example22/public/javascripts/controls.js +963 -0
  51. data/example22/public/javascripts/dragdrop.js +973 -0
  52. data/example22/public/javascripts/effects.js +1128 -0
  53. data/example22/public/javascripts/prototype.js +4320 -0
  54. data/example22/public/robots.txt +5 -0
  55. data/example22/script/about +4 -0
  56. data/example22/script/console +3 -0
  57. data/example22/script/dbconsole +3 -0
  58. data/example22/script/destroy +3 -0
  59. data/example22/script/generate +3 -0
  60. data/example22/script/performance/benchmarker +3 -0
  61. data/example22/script/performance/profiler +3 -0
  62. data/example22/script/performance/request +3 -0
  63. data/example22/script/plugin +3 -0
  64. data/example22/script/process/inspector +3 -0
  65. data/example22/script/process/reaper +3 -0
  66. data/example22/script/process/spawner +3 -0
  67. data/example22/script/runner +3 -0
  68. data/example22/script/server +3 -0
  69. data/example22/test/fixtures/accounts.yml +7 -0
  70. data/example22/test/functional/accounts_controller_test.rb +12 -0
  71. data/example22/test/integration/account_figments_test.rb +97 -0
  72. data/example22/test/performance/browsing_test.rb +9 -0
  73. data/example22/test/test_helper.rb +38 -0
  74. data/lib/data_fabric.rb +7 -132
  75. data/lib/data_fabric/ar20.rb +133 -0
  76. data/lib/data_fabric/ar22.rb +172 -0
  77. data/lib/data_fabric/version.rb +1 -1
  78. data/test/connection_test.rb +6 -2
  79. data/test/database_test.rb +26 -2
  80. data/test/test_helper.rb +34 -28
  81. data/test/thread_test.rb +19 -11
  82. data/test/vr_austin_master.db +0 -0
  83. data/test/vr_austin_slave.db +0 -0
  84. data/test/vr_dallas_master.db +0 -0
  85. data/test/vr_dallas_slave.db +0 -0
  86. metadata +79 -5
data/CHANGELOG CHANGED
@@ -1,5 +1,9 @@
1
1
  DataFabric changelog
2
2
 
3
+ v1.2.0 - 2008-12-25
4
+
5
+ - Now supports ActiveRecord 2.2.
6
+
3
7
  v1.1.0 - 2008-11-22
4
8
 
5
9
  - Cache connections so we don't have to reconnect constantly. (Justin Balthrop, Geni)
data/Manifest CHANGED
@@ -19,9 +19,15 @@ 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
22
23
  example/db/migrate/20080702154628_create_accounts.rb
23
24
  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
24
29
  example/db/schema.rb
30
+ example/db/test.sqlite3
25
31
  example/public/404.html
26
32
  example/public/422.html
27
33
  example/public/500.html
@@ -55,7 +61,70 @@ example/test/fixtures/accounts.yml
55
61
  example/test/functional/accounts_controller_test.rb
56
62
  example/test/integration/account_figments_test.rb
57
63
  example/test/test_helper.rb
64
+ example/vendor/plugins/data_fabric/init.rb
65
+ example/vendor/plugins/data_fabric/lib/data_fabric/ar20.rb
66
+ example/vendor/plugins/data_fabric/lib/data_fabric/ar22.rb
67
+ example/vendor/plugins/data_fabric/lib/data_fabric/version.rb
68
+ example/vendor/plugins/data_fabric/lib/data_fabric.rb
69
+ example22/app/controllers/accounts_controller.rb
70
+ example22/app/controllers/application.rb
71
+ example22/app/controllers/figments_controller.rb
72
+ example22/app/helpers/application_helper.rb
73
+ example22/app/models/account.rb
74
+ example22/app/models/figment.rb
75
+ example22/app/views/accounts/index.html.erb
76
+ example22/app/views/layouts/application.html.erb
77
+ example22/config/boot.rb
78
+ example22/config/database.yml
79
+ example22/config/environment.rb
80
+ example22/config/environments/development.rb
81
+ example22/config/environments/production.rb
82
+ example22/config/environments/test.rb
83
+ example22/config/initializers/inflections.rb
84
+ example22/config/initializers/mime_types.rb
85
+ example22/config/initializers/new_rails_defaults.rb
86
+ example22/config/locales/en.yml
87
+ example22/config/routes.rb
88
+ example22/db/migrate/20080702154628_create_accounts.rb
89
+ example22/db/migrate/20080702154820_create_figments.rb
90
+ example22/public/404.html
91
+ example22/public/422.html
92
+ example22/public/500.html
93
+ example22/public/dispatch.cgi
94
+ example22/public/dispatch.fcgi
95
+ example22/public/dispatch.rb
96
+ example22/public/favicon.ico
97
+ example22/public/images/rails.png
98
+ example22/public/index.html
99
+ example22/public/javascripts/application.js
100
+ example22/public/javascripts/controls.js
101
+ example22/public/javascripts/dragdrop.js
102
+ example22/public/javascripts/effects.js
103
+ example22/public/javascripts/prototype.js
104
+ example22/public/robots.txt
105
+ example22/Rakefile
106
+ example22/script/about
107
+ example22/script/console
108
+ example22/script/dbconsole
109
+ example22/script/destroy
110
+ example22/script/generate
111
+ example22/script/performance/benchmarker
112
+ example22/script/performance/profiler
113
+ example22/script/performance/request
114
+ example22/script/plugin
115
+ example22/script/process/inspector
116
+ example22/script/process/reaper
117
+ example22/script/process/spawner
118
+ example22/script/runner
119
+ example22/script/server
120
+ example22/test/fixtures/accounts.yml
121
+ example22/test/functional/accounts_controller_test.rb
122
+ example22/test/integration/account_figments_test.rb
123
+ example22/test/performance/browsing_test.rb
124
+ example22/test/test_helper.rb
58
125
  init.rb
126
+ lib/data_fabric/ar20.rb
127
+ lib/data_fabric/ar22.rb
59
128
  lib/data_fabric/version.rb
60
129
  lib/data_fabric.rb
61
130
  Manifest
@@ -68,4 +137,8 @@ test/database_test.rb
68
137
  test/shard_test.rb
69
138
  test/test_helper.rb
70
139
  test/thread_test.rb
140
+ test/vr_austin_master.db
141
+ test/vr_austin_slave.db
142
+ test/vr_dallas_master.db
143
+ test/vr_dallas_slave.db
71
144
  TESTING.rdoc
data/README.rdoc CHANGED
@@ -62,7 +62,7 @@ ActionController around_filter based on the user as follows:
62
62
  == Warnings
63
63
 
64
64
  * Sharded models should never be placed in the session store or you will get "Shard not set" errors when the session is persisted.
65
- * ActiveRecord's allow_concurrency = true is NOT supported in this version of data_fabric.
65
+ * DataFabric does not support running with ActiveRecord's allow_concurrency = true in AR 2.0 and 2.1. allow_concurrency is gone in AR 2.2.
66
66
 
67
67
  == Testing and Bug Reports
68
68
 
data/Rakefile CHANGED
@@ -1,8 +1,6 @@
1
1
  require 'rubygems'
2
2
  require 'echoe'
3
3
 
4
- #gem 'rails', '=2.0.2'
5
-
6
4
  require File.dirname(__FILE__) << "/lib/data_fabric/version"
7
5
 
8
6
  Echoe.new 'data_fabric' do |p|
@@ -10,16 +8,23 @@ Echoe.new 'data_fabric' do |p|
10
8
  p.author = "Mike Perham"
11
9
  p.email = 'mperham@gmail.com'
12
10
  p.project = 'fiveruns'
13
- p.summary = 'Sharding and replication support for ActiveRecord 2.0 and 2.1'
11
+ p.summary = 'Sharding and replication support for ActiveRecord 2.x'
14
12
  p.url = "http://github.com/fiveruns/data_fabric"
15
- # p.dependencies = ['activerecord >=2.0.2']
16
13
  p.development_dependencies = []
17
14
  p.rubygems_version = nil
18
15
  p.include_rakefile = true
16
+ p.test_pattern = 'test/*_test.rb'
19
17
  end
20
18
 
21
19
  task :test => [:pretest]
22
20
 
21
+ desc "Test all versions of ActiveRecord installed locally"
22
+ task :test_all do
23
+ Gem.source_index.search(Gem::Dependency.new('activerecord', '>=2.0')).each do |spec|
24
+ puts `rake test AR_VERSION=#{spec.version}`
25
+ end
26
+ end
27
+
23
28
  task :pretest do
24
29
  setup(false)
25
30
  end
@@ -28,10 +33,6 @@ task :create_db do
28
33
  setup(true)
29
34
  end
30
35
 
31
- task :changelog do
32
- `git log | grep -v git-svn-id > History.txt`
33
- end
34
-
35
36
  def load_database_yml
36
37
  filename = "test/database.yml"
37
38
  if !File.exist?(filename)
@@ -87,3 +88,28 @@ def create_mysql(create, db_name)
87
88
  execute "create table the_whole_burritos (id integer not null auto_increment, name varchar(30) not null, primary key(id))"
88
89
  execute "insert into the_whole_burritos (id, name) values (1, '#{db_name}')"
89
90
  end
91
+
92
+ # Test coverage
93
+ gem 'spicycode-rcov' rescue nil
94
+ begin
95
+ require 'rcov/rcovtask'
96
+
97
+ desc "Generate coverage numbers for all locally installed versions of ActiveRecord"
98
+ task :cover_all do
99
+ Gem.source_index.search(Gem::Dependency.new('activerecord', '>=2.0')).each do |spec|
100
+ puts `rake cover AR_VERSION=#{spec.version}`
101
+ end
102
+ end
103
+
104
+ task :cover => [:pretest, :rcov_impl]
105
+
106
+ Rcov::RcovTask.new('rcov_impl') do |t|
107
+ t.libs << "test"
108
+ t.test_files = FileList["test/*_test.rb"]
109
+ t.output_dir = "coverage/#{ENV['AR_VERSION']}"
110
+ t.verbose = true
111
+ t.rcov_opts = ['--text-report', '--exclude', "test,Library,#{ENV['GEM_HOME']}", '--sort', 'coverage']
112
+ end
113
+ rescue LoadError => e
114
+ puts 'Test coverage support requires \'gem install spicycode-rcov\''
115
+ end
data/data_fabric.gemspec CHANGED
@@ -2,23 +2,23 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{data_fabric}
5
- s.version = "1.1.0"
5
+ s.version = "1.2.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
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}
9
+ s.date = %q{2008-12-01}
10
+ s.description = %q{Sharding and replication support for ActiveRecord 2.x}
11
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"]
12
+ s.extra_rdoc_files = ["CHANGELOG", "lib/data_fabric/ar20.rb", "lib/data_fabric/ar22.rb", "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/development.sqlite3", "example/db/migrate/20080702154628_create_accounts.rb", "example/db/migrate/20080702154820_create_figments.rb", "example/db/s0_development.sqlite3", "example/db/s0_test.sqlite3", "example/db/s1_development.sqlite3", "example/db/s1_test.sqlite3", "example/db/schema.rb", "example/db/test.sqlite3", "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", "example/vendor/plugins/data_fabric/init.rb", "example/vendor/plugins/data_fabric/lib/data_fabric/ar20.rb", "example/vendor/plugins/data_fabric/lib/data_fabric/ar22.rb", "example/vendor/plugins/data_fabric/lib/data_fabric/version.rb", "example/vendor/plugins/data_fabric/lib/data_fabric.rb", "example22/app/controllers/accounts_controller.rb", "example22/app/controllers/application.rb", "example22/app/controllers/figments_controller.rb", "example22/app/helpers/application_helper.rb", "example22/app/models/account.rb", "example22/app/models/figment.rb", "example22/app/views/accounts/index.html.erb", "example22/app/views/layouts/application.html.erb", "example22/config/boot.rb", "example22/config/database.yml", "example22/config/environment.rb", "example22/config/environments/development.rb", "example22/config/environments/production.rb", "example22/config/environments/test.rb", "example22/config/initializers/inflections.rb", "example22/config/initializers/mime_types.rb", "example22/config/initializers/new_rails_defaults.rb", "example22/config/locales/en.yml", "example22/config/routes.rb", "example22/db/migrate/20080702154628_create_accounts.rb", "example22/db/migrate/20080702154820_create_figments.rb", "example22/public/404.html", "example22/public/422.html", "example22/public/500.html", "example22/public/dispatch.cgi", "example22/public/dispatch.fcgi", "example22/public/dispatch.rb", "example22/public/favicon.ico", "example22/public/images/rails.png", "example22/public/index.html", "example22/public/javascripts/application.js", "example22/public/javascripts/controls.js", "example22/public/javascripts/dragdrop.js", "example22/public/javascripts/effects.js", "example22/public/javascripts/prototype.js", "example22/public/robots.txt", "example22/Rakefile", "example22/script/about", "example22/script/console", "example22/script/dbconsole", "example22/script/destroy", "example22/script/generate", "example22/script/performance/benchmarker", "example22/script/performance/profiler", "example22/script/performance/request", "example22/script/plugin", "example22/script/process/inspector", "example22/script/process/reaper", "example22/script/process/spawner", "example22/script/runner", "example22/script/server", "example22/test/fixtures/accounts.yml", "example22/test/functional/accounts_controller_test.rb", "example22/test/integration/account_figments_test.rb", "example22/test/performance/browsing_test.rb", "example22/test/test_helper.rb", "init.rb", "lib/data_fabric/ar20.rb", "lib/data_fabric/ar22.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", "test/vr_austin_master.db", "test/vr_austin_slave.db", "test/vr_dallas_master.db", "test/vr_dallas_slave.db", "TESTING.rdoc", "data_fabric.gemspec"]
14
14
  s.has_rdoc = true
15
15
  s.homepage = %q{http://github.com/fiveruns/data_fabric}
16
16
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Data_fabric", "--main", "README.rdoc"]
17
17
  s.require_paths = ["lib"]
18
18
  s.rubyforge_project = %q{fiveruns}
19
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"]
20
+ s.summary = %q{Sharding and replication support for ActiveRecord 2.x}
21
+ s.test_files = ["test/connection_test.rb", "test/database_test.rb", "test/shard_test.rb", "test/thread_test.rb"]
22
22
 
23
23
  if s.respond_to? :specification_version then
24
24
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
@@ -1,4 +1,4 @@
1
1
  class Figment < ActiveRecord::Base
2
- connection_topology :shard_by => 'shard', :replicated => false
2
+ data_fabric :shard_by => 'shard', :replicated => false
3
3
  belongs_to :account
4
4
  end
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1 @@
1
+ DataFabric.init
@@ -0,0 +1,106 @@
1
+ require 'active_record'
2
+ require 'active_record/version'
3
+ require 'data_fabric/version'
4
+
5
+ # DataFabric adds a new level of flexibility to ActiveRecord connection handling.
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.
7
+ #
8
+ # class MyHugeVolumeOfDataModel < ActiveRecord::Base
9
+ # data_fabric :replicated => true, :shard_by => :city
10
+ # end
11
+ #
12
+ # There are four supported modes of operation, depending on the options given to the data_fabric method. The plugin will look for connections in your config/database.yml with the following convention:
13
+ #
14
+ # No connection topology:
15
+ # #{environment} - this is the default, as with ActiveRecord, e.g. "production"
16
+ #
17
+ # data_fabric :replicated => true
18
+ # #{environment}_#{role} - no sharding, just replication, where role is "master" or "slave", e.g. "production_master"
19
+ #
20
+ # data_fabric :shard_by => :city
21
+ # #{group}_#{shard}_#{environment} - sharding, no replication, e.g. "city_austin_production"
22
+ #
23
+ # data_fabric :replicated => true, :shard_by => :city
24
+ # #{group}_#{shard}_#{environment}_#{role} - sharding with replication, e.g. "city_austin_production_master"
25
+ #
26
+ #
27
+ # When marked as replicated, all write and transactional operations for the model go to the master, whereas read operations go to the slave.
28
+ #
29
+ # 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:
30
+ #
31
+ # class ApplicationController < ActionController::Base
32
+ # around_filter :select_shard
33
+ #
34
+ # private
35
+ # def select_shard(&action_block)
36
+ # DataFabric.activate_shard(:city => @current_user.city, &action_block)
37
+ # end
38
+ # end
39
+ module DataFabric
40
+
41
+ # Set this logger to log DataFabric operations.
42
+ # The logger should quack like a standard Ruby Logger.
43
+ mattr_accessor :logger
44
+
45
+ def self.init
46
+ logger = ActiveRecord::Base.logger unless logger
47
+ log { "Loading data_fabric #{DataFabric::Version::STRING} with ActiveRecord #{ActiveRecord::VERSION::STRING}" }
48
+
49
+ if ActiveRecord::VERSION::STRING < '2.2.0'
50
+ require 'data_fabric/ar20'
51
+ else
52
+ require 'data_fabric/ar22'
53
+ end
54
+ ActiveRecord::Base.send(:include, DataFabric::Extensions)
55
+ end
56
+
57
+ def self.activate_shard(shards, &block)
58
+ ensure_setup
59
+
60
+ # Save the old shard settings to handle nested activation
61
+ old = Thread.current[:shards].dup
62
+
63
+ shards.each_pair do |key, value|
64
+ Thread.current[:shards][key.to_s] = value.to_s
65
+ end
66
+ if block_given?
67
+ begin
68
+ yield
69
+ ensure
70
+ Thread.current[:shards] = old
71
+ end
72
+ end
73
+ end
74
+
75
+ # For cases where you can't pass a block to activate_shards, you can
76
+ # clean up the thread local settings by calling this method at the
77
+ # end of processing
78
+ def self.deactivate_shard(shards)
79
+ ensure_setup
80
+ shards.each do |key, value|
81
+ Thread.current[:shards].delete(key.to_s)
82
+ end
83
+ end
84
+
85
+ def self.active_shard(group)
86
+ raise ArgumentError, 'No shard has been activated' unless Thread.current[:shards]
87
+
88
+ returning(Thread.current[:shards][group.to_s]) do |shard|
89
+ raise ArgumentError, "No active shard for #{group}" unless shard
90
+ end
91
+ end
92
+
93
+ def self.shard_active_for?(group)
94
+ return true unless group
95
+ Thread.current[:shards] and Thread.current[:shards][group.to_s]
96
+ end
97
+
98
+ def self.ensure_setup
99
+ Thread.current[:shards] = {} unless Thread.current[:shards]
100
+ end
101
+
102
+ def self.log(level=Logger::INFO, &block)
103
+ logger && logger.add(level, &block)
104
+ end
105
+
106
+ end
@@ -0,0 +1,135 @@
1
+ module DataFabric
2
+ module Extensions
3
+ def self.included(model)
4
+ # Wire up ActiveRecord::Base
5
+ model.extend ClassMethods
6
+ end
7
+
8
+ # Class methods injected into ActiveRecord::Base
9
+ module ClassMethods
10
+ def data_fabric(options)
11
+ proxy = DataFabric::ConnectionProxy.new(self, options)
12
+ ActiveRecord::Base.active_connections[name] = proxy
13
+
14
+ raise ArgumentError, "data_fabric does not support ActiveRecord's allow_concurrency = true" if allow_concurrency
15
+ DataFabric.log { "Creating data_fabric proxy for class #{name}" }
16
+ end
17
+ end
18
+ end
19
+
20
+ class ConnectionProxy
21
+ def initialize(model_class, options)
22
+ @model_class = model_class
23
+ @replicated = options[:replicated]
24
+ @shard_group = options[:shard_by]
25
+ @prefix = options[:prefix]
26
+ @role = 'slave' if @replicated
27
+
28
+ @model_class.send :include, ActiveRecordConnectionMethods if @replicated
29
+ end
30
+
31
+ delegate :insert, :update, :delete, :create_table, :rename_table, :drop_table, :add_column, :remove_column,
32
+ :change_column, :change_column_default, :rename_column, :add_index, :remove_index, :initialize_schema_information,
33
+ :dump_schema_information, :execute, :execute_ignore_duplicate, :to => :master
34
+
35
+ def cache(&block)
36
+ connection.cache(&block)
37
+ end
38
+
39
+ def transaction(start_db_transaction = true, &block)
40
+ with_master { connection.transaction(start_db_transaction, &block) }
41
+ end
42
+
43
+ def method_missing(method, *args, &block)
44
+ DataFabric.log(Logger::DEBUG) { "Calling #{method} on #{connection}" }
45
+ connection.send(method, *args, &block)
46
+ end
47
+
48
+ def connection_name
49
+ connection_name_builder.join('_')
50
+ end
51
+
52
+ def disconnect!
53
+ if connected?
54
+ connection.disconnect!
55
+ cached_connections[connection_name] = nil
56
+ end
57
+ end
58
+
59
+ def verify!(arg)
60
+ connection.verify!(arg) if connected?
61
+ end
62
+
63
+ def with_master
64
+ # Allow nesting of with_master.
65
+ old_role = @role
66
+ set_role('master')
67
+ yield
68
+ ensure
69
+ set_role(old_role)
70
+ end
71
+
72
+ private
73
+
74
+ def cached_connections
75
+ @cached_connections ||= {}
76
+ end
77
+
78
+ def connection_name_builder
79
+ @connection_name_builder ||= begin
80
+ clauses = []
81
+ clauses << @prefix if @prefix
82
+ clauses << @shard_group if @shard_group
83
+ clauses << StringProxy.new { DataFabric.active_shard(@shard_group) } if @shard_group
84
+ clauses << RAILS_ENV
85
+ clauses << StringProxy.new { @role } if @replicated
86
+ clauses
87
+ end
88
+ end
89
+
90
+ def connection
91
+ name = connection_name
92
+ if not connected?
93
+ config = ActiveRecord::Base.configurations[name]
94
+ raise ArgumentError, "Unknown database config: #{name}, have #{ActiveRecord::Base.configurations.inspect}" unless config
95
+ DataFabric.log { "Connecting to #{name}" }
96
+ @model_class.establish_connection(config)
97
+ cached_connections[name] = @model_class.connection
98
+ @model_class.active_connections[@model_class.name] = self
99
+ end
100
+ cached_connections[name].verify!(3600)
101
+ cached_connections[name]
102
+ end
103
+
104
+ def connected?
105
+ DataFabric.shard_active_for?(@shard_group) and cached_connections[connection_name]
106
+ end
107
+
108
+ def set_role(role)
109
+ @role = role if @replicated
110
+ end
111
+
112
+ def master
113
+ with_master { return connection }
114
+ end
115
+ end
116
+
117
+ module ActiveRecordConnectionMethods
118
+ def self.included(base)
119
+ base.alias_method_chain :reload, :master
120
+ end
121
+
122
+ def reload_with_master(*args, &block)
123
+ connection.with_master { reload_without_master }
124
+ end
125
+ end
126
+
127
+ class StringProxy
128
+ def initialize(&block)
129
+ @proc = block
130
+ end
131
+ def to_s
132
+ @proc.call
133
+ end
134
+ end
135
+ end