data_fabric 1.2.7 → 1.3.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 (118) hide show
  1. data/CHANGELOG +7 -0
  2. data/README.rdoc +9 -8
  3. data/Rakefile +9 -41
  4. data/TESTING.rdoc +3 -3
  5. data/example23/Rakefile +28 -27
  6. data/example23/config/environment.rb +4 -2
  7. data/example30/Gemfile +5 -0
  8. data/example30/Gemfile.lock +79 -0
  9. data/example30/README +256 -0
  10. data/example30/Rakefile +52 -0
  11. data/{example → example30}/app/controllers/accounts_controller.rb +0 -0
  12. data/{example/app/controllers/application.rb → example30/app/controllers/application_controller.rb} +0 -0
  13. data/{example → example30}/app/controllers/figments_controller.rb +0 -0
  14. data/example30/app/helpers/application_helper.rb +2 -0
  15. data/{example → example30}/app/models/account.rb +0 -0
  16. data/{example → example30}/app/models/figment.rb +0 -0
  17. data/{example → example30}/app/views/accounts/index.html.erb +2 -2
  18. data/{example → example30}/app/views/layouts/application.html.erb +0 -0
  19. data/example30/config.ru +4 -0
  20. data/example30/config/application.rb +42 -0
  21. data/example30/config/boot.rb +13 -0
  22. data/{example → example30}/config/database.yml +0 -0
  23. data/example30/config/environment.rb +5 -0
  24. data/example30/config/environments/development.rb +26 -0
  25. data/example30/config/environments/production.rb +49 -0
  26. data/example30/config/environments/test.rb +35 -0
  27. data/example30/config/initializers/backtrace_silencers.rb +7 -0
  28. data/{example → example30}/config/initializers/inflections.rb +2 -2
  29. data/{example → example30}/config/initializers/mime_types.rb +0 -0
  30. data/example30/config/initializers/secret_token.rb +7 -0
  31. data/example30/config/initializers/session_store.rb +8 -0
  32. data/example30/config/locales/en.yml +5 -0
  33. data/example30/config/routes.rb +65 -0
  34. data/example30/db/development.sqlite3 +0 -0
  35. data/{example → example30}/db/migrate/20080702154628_create_accounts.rb +0 -0
  36. data/{example → example30}/db/migrate/20080702154820_create_figments.rb +0 -0
  37. data/example30/db/s0_development.sqlite3 +0 -0
  38. data/example30/db/s0_test.sqlite3 +0 -0
  39. data/example30/db/s1_development.sqlite3 +0 -0
  40. data/example30/db/s1_test.sqlite3 +0 -0
  41. data/{example23 → example30}/db/schema.rb +7 -6
  42. data/example30/db/seeds.rb +7 -0
  43. data/example30/db/test.sqlite3 +0 -0
  44. data/example30/log/development.log +132 -0
  45. data/example30/log/test.log +444 -0
  46. data/example30/script/rails +6 -0
  47. data/{example → example30}/test/fixtures/accounts.yml +0 -0
  48. data/{example → example30}/test/functional/accounts_controller_test.rb +0 -0
  49. data/{example → example30}/test/integration/account_figments_test.rb +0 -0
  50. data/example30/test/performance/browsing_test.rb +9 -0
  51. data/example30/test/test_helper.rb +13 -0
  52. data/lib/data_fabric.rb +14 -21
  53. data/lib/data_fabric/{ar22.rb → connection_proxy.rb} +43 -63
  54. data/lib/data_fabric/extensions.rb +29 -0
  55. data/lib/data_fabric/version.rb +1 -1
  56. data/test/connection_test.rb +17 -12
  57. data/test/database_test.rb +3 -11
  58. data/test/test_helper.rb +3 -8
  59. data/test/thread_test.rb +0 -14
  60. data/test/vr_austin_master.db +0 -0
  61. data/test/vr_austin_slave.db +0 -0
  62. data/test/vr_dallas_master.db +0 -0
  63. data/test/vr_dallas_slave.db +0 -0
  64. metadata +72 -99
  65. data/example/Rakefile +0 -58
  66. data/example/app/helpers/accounts_helper.rb +0 -2
  67. data/example/app/helpers/application_helper.rb +0 -3
  68. data/example/app/helpers/figments_helper.rb +0 -2
  69. data/example/config/boot.rb +0 -109
  70. data/example/config/environment.rb +0 -67
  71. data/example/config/environments/development.rb +0 -17
  72. data/example/config/environments/production.rb +0 -22
  73. data/example/config/environments/test.rb +0 -22
  74. data/example/config/initializers/new_rails_defaults.rb +0 -15
  75. data/example/config/routes.rb +0 -45
  76. data/example/db/schema.rb +0 -28
  77. data/example/public/404.html +0 -30
  78. data/example/public/422.html +0 -30
  79. data/example/public/500.html +0 -30
  80. data/example/public/dispatch.cgi +0 -10
  81. data/example/public/dispatch.fcgi +0 -24
  82. data/example/public/dispatch.rb +0 -10
  83. data/example/public/favicon.ico +0 -0
  84. data/example/public/images/rails.png +0 -0
  85. data/example/public/robots.txt +0 -5
  86. data/example/script/about +0 -3
  87. data/example/script/console +0 -3
  88. data/example/script/dbconsole +0 -3
  89. data/example/script/destroy +0 -3
  90. data/example/script/generate +0 -3
  91. data/example/script/performance/benchmarker +0 -3
  92. data/example/script/performance/profiler +0 -3
  93. data/example/script/performance/request +0 -3
  94. data/example/script/plugin +0 -3
  95. data/example/script/process/inspector +0 -3
  96. data/example/script/process/reaper +0 -3
  97. data/example/script/process/spawner +0 -3
  98. data/example/script/runner +0 -3
  99. data/example/script/server +0 -3
  100. data/example/test/test_helper.rb +0 -41
  101. data/example23/db/development.sqlite3 +0 -0
  102. data/example23/db/s0_development.sqlite3 +0 -0
  103. data/example23/db/s0_test.sqlite3 +0 -0
  104. data/example23/db/s1_development.sqlite3 +0 -0
  105. data/example23/db/s1_test.sqlite3 +0 -0
  106. data/example23/db/test.sqlite3 +0 -0
  107. data/example23/log/development.log +0 -295
  108. data/example23/log/test.log +0 -551
  109. data/example23/vendor/plugins/data_fabric/init.rb +0 -3
  110. data/example23/vendor/plugins/data_fabric/lib/data_fabric.rb +0 -106
  111. data/example23/vendor/plugins/data_fabric/lib/data_fabric/ar20.rb +0 -133
  112. data/example23/vendor/plugins/data_fabric/lib/data_fabric/ar22.rb +0 -181
  113. data/example23/vendor/plugins/data_fabric/lib/data_fabric/dash.rb +0 -20
  114. data/example23/vendor/plugins/data_fabric/lib/data_fabric/version.rb +0 -7
  115. data/init.rb +0 -3
  116. data/lib/data_fabric/ar20.rb +0 -133
  117. data/lib/data_fabric/dash.rb +0 -20
  118. data/rails/init.rb +0 -1
