ar-octopus-ruby-3 0.11.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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +46 -0
- data/.rubocop_todo.yml +56 -0
- data/.travis.yml +18 -0
- data/Appraisals +16 -0
- data/Gemfile +4 -0
- data/README.mkdn +257 -0
- data/Rakefile +175 -0
- data/TODO.txt +7 -0
- data/ar-octopus.gemspec +44 -0
- data/gemfiles/rails42.gemfile +7 -0
- data/gemfiles/rails5.gemfile +7 -0
- data/gemfiles/rails51.gemfile +7 -0
- data/gemfiles/rails52.gemfile +7 -0
- data/lib/ar-octopus.rb +1 -0
- data/lib/octopus/abstract_adapter.rb +33 -0
- data/lib/octopus/association.rb +14 -0
- data/lib/octopus/association_shard_tracking.rb +74 -0
- data/lib/octopus/collection_association.rb +17 -0
- data/lib/octopus/collection_proxy.rb +16 -0
- data/lib/octopus/exception.rb +4 -0
- data/lib/octopus/finder_methods.rb +8 -0
- data/lib/octopus/load_balancing/round_robin.rb +20 -0
- data/lib/octopus/load_balancing.rb +4 -0
- data/lib/octopus/log_subscriber.rb +26 -0
- data/lib/octopus/migration.rb +236 -0
- data/lib/octopus/model.rb +216 -0
- data/lib/octopus/persistence.rb +45 -0
- data/lib/octopus/proxy.rb +399 -0
- data/lib/octopus/proxy_config.rb +251 -0
- data/lib/octopus/query_cache_for_shards.rb +24 -0
- data/lib/octopus/railtie.rb +11 -0
- data/lib/octopus/relation_proxy.rb +74 -0
- data/lib/octopus/result_patch.rb +19 -0
- data/lib/octopus/scope_proxy.rb +68 -0
- data/lib/octopus/shard_tracking/attribute.rb +22 -0
- data/lib/octopus/shard_tracking/dynamic.rb +11 -0
- data/lib/octopus/shard_tracking.rb +46 -0
- data/lib/octopus/singular_association.rb +9 -0
- data/lib/octopus/slave_group.rb +13 -0
- data/lib/octopus/version.rb +3 -0
- data/lib/octopus.rb +209 -0
- data/lib/tasks/octopus.rake +16 -0
- data/sample_app/.gitignore +4 -0
- data/sample_app/.rspec +1 -0
- data/sample_app/Gemfile +20 -0
- data/sample_app/Gemfile.lock +155 -0
- data/sample_app/README +3 -0
- data/sample_app/README.rdoc +261 -0
- data/sample_app/Rakefile +7 -0
- data/sample_app/app/assets/images/rails.png +0 -0
- data/sample_app/app/assets/javascripts/application.js +15 -0
- data/sample_app/app/assets/stylesheets/application.css +13 -0
- data/sample_app/app/controllers/application_controller.rb +4 -0
- data/sample_app/app/helpers/application_helper.rb +2 -0
- data/sample_app/app/mailers/.gitkeep +0 -0
- data/sample_app/app/models/.gitkeep +0 -0
- data/sample_app/app/models/item.rb +3 -0
- data/sample_app/app/models/user.rb +3 -0
- data/sample_app/app/views/layouts/application.html.erb +14 -0
- data/sample_app/autotest/discover.rb +2 -0
- data/sample_app/config/application.rb +62 -0
- data/sample_app/config/boot.rb +6 -0
- data/sample_app/config/cucumber.yml +8 -0
- data/sample_app/config/database.yml +28 -0
- data/sample_app/config/environment.rb +5 -0
- data/sample_app/config/environments/development.rb +37 -0
- data/sample_app/config/environments/production.rb +67 -0
- data/sample_app/config/environments/test.rb +37 -0
- data/sample_app/config/initializers/backtrace_silencers.rb +7 -0
- data/sample_app/config/initializers/inflections.rb +15 -0
- data/sample_app/config/initializers/mime_types.rb +5 -0
- data/sample_app/config/initializers/secret_token.rb +7 -0
- data/sample_app/config/initializers/session_store.rb +8 -0
- data/sample_app/config/initializers/wrap_parameters.rb +14 -0
- data/sample_app/config/locales/en.yml +5 -0
- data/sample_app/config/routes.rb +58 -0
- data/sample_app/config/shards.yml +28 -0
- data/sample_app/config.ru +4 -0
- data/sample_app/db/migrate/20100720172715_create_users.rb +15 -0
- data/sample_app/db/migrate/20100720172730_create_items.rb +16 -0
- data/sample_app/db/migrate/20100720210335_create_sample_users.rb +11 -0
- data/sample_app/db/schema.rb +29 -0
- data/sample_app/db/seeds.rb +16 -0
- data/sample_app/doc/README_FOR_APP +2 -0
- data/sample_app/features/migrate.feature +45 -0
- data/sample_app/features/seed.feature +15 -0
- data/sample_app/features/step_definitions/seeds_steps.rb +13 -0
- data/sample_app/features/step_definitions/web_steps.rb +218 -0
- data/sample_app/features/support/database.rb +13 -0
- data/sample_app/features/support/env.rb +57 -0
- data/sample_app/features/support/paths.rb +33 -0
- data/sample_app/lib/assets/.gitkeep +0 -0
- data/sample_app/lib/tasks/.gitkeep +0 -0
- data/sample_app/lib/tasks/cucumber.rake +64 -0
- data/sample_app/log/.gitkeep +0 -0
- data/sample_app/public/404.html +26 -0
- data/sample_app/public/422.html +26 -0
- data/sample_app/public/500.html +26 -0
- data/sample_app/public/favicon.ico +0 -0
- data/sample_app/public/images/rails.png +0 -0
- data/sample_app/public/index.html +279 -0
- data/sample_app/public/javascripts/application.js +2 -0
- data/sample_app/public/javascripts/controls.js +965 -0
- data/sample_app/public/javascripts/dragdrop.js +974 -0
- data/sample_app/public/javascripts/effects.js +1123 -0
- data/sample_app/public/javascripts/prototype.js +4874 -0
- data/sample_app/public/javascripts/rails.js +118 -0
- data/sample_app/public/robots.txt +5 -0
- data/sample_app/public/stylesheets/.gitkeep +0 -0
- data/sample_app/script/cucumber +10 -0
- data/sample_app/script/rails +6 -0
- data/sample_app/spec/models/item_spec.rb +5 -0
- data/sample_app/spec/models/user_spec.rb +5 -0
- data/sample_app/spec/spec_helper.rb +27 -0
- data/sample_app/vendor/assets/javascripts/.gitkeep +0 -0
- data/sample_app/vendor/assets/stylesheets/.gitkeep +0 -0
- data/sample_app/vendor/plugins/.gitkeep +0 -0
- data/spec/config/shards.yml +231 -0
- data/spec/migrations/10_create_users_using_replication.rb +9 -0
- data/spec/migrations/11_add_field_in_all_slaves.rb +11 -0
- data/spec/migrations/12_create_users_using_block.rb +23 -0
- data/spec/migrations/13_create_users_using_block_and_using.rb +15 -0
- data/spec/migrations/14_create_users_on_shards_of_a_group_with_versions.rb +11 -0
- data/spec/migrations/15_create_user_on_shards_of_default_group_with_versions.rb +9 -0
- data/spec/migrations/1_create_users_on_master.rb +9 -0
- data/spec/migrations/2_create_users_on_canada.rb +11 -0
- data/spec/migrations/3_create_users_on_both_shards.rb +11 -0
- data/spec/migrations/4_create_users_on_shards_of_a_group.rb +11 -0
- data/spec/migrations/5_create_users_on_multiples_groups.rb +11 -0
- data/spec/migrations/6_raise_exception_with_invalid_shard_name.rb +11 -0
- data/spec/migrations/7_raise_exception_with_invalid_multiple_shard_names.rb +11 -0
- data/spec/migrations/8_raise_exception_with_invalid_group_name.rb +11 -0
- data/spec/migrations/9_raise_exception_with_multiple_invalid_group_names.rb +11 -0
- data/spec/octopus/association_shard_tracking_spec.rb +1036 -0
- data/spec/octopus/collection_proxy_spec.rb +16 -0
- data/spec/octopus/load_balancing/round_robin_spec.rb +15 -0
- data/spec/octopus/log_subscriber_spec.rb +19 -0
- data/spec/octopus/migration_spec.rb +151 -0
- data/spec/octopus/model_spec.rb +837 -0
- data/spec/octopus/octopus_spec.rb +123 -0
- data/spec/octopus/proxy_spec.rb +303 -0
- data/spec/octopus/query_cache_for_shards_spec.rb +40 -0
- data/spec/octopus/relation_proxy_spec.rb +132 -0
- data/spec/octopus/replicated_slave_grouped_spec.rb +91 -0
- data/spec/octopus/replication_spec.rb +196 -0
- data/spec/octopus/scope_proxy_spec.rb +97 -0
- data/spec/octopus/sharded_replicated_slave_grouped_spec.rb +55 -0
- data/spec/octopus/sharded_spec.rb +33 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/active_record/connection_adapters/modify_config_adapter.rb +15 -0
- data/spec/support/database_connection.rb +4 -0
- data/spec/support/database_models.rb +118 -0
- data/spec/support/octopus_helper.rb +66 -0
- data/spec/support/query_count.rb +17 -0
- data/spec/support/shared_contexts.rb +18 -0
- data/spec/tasks/octopus.rake_spec.rb +32 -0
- metadata +351 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# query cache methods are moved to ConnectionPool for Rails >= 5.0
|
|
2
|
+
module Octopus
|
|
3
|
+
module ConnectionPool
|
|
4
|
+
module QueryCacheForShards
|
|
5
|
+
%i(enable_query_cache! disable_query_cache!).each do |method|
|
|
6
|
+
define_method(method) do
|
|
7
|
+
if(Octopus.enabled? && (shards = ActiveRecord::Base.connection.shards)['master'] == self)
|
|
8
|
+
shards.each do |shard_name, v|
|
|
9
|
+
if shard_name == 'master'
|
|
10
|
+
super()
|
|
11
|
+
else
|
|
12
|
+
v.public_send(method)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
else
|
|
16
|
+
super()
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
ActiveRecord::ConnectionAdapters::ConnectionPool.send(:prepend, Octopus::ConnectionPool::QueryCacheForShards)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Octopus
|
|
2
|
+
class RelationProxy < BasicObject
|
|
3
|
+
include ::Octopus::ShardTracking::Attribute
|
|
4
|
+
|
|
5
|
+
module CaseFixer
|
|
6
|
+
def ===(other)
|
|
7
|
+
other = other.ar_relation while ::Octopus::RelationProxy === other
|
|
8
|
+
super
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
attr_accessor :ar_relation
|
|
13
|
+
|
|
14
|
+
def initialize(shard, ar_relation)
|
|
15
|
+
@current_shard = shard
|
|
16
|
+
@ar_relation = ar_relation
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def respond_to?(*args)
|
|
20
|
+
method_missing(:respond_to?, *args)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# methods redefined in ActiveRecord that should run_on_shard
|
|
24
|
+
ENUM_METHODS = (::Enumerable.instance_methods - ::Object.instance_methods).reject do |m|
|
|
25
|
+
::ActiveRecord::Relation.instance_method(m).source_location rescue nil
|
|
26
|
+
end + [:each, :map, :index_by]
|
|
27
|
+
# `find { ... }` etc. should run_on_shard, `find(id)` should be sent to relation
|
|
28
|
+
ENUM_WITH_BLOCK_METHODS = [:find, :select, :none?, :any?, :one?, :many?, :sum]
|
|
29
|
+
BATCH_METHODS = [:find_each, :find_in_batches, :in_batches]
|
|
30
|
+
WHERE_CHAIN_METHODS = [:not]
|
|
31
|
+
|
|
32
|
+
def method_missing(method, *args, &block)
|
|
33
|
+
if !block && BATCH_METHODS.include?(method)
|
|
34
|
+
::Enumerator.new do |yielder|
|
|
35
|
+
run_on_shard do
|
|
36
|
+
parsed_args = args.empty? ? {} : args.first
|
|
37
|
+
|
|
38
|
+
@ar_relation.public_send(method, **parsed_args) do |batch_item|
|
|
39
|
+
yielder << batch_item
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
elsif ENUM_METHODS.include?(method) || block && ENUM_WITH_BLOCK_METHODS.include?(method)
|
|
44
|
+
run_on_shard { @ar_relation.to_a }.public_send(method, *args, &block)
|
|
45
|
+
elsif WHERE_CHAIN_METHODS.include?(method)
|
|
46
|
+
::Octopus::ScopeProxy.new(@current_shard, run_on_shard { @ar_relation.public_send(method, *args) } )
|
|
47
|
+
elsif block
|
|
48
|
+
parsed_args = args.empty? ? {} : args.first
|
|
49
|
+
|
|
50
|
+
@ar_relation.public_send(method, **parsed_args, &block)
|
|
51
|
+
else
|
|
52
|
+
run_on_shard do
|
|
53
|
+
if method == :load_records
|
|
54
|
+
@ar_relation.send(method, *args)
|
|
55
|
+
else
|
|
56
|
+
@ar_relation.public_send(method, *args)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def ==(other)
|
|
63
|
+
case other
|
|
64
|
+
when ::Octopus::RelationProxy
|
|
65
|
+
method_missing(:==, other.ar_relation)
|
|
66
|
+
else
|
|
67
|
+
method_missing(:==, other)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
alias_method :eql?, :==
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
ActiveRecord::Relation.extend(Octopus::RelationProxy::CaseFixer)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Octopus::ResultPatch
|
|
2
|
+
attr_accessor :current_shard
|
|
3
|
+
|
|
4
|
+
private
|
|
5
|
+
|
|
6
|
+
def hash_rows
|
|
7
|
+
if current_shard.blank?
|
|
8
|
+
super
|
|
9
|
+
else
|
|
10
|
+
foo = super
|
|
11
|
+
foo.each { |f| f.merge!('current_shard' => current_shard) }
|
|
12
|
+
foo
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class ActiveRecord::Result
|
|
18
|
+
prepend Octopus::ResultPatch
|
|
19
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Octopus
|
|
2
|
+
class ScopeProxy < BasicObject
|
|
3
|
+
include ::Octopus::ShardTracking::Attribute
|
|
4
|
+
|
|
5
|
+
module CaseFixer
|
|
6
|
+
def ===(other)
|
|
7
|
+
other = other.klass while ::Octopus::ScopeProxy === other
|
|
8
|
+
super
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
attr_accessor :klass
|
|
13
|
+
|
|
14
|
+
# Dup and clone should be delegated to the class.
|
|
15
|
+
# We want to dup the query, not the scope proxy.
|
|
16
|
+
delegate :dup, :clone, to: :klass
|
|
17
|
+
|
|
18
|
+
def initialize(shard, klass)
|
|
19
|
+
@current_shard = shard
|
|
20
|
+
@klass = klass
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def using(shard)
|
|
24
|
+
fail "Nonexistent Shard Name: #{shard}" if @klass.connection.shards[shard].nil?
|
|
25
|
+
@current_shard = shard
|
|
26
|
+
self
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Transaction Method send all queries to a specified shard.
|
|
30
|
+
def transaction(options = {}, &block)
|
|
31
|
+
run_on_shard { klass.transaction(**options, &block) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def connection
|
|
35
|
+
@klass.connection_proxy.current_shard = @current_shard
|
|
36
|
+
|
|
37
|
+
if @klass.custom_octopus_connection && @klass.allowed_shard?(@current_shard)
|
|
38
|
+
# Force use of proxy, given we called 'using' explicitly to get here
|
|
39
|
+
@klass.connection_proxy.current_model = @klass
|
|
40
|
+
@klass.connection_proxy
|
|
41
|
+
else
|
|
42
|
+
@klass.connection
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def method_missing(method, *args, &block)
|
|
47
|
+
result = run_on_shard { @klass.__send__(method, *args, &block) }
|
|
48
|
+
if result.respond_to?(:all)
|
|
49
|
+
return ::Octopus::ScopeProxy.new(current_shard, result)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
if result.respond_to?(:current_shard)
|
|
53
|
+
result.current_shard = current_shard
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
result
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Delegates to method_missing (instead of @klass) so that User.using(:blah).where(:name => "Mike")
|
|
60
|
+
# gets run in the correct shard context when #== is evaluated.
|
|
61
|
+
def ==(other)
|
|
62
|
+
method_missing(:==, other)
|
|
63
|
+
end
|
|
64
|
+
alias_method :eql?, :==
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
ActiveRecord::Relation.extend(Octopus::ScopeProxy::CaseFixer)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Adds current_shard as an attribute; provide a default
|
|
2
|
+
# implementation of set_current_shard which considers
|
|
3
|
+
# only the current ActiveRecord::Base.connection_proxy
|
|
4
|
+
module Octopus
|
|
5
|
+
module ShardTracking
|
|
6
|
+
module Attribute
|
|
7
|
+
def self.included(base)
|
|
8
|
+
base.send(:include, Octopus::ShardTracking)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
attr_accessor :current_shard
|
|
12
|
+
|
|
13
|
+
def set_current_shard
|
|
14
|
+
return unless Octopus.enabled?
|
|
15
|
+
|
|
16
|
+
if ActiveRecord::Base.connection_proxy.block
|
|
17
|
+
self.current_shard = ActiveRecord::Base.connection_proxy.current_shard
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Octopus
|
|
2
|
+
module ShardTracking
|
|
3
|
+
def self.included(base)
|
|
4
|
+
base.extend(ClassMethods)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
module ClassMethods
|
|
8
|
+
# If the class which includes this module responds to the class
|
|
9
|
+
# method sharded_methods, then automagically alias_method_chain
|
|
10
|
+
# a sharding-friendly version of each of those methods into existence
|
|
11
|
+
def sharded_methods(*methods)
|
|
12
|
+
methods.each { |m| create_sharded_method(m) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def create_sharded_method(name)
|
|
16
|
+
name.to_s =~ /([^!?]+)([!?])?/
|
|
17
|
+
method, punctuation = [Regexp.last_match[1], Regexp.last_match[2]]
|
|
18
|
+
with = :"#{method}_with_octopus#{punctuation}"
|
|
19
|
+
without = :"#{method}_without_octopus#{punctuation}"
|
|
20
|
+
define_method with do |*args, &block|
|
|
21
|
+
run_on_shard { send(without, *args, &block) }
|
|
22
|
+
end
|
|
23
|
+
alias_method without.to_sym, name.to_sym
|
|
24
|
+
alias_method name.to_sym, with.to_sym
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Adds run_on_shard method, but does not implement current_shard method
|
|
29
|
+
def run_on_shard(&block)
|
|
30
|
+
if (cs = current_shard)
|
|
31
|
+
r = ActiveRecord::Base.connection_proxy.run_queries_on_shard(cs, &block)
|
|
32
|
+
# Use a case statement to avoid any path through ActiveRecord::Delegation's
|
|
33
|
+
# respond_to? code. We want to avoid the respond_to? code because it can have
|
|
34
|
+
# the side effect of causing a call to load_target
|
|
35
|
+
|
|
36
|
+
if (ActiveRecord::Relation === r || ActiveRecord::QueryMethods::WhereChain === r) && !(Octopus::RelationProxy === r)
|
|
37
|
+
Octopus::RelationProxy.new(cs, r)
|
|
38
|
+
else
|
|
39
|
+
r
|
|
40
|
+
end
|
|
41
|
+
else
|
|
42
|
+
yield
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Octopus
|
|
2
|
+
class SlaveGroup
|
|
3
|
+
def initialize(slaves)
|
|
4
|
+
slaves = HashWithIndifferentAccess.new(slaves)
|
|
5
|
+
slaves_list = slaves.values
|
|
6
|
+
@load_balancer = Octopus.load_balancer.new(slaves_list)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def next(options)
|
|
10
|
+
@load_balancer.next options
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
data/lib/octopus.rb
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
require 'active_record'
|
|
2
|
+
require 'active_support/version'
|
|
3
|
+
require 'active_support/core_ext/class'
|
|
4
|
+
require 'byebug'
|
|
5
|
+
|
|
6
|
+
require 'yaml'
|
|
7
|
+
require 'erb'
|
|
8
|
+
|
|
9
|
+
module Octopus
|
|
10
|
+
def self.env
|
|
11
|
+
@env ||= 'octopus'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.rails_env
|
|
15
|
+
@rails_env ||= defined?(::Rails.env) ? Rails.env.to_s : 'shards'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.config
|
|
19
|
+
@config ||= begin
|
|
20
|
+
file_name = File.join(Octopus.directory, 'config/shards.yml').to_s
|
|
21
|
+
|
|
22
|
+
if File.exist?(file_name) || File.symlink?(file_name)
|
|
23
|
+
config ||= HashWithIndifferentAccess.new(YAML.load(ERB.new(File.read(file_name)).result))[Octopus.env]
|
|
24
|
+
else
|
|
25
|
+
config ||= HashWithIndifferentAccess.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
config
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.load_balancer=(balancer)
|
|
33
|
+
@load_balancer = balancer
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.load_balancer
|
|
37
|
+
@load_balancer ||= Octopus::LoadBalancing::RoundRobin
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.master_shard
|
|
41
|
+
((config && config[:master_shard]) || :master).to_sym
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Public: Whether or not Octopus is configured and should hook into the
|
|
45
|
+
# current environment. Checks the environments config option for the Rails
|
|
46
|
+
# environment by default.
|
|
47
|
+
#
|
|
48
|
+
# Returns a boolean
|
|
49
|
+
def self.enabled?
|
|
50
|
+
if defined?(::Rails.env)
|
|
51
|
+
Octopus.environments.include?(Rails.env.to_s)
|
|
52
|
+
else
|
|
53
|
+
# TODO: This doens't feel right but !Octopus.config.blank? is breaking a
|
|
54
|
+
# test. Also, Octopus.config is always returning a hash.
|
|
55
|
+
Octopus.config
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Returns the Rails.root_to_s when you are using rails
|
|
60
|
+
# Running the current directory in a generic Ruby process
|
|
61
|
+
def self.directory
|
|
62
|
+
@directory ||= defined?(::Rails.root) ? Rails.root.to_s : Dir.pwd
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# This is the default way to do Octopus Setup
|
|
66
|
+
# Available variables:
|
|
67
|
+
# :enviroments => the enviroments that octopus will run. default: 'production'
|
|
68
|
+
def self.setup
|
|
69
|
+
yield self
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.environments=(environments)
|
|
73
|
+
@environments = environments.map(&:to_s)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def self.environments
|
|
77
|
+
@environments ||= config['environments'] || ['production']
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def self.robust_environments=(environments)
|
|
81
|
+
@robust_environments = environments.map(&:to_s)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Environments in which to swallow failures from a single shard
|
|
85
|
+
# when iterating through all.
|
|
86
|
+
def self.robust_environments
|
|
87
|
+
@robust_environments ||= config['robust_environments'] || ['production']
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def self.robust_environment?
|
|
91
|
+
robust_environments.include? rails_env
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def self.rails4?
|
|
95
|
+
ActiveRecord::VERSION::MAJOR == 4
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def self.rails42?
|
|
99
|
+
rails4? && ActiveRecord::VERSION::MINOR == 2
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def self.rails50?
|
|
103
|
+
ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR == 0
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def self.atleast_rails50?
|
|
107
|
+
ActiveRecord::VERSION::MAJOR >= 5
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def self.rails51?
|
|
111
|
+
ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR == 1
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def self.rails52?
|
|
115
|
+
ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR == 2
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def self.rails60?
|
|
119
|
+
ActiveRecord::VERSION::MAJOR > 6 || ActiveRecord::VERSION::MAJOR == 6
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def self.atleast_rails51?
|
|
123
|
+
ActiveRecord::VERSION::MAJOR > 5 || (ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR >= 1)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def self.atleast_rails52?
|
|
127
|
+
ActiveRecord::VERSION::MAJOR > 5 || (ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR > 1)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
attr_writer :logger
|
|
131
|
+
|
|
132
|
+
def self.logger
|
|
133
|
+
if defined?(Rails.logger)
|
|
134
|
+
@logger ||= Rails.logger
|
|
135
|
+
else
|
|
136
|
+
@logger ||= Logger.new($stderr)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def self.shards=(shards)
|
|
141
|
+
config[rails_env] = HashWithIndifferentAccess.new(shards)
|
|
142
|
+
ActiveRecord::Base.connection.initialize_shards(@config)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def self.using(shard, &block)
|
|
146
|
+
conn = ActiveRecord::Base.connection
|
|
147
|
+
|
|
148
|
+
if conn.is_a?(Octopus::Proxy)
|
|
149
|
+
conn.run_queries_on_shard(shard, &block)
|
|
150
|
+
else
|
|
151
|
+
yield
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def self.using_group(group, &block)
|
|
156
|
+
conn = ActiveRecord::Base.connection
|
|
157
|
+
|
|
158
|
+
if conn.is_a?(Octopus::Proxy)
|
|
159
|
+
conn.send_queries_to_group(group, &block)
|
|
160
|
+
else
|
|
161
|
+
yield
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def self.using_all(&block)
|
|
166
|
+
conn = ActiveRecord::Base.connection
|
|
167
|
+
|
|
168
|
+
if conn.is_a?(Octopus::Proxy)
|
|
169
|
+
conn.send_queries_to_all_shards(&block)
|
|
170
|
+
else
|
|
171
|
+
yield
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def self.fully_replicated(&_block)
|
|
176
|
+
old_fully_replicated = Thread.current[Octopus::ProxyConfig::FULLY_REPLICATED_KEY]
|
|
177
|
+
Thread.current[Octopus::ProxyConfig::FULLY_REPLICATED_KEY] = true
|
|
178
|
+
yield
|
|
179
|
+
ensure
|
|
180
|
+
Thread.current[Octopus::ProxyConfig::FULLY_REPLICATED_KEY] = old_fully_replicated
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
require 'octopus/exception'
|
|
185
|
+
|
|
186
|
+
require 'octopus/shard_tracking'
|
|
187
|
+
require 'octopus/shard_tracking/attribute'
|
|
188
|
+
require 'octopus/shard_tracking/dynamic'
|
|
189
|
+
|
|
190
|
+
require 'octopus/model'
|
|
191
|
+
require 'octopus/result_patch'
|
|
192
|
+
require 'octopus/migration'
|
|
193
|
+
require 'octopus/association'
|
|
194
|
+
require 'octopus/collection_association'
|
|
195
|
+
require 'octopus/association_shard_tracking'
|
|
196
|
+
require 'octopus/persistence'
|
|
197
|
+
require 'octopus/log_subscriber'
|
|
198
|
+
require 'octopus/abstract_adapter'
|
|
199
|
+
require 'octopus/singular_association'
|
|
200
|
+
require 'octopus/finder_methods'
|
|
201
|
+
require 'octopus/query_cache_for_shards' unless Octopus.rails4?
|
|
202
|
+
|
|
203
|
+
require 'octopus/railtie' if defined?(::Rails::Railtie)
|
|
204
|
+
|
|
205
|
+
require 'octopus/proxy_config'
|
|
206
|
+
require 'octopus/proxy'
|
|
207
|
+
require 'octopus/collection_proxy'
|
|
208
|
+
require 'octopus/relation_proxy'
|
|
209
|
+
require 'octopus/scope_proxy'
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
namespace :octopus do
|
|
2
|
+
desc 'Copy schema version information from master to all shards'
|
|
3
|
+
task :copy_schema_versions => :environment do
|
|
4
|
+
abort('Octopus is not enabled for this environment') unless Octopus.enabled?
|
|
5
|
+
|
|
6
|
+
connection = ActiveRecord::Base.connection
|
|
7
|
+
|
|
8
|
+
current_version = ActiveRecord::Migrator.current_version
|
|
9
|
+
migrations_paths = ActiveRecord::Migrator.migrations_paths
|
|
10
|
+
|
|
11
|
+
connection.send_queries_to_multiple_shards(connection.shard_names) do
|
|
12
|
+
ActiveRecord::Schema.initialize_schema_migrations_table
|
|
13
|
+
ActiveRecord::Schema.assume_migrated_upto_version(current_version, migrations_paths)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/sample_app/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--colour
|
data/sample_app/Gemfile
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
source 'https://rubygems.org'
|
|
2
|
+
|
|
3
|
+
gem 'rails', '3.2.13'
|
|
4
|
+
|
|
5
|
+
# Bundle edge Rails instead:
|
|
6
|
+
# gem 'rails', :git => 'git://github.com/rails/rails.git'
|
|
7
|
+
|
|
8
|
+
gem 'pg'
|
|
9
|
+
gem 'sqlite3'
|
|
10
|
+
gem 'ar-octopus', '0.6.0'
|
|
11
|
+
gem 'pry'
|
|
12
|
+
|
|
13
|
+
group :test do
|
|
14
|
+
gem 'capybara'
|
|
15
|
+
gem 'cucumber-rails', :require => false
|
|
16
|
+
gem 'launchy'
|
|
17
|
+
gem 'rspec-rails'
|
|
18
|
+
gem 'aruba'
|
|
19
|
+
gem 'database_cleaner'
|
|
20
|
+
end
|