@@ -1,3 +0,0 @@
1
- # This file is just here for Rails 2.0 compatability. Rails 2.1+
2
- # should use rails/init.rb.
3
- DataFabric.init
@@ -1,106 +0,0 @@
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
@@ -1,133 +0,0 @@
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
- delegate :insert_many, :to => :master # ar-extensions bulk insert support
36
-
37
- def transaction(start_db_transaction = true, &block)
38
- with_master { connection.transaction(start_db_transaction, &block) }
39
- end
40
-
41
- def method_missing(method, *args, &block)
42
- DataFabric.log(Logger::DEBUG) { "Calling #{method} on #{connection}" }
43
- connection.send(method, *args, &block)
44
- end
45
-
46
- def connection_name
47
- connection_name_builder.join('_')
48
- end
49
-
50
- def disconnect!
51
- if connected?
52
- connection.disconnect!
53
- cached_connections[connection_name] = nil
54
- end
55
- end
56
-
57
- def verify!(arg)
58
- connection.verify!(arg) if connected?
59
- end
60
-
61
- def with_master
62
- # Allow nesting of with_master.
63
- old_role = @role
64
- set_role('master')
65
- yield
66
- ensure
67
- set_role(old_role)
68
- end
69
-
70
- private
71
-
72
- def cached_connections
73
- @cached_connections ||= {}
74
- end
75
-
76
- def connection_name_builder
77
- @connection_name_builder ||= begin
78
- clauses = []
79
- clauses << @prefix if @prefix
80
- clauses << @shard_group if @shard_group
81
- clauses << StringProxy.new { DataFabric.active_shard(@shard_group) } if @shard_group
82
- clauses << RAILS_ENV
83
- clauses << StringProxy.new { @role } if @replicated
84
- clauses
85
- end
86
- end
87
-
88
- def connection
89
- name = connection_name
90
- if not connected?
91
- config = ActiveRecord::Base.configurations[name]
92
- raise ArgumentError, "Unknown database config: #{name}, have #{ActiveRecord::Base.configurations.inspect}" unless config
93
- DataFabric.log { "Connecting to #{name}" }
94
- @model_class.establish_connection(config)
95
- cached_connections[name] = @model_class.connection
96
- @model_class.active_connections[@model_class.name] = self
97
- end
98
- cached_connections[name].verify!(3600)
99
- cached_connections[name]
100
- end
101
-
102
- def connected?
103
- DataFabric.shard_active_for?(@shard_group) and cached_connections[connection_name]
104
- end
105
-
106
- def set_role(role)
107
- @role = role if @replicated
108
- end
109
-
110
- def master
111
- with_master { return connection }
112
- end
113
- end
114
-
115
- module ActiveRecordConnectionMethods
116
- def self.included(base)
117
- base.alias_method_chain :reload, :master
118
- end
119
-
120
- def reload_with_master(*args, &block)
121
- connection.with_master { reload_without_master }
122
- end
123
- end
124
-
125
- class StringProxy
126
- def initialize(&block)
127
- @proc = block
128
- end
129
- def to_s
130
- @proc.call
131
- end
132
- end
133
- end
@@ -1,181 +0,0 @@
1
-
2
- class ActiveRecord::ConnectionAdapters::ConnectionHandler
3
- def clear_active_connections_with_data_fabric!
4
- clear_active_connections_without_data_fabric!
5
- DataFabric::ConnectionProxy.shard_pools.each_value { |pool| pool.release_connection }
6
- end
7
- alias_method_chain :clear_active_connections!, :data_fabric
8
- end
9
-
10
- module DataFabric
11
- module Extensions
12
- def self.included(model)
13
- # Wire up ActiveRecord::Base
14
- model.extend ClassMethods
15
- ConnectionProxy.shard_pools = {}
16
- end
17
-
18
- # Class methods injected into ActiveRecord::Base
19
- module ClassMethods
20
- def data_fabric(options)
21
- DataFabric.log { "Creating data_fabric proxy for class #{name}" }
22
- @proxy = DataFabric::ConnectionProxy.new(self, options)
23
-
24
- class << self
25
- def connection
26
- @proxy || superclass.connection
27
- end
28
-
29
- def connected?
30
- @proxy.connected?
31
- end
32
-
33
- def remove_connection(klass=self)
34
- DataFabric.log(Logger::WARN) { "remove_connection not implemented by data_fabric" }
35
- end
36
-
37
- def connection_pool
38
- raise "dynamic connection switching means you cannot get direct access to a pool"
39
- end
40
- end
41
- end
42
- end
43
- end
44
-
45
- class ConnectionProxy
46
- cattr_accessor :shard_pools
47
-
48
- def initialize(model_class, options)
49
- @model_class = model_class
50
- @replicated = options[:replicated]
51
- @shard_group = options[:shard_by]
52
- @prefix = options[:prefix]
53
- set_role('slave') if @replicated
54
-
55
- @model_class.send :include, ActiveRecordConnectionMethods if @replicated
56
- end
57
-
58
- delegate :insert, :update, :delete, :create_table, :rename_table, :drop_table, :add_column, :remove_column,
59
- :change_column, :change_column_default, :rename_column, :add_index, :remove_index, :initialize_schema_information,
60
- :dump_schema_information, :execute, :execute_ignore_duplicate, :to => :master
61
-
62
- delegate :insert_many, :to => :master # ar-extensions bulk insert support
63
-
64
- def transaction(start_db_transaction = true, &block)
65
- # Transaction is not re-entrant in SQLite 3 so we
66
- # need to track if we've already started an XA to avoid
67
- # calling it twice.
68
- return yield if in_transaction?
69
-
70
- with_master do
71
- connection.transaction(start_db_transaction, &block)
72
- end
73
- end
74
-
75
- def method_missing(method, *args, &block)
76
- DataFabric.log(Logger::DEBUG) { "Calling #{method} on #{connection}" }
77
- connection.send(method, *args, &block)
78
- end
79
-
80
- def connection_name
81
- connection_name_builder.join('_')
82
- end
83
-
84
- def with_master
85
- # Allow nesting of with_master.
86
- old_role = current_role
87
- set_role('master')
88
- yield
89
- ensure
90
- set_role(old_role)
91
- end
92
-
93
- def connected?
94
- current_pool.connected?
95
- end
96
-
97
- private
98
-
99
- def in_transaction?
100
- current_role == 'master'
101
- end
102
-
103
- def current_pool
104
- name = connection_name
105
- self.class.shard_pools[name] ||= begin
106
- config = ActiveRecord::Base.configurations[name]
107
- raise ArgumentError, "Unknown database config: #{name}, have #{ActiveRecord::Base.configurations.inspect}" unless config
108
- ActiveRecord::ConnectionAdapters::ConnectionPool.new(spec_for(config))
109
- end
110
- end
111
-
112
- def spec_for(config)
113
- # XXX This looks pretty fragile. Will break if AR changes how it initializes connections and adapters.
114
- config = config.symbolize_keys
115
- adapter_method = "#{config[:adapter]}_connection"
116
- initialize_adapter(config[:adapter])
117
- ActiveRecord::Base::ConnectionSpecification.new(config, adapter_method)
118
- end
119
-
120
- def initialize_adapter(adapter)
121
- begin
122
- require 'rubygems'
123
- gem "activerecord-#{adapter}-adapter"
124
- require "active_record/connection_adapters/#{adapter}_adapter"
125
- rescue LoadError
126
- begin
127
- require "active_record/connection_adapters/#{adapter}_adapter"
128
- rescue LoadError
129
- raise "Please install the #{adapter} adapter: `gem install activerecord-#{adapter}-adapter` (#{$!})"
130
- end
131
- end
132
- end
133
-
134
- def connection_name_builder
135
- @connection_name_builder ||= begin
136
- clauses = []
137
- clauses << @prefix if @prefix
138
- clauses << @shard_group if @shard_group
139
- clauses << StringProxy.new { DataFabric.active_shard(@shard_group) } if @shard_group
140
- clauses << RAILS_ENV
141
- clauses << StringProxy.new { current_role } if @replicated
142
- clauses
143
- end
144
- end
145
-
146
- def connection
147
- current_pool.connection
148
- end
149
-
150
- def set_role(role)
151
- Thread.current[:data_fabric_role] = role
152
- end
153
-
154
- def current_role
155
- Thread.current[:data_fabric_role] || 'slave'
156
- end
157
-
158
- def master
159
- with_master { return connection }
160
- end
161
- end
162
-
163
- module ActiveRecordConnectionMethods
164
- def self.included(base)
165
- base.alias_method_chain :reload, :master
166
- end
167
-
168
- def reload_with_master(*args, &block)
169
- connection.with_master { reload_without_master }
170
- end
171
- end
172
-
173
- class StringProxy
174
- def initialize(&block)
175
- @proc = block
176
- end
177
- def to_s
178
- @proc.call
179
- end
180
- end
181
- end
@@ -1,20 +0,0 @@
1
- # A data_fabric recipe for use with the FiveRuns Dash metrics service at
2
- # http://dash.fiveruns.com.
3
- #
4
- # Hook into your Rails application by adding the recipe in your
5
- # config/initializers/dash.rb, like so:
6
- #
7
- # require 'data_fabric/dash'
8
- # Fiveruns::Dash::Rails.start :production => 'your-token' do |config|
9
- # config.add_recipe :data_fabric, :url => 'http://mikeperham.com'
10
- # end
11
- #
12
- raise ArgumentError, "The Dash recipe for DataFabric is only supported on ActiveRecord 2.2 and greater" if ActiveRecord::VERSION::STRING < '2.2.0'
13
-
14
- Fiveruns::Dash.register_recipe :data_fabric, :url => 'http://mikeperham.com' do |recipe|
15
- recipe.absolute :open_connections, 'Open Connections' do
16
- DataFabric::ConnectionProxy.shard_pools.values.map do |pool|
17
- (pool.instance_variable_get(:@checked_out) || []).size
18
- end.sum
19
- end
20
- end