ar-octopus-ruby-3 0.11.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (160) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +46 -0
  5. data/.rubocop_todo.yml +56 -0
  6. data/.travis.yml +18 -0
  7. data/Appraisals +16 -0
  8. data/Gemfile +4 -0
  9. data/README.mkdn +257 -0
  10. data/Rakefile +175 -0
  11. data/TODO.txt +7 -0
  12. data/ar-octopus.gemspec +44 -0
  13. data/gemfiles/rails42.gemfile +7 -0
  14. data/gemfiles/rails5.gemfile +7 -0
  15. data/gemfiles/rails51.gemfile +7 -0
  16. data/gemfiles/rails52.gemfile +7 -0
  17. data/lib/ar-octopus.rb +1 -0
  18. data/lib/octopus/abstract_adapter.rb +33 -0
  19. data/lib/octopus/association.rb +14 -0
  20. data/lib/octopus/association_shard_tracking.rb +74 -0
  21. data/lib/octopus/collection_association.rb +17 -0
  22. data/lib/octopus/collection_proxy.rb +16 -0
  23. data/lib/octopus/exception.rb +4 -0
  24. data/lib/octopus/finder_methods.rb +8 -0
  25. data/lib/octopus/load_balancing/round_robin.rb +20 -0
  26. data/lib/octopus/load_balancing.rb +4 -0
  27. data/lib/octopus/log_subscriber.rb +26 -0
  28. data/lib/octopus/migration.rb +236 -0
  29. data/lib/octopus/model.rb +216 -0
  30. data/lib/octopus/persistence.rb +45 -0
  31. data/lib/octopus/proxy.rb +399 -0
  32. data/lib/octopus/proxy_config.rb +251 -0
  33. data/lib/octopus/query_cache_for_shards.rb +24 -0
  34. data/lib/octopus/railtie.rb +11 -0
  35. data/lib/octopus/relation_proxy.rb +74 -0
  36. data/lib/octopus/result_patch.rb +19 -0
  37. data/lib/octopus/scope_proxy.rb +68 -0
  38. data/lib/octopus/shard_tracking/attribute.rb +22 -0
  39. data/lib/octopus/shard_tracking/dynamic.rb +11 -0
  40. data/lib/octopus/shard_tracking.rb +46 -0
  41. data/lib/octopus/singular_association.rb +9 -0
  42. data/lib/octopus/slave_group.rb +13 -0
  43. data/lib/octopus/version.rb +3 -0
  44. data/lib/octopus.rb +209 -0
  45. data/lib/tasks/octopus.rake +16 -0
  46. data/sample_app/.gitignore +4 -0
  47. data/sample_app/.rspec +1 -0
  48. data/sample_app/Gemfile +20 -0
  49. data/sample_app/Gemfile.lock +155 -0
  50. data/sample_app/README +3 -0
  51. data/sample_app/README.rdoc +261 -0
  52. data/sample_app/Rakefile +7 -0
  53. data/sample_app/app/assets/images/rails.png +0 -0
  54. data/sample_app/app/assets/javascripts/application.js +15 -0
  55. data/sample_app/app/assets/stylesheets/application.css +13 -0
  56. data/sample_app/app/controllers/application_controller.rb +4 -0
  57. data/sample_app/app/helpers/application_helper.rb +2 -0
  58. data/sample_app/app/mailers/.gitkeep +0 -0
  59. data/sample_app/app/models/.gitkeep +0 -0
  60. data/sample_app/app/models/item.rb +3 -0
  61. data/sample_app/app/models/user.rb +3 -0
  62. data/sample_app/app/views/layouts/application.html.erb +14 -0
  63. data/sample_app/autotest/discover.rb +2 -0
  64. data/sample_app/config/application.rb +62 -0
  65. data/sample_app/config/boot.rb +6 -0
  66. data/sample_app/config/cucumber.yml +8 -0
  67. data/sample_app/config/database.yml +28 -0
  68. data/sample_app/config/environment.rb +5 -0
  69. data/sample_app/config/environments/development.rb +37 -0
  70. data/sample_app/config/environments/production.rb +67 -0
  71. data/sample_app/config/environments/test.rb +37 -0
  72. data/sample_app/config/initializers/backtrace_silencers.rb +7 -0
  73. data/sample_app/config/initializers/inflections.rb +15 -0
  74. data/sample_app/config/initializers/mime_types.rb +5 -0
  75. data/sample_app/config/initializers/secret_token.rb +7 -0
  76. data/sample_app/config/initializers/session_store.rb +8 -0
  77. data/sample_app/config/initializers/wrap_parameters.rb +14 -0
  78. data/sample_app/config/locales/en.yml +5 -0
  79. data/sample_app/config/routes.rb +58 -0
  80. data/sample_app/config/shards.yml +28 -0
  81. data/sample_app/config.ru +4 -0
  82. data/sample_app/db/migrate/20100720172715_create_users.rb +15 -0
  83. data/sample_app/db/migrate/20100720172730_create_items.rb +16 -0
  84. data/sample_app/db/migrate/20100720210335_create_sample_users.rb +11 -0
  85. data/sample_app/db/schema.rb +29 -0
  86. data/sample_app/db/seeds.rb +16 -0
  87. data/sample_app/doc/README_FOR_APP +2 -0
  88. data/sample_app/features/migrate.feature +45 -0
  89. data/sample_app/features/seed.feature +15 -0
  90. data/sample_app/features/step_definitions/seeds_steps.rb +13 -0
  91. data/sample_app/features/step_definitions/web_steps.rb +218 -0
  92. data/sample_app/features/support/database.rb +13 -0
  93. data/sample_app/features/support/env.rb +57 -0
  94. data/sample_app/features/support/paths.rb +33 -0
  95. data/sample_app/lib/assets/.gitkeep +0 -0
  96. data/sample_app/lib/tasks/.gitkeep +0 -0
  97. data/sample_app/lib/tasks/cucumber.rake +64 -0
  98. data/sample_app/log/.gitkeep +0 -0
  99. data/sample_app/public/404.html +26 -0
  100. data/sample_app/public/422.html +26 -0
  101. data/sample_app/public/500.html +26 -0
  102. data/sample_app/public/favicon.ico +0 -0
  103. data/sample_app/public/images/rails.png +0 -0
  104. data/sample_app/public/index.html +279 -0
  105. data/sample_app/public/javascripts/application.js +2 -0
  106. data/sample_app/public/javascripts/controls.js +965 -0
  107. data/sample_app/public/javascripts/dragdrop.js +974 -0
  108. data/sample_app/public/javascripts/effects.js +1123 -0
  109. data/sample_app/public/javascripts/prototype.js +4874 -0
  110. data/sample_app/public/javascripts/rails.js +118 -0
  111. data/sample_app/public/robots.txt +5 -0
  112. data/sample_app/public/stylesheets/.gitkeep +0 -0
  113. data/sample_app/script/cucumber +10 -0
  114. data/sample_app/script/rails +6 -0
  115. data/sample_app/spec/models/item_spec.rb +5 -0
  116. data/sample_app/spec/models/user_spec.rb +5 -0
  117. data/sample_app/spec/spec_helper.rb +27 -0
  118. data/sample_app/vendor/assets/javascripts/.gitkeep +0 -0
  119. data/sample_app/vendor/assets/stylesheets/.gitkeep +0 -0
  120. data/sample_app/vendor/plugins/.gitkeep +0 -0
  121. data/spec/config/shards.yml +231 -0
  122. data/spec/migrations/10_create_users_using_replication.rb +9 -0
  123. data/spec/migrations/11_add_field_in_all_slaves.rb +11 -0
  124. data/spec/migrations/12_create_users_using_block.rb +23 -0
  125. data/spec/migrations/13_create_users_using_block_and_using.rb +15 -0
  126. data/spec/migrations/14_create_users_on_shards_of_a_group_with_versions.rb +11 -0
  127. data/spec/migrations/15_create_user_on_shards_of_default_group_with_versions.rb +9 -0
  128. data/spec/migrations/1_create_users_on_master.rb +9 -0
  129. data/spec/migrations/2_create_users_on_canada.rb +11 -0
  130. data/spec/migrations/3_create_users_on_both_shards.rb +11 -0
  131. data/spec/migrations/4_create_users_on_shards_of_a_group.rb +11 -0
  132. data/spec/migrations/5_create_users_on_multiples_groups.rb +11 -0
  133. data/spec/migrations/6_raise_exception_with_invalid_shard_name.rb +11 -0
  134. data/spec/migrations/7_raise_exception_with_invalid_multiple_shard_names.rb +11 -0
  135. data/spec/migrations/8_raise_exception_with_invalid_group_name.rb +11 -0
  136. data/spec/migrations/9_raise_exception_with_multiple_invalid_group_names.rb +11 -0
  137. data/spec/octopus/association_shard_tracking_spec.rb +1036 -0
  138. data/spec/octopus/collection_proxy_spec.rb +16 -0
  139. data/spec/octopus/load_balancing/round_robin_spec.rb +15 -0
  140. data/spec/octopus/log_subscriber_spec.rb +19 -0
  141. data/spec/octopus/migration_spec.rb +151 -0
  142. data/spec/octopus/model_spec.rb +837 -0
  143. data/spec/octopus/octopus_spec.rb +123 -0
  144. data/spec/octopus/proxy_spec.rb +303 -0
  145. data/spec/octopus/query_cache_for_shards_spec.rb +40 -0
  146. data/spec/octopus/relation_proxy_spec.rb +132 -0
  147. data/spec/octopus/replicated_slave_grouped_spec.rb +91 -0
  148. data/spec/octopus/replication_spec.rb +196 -0
  149. data/spec/octopus/scope_proxy_spec.rb +97 -0
  150. data/spec/octopus/sharded_replicated_slave_grouped_spec.rb +55 -0
  151. data/spec/octopus/sharded_spec.rb +33 -0
  152. data/spec/spec_helper.rb +18 -0
  153. data/spec/support/active_record/connection_adapters/modify_config_adapter.rb +15 -0
  154. data/spec/support/database_connection.rb +4 -0
  155. data/spec/support/database_models.rb +118 -0
  156. data/spec/support/octopus_helper.rb +66 -0
  157. data/spec/support/query_count.rb +17 -0
  158. data/spec/support/shared_contexts.rb +18 -0
  159. data/spec/tasks/octopus.rake_spec.rb +32 -0
  160. 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,11 @@
1
+ begin
2
+ module Octopus
3
+ class Railtie < Rails::Railtie
4
+ rake_tasks do
5
+ Dir[File.join(File.dirname(__FILE__), '../tasks/*.rake')].each { |ext| load ext }
6
+ end
7
+ end
8
+ end
9
+ rescue LoadError
10
+ # nop
11
+ end
@@ -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,11 @@
1
+ require 'octopus/shard_tracking'
2
+
3
+ module Octopus
4
+ module ShardTracking
5
+ module Dynamic
6
+ def self.included(base)
7
+ base.send(:include, Octopus::ShardTracking)
8
+ end
9
+ end
10
+ end
11
+ 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,9 @@
1
+ module Octopus
2
+ module SingularAssociation
3
+ def self.included(base)
4
+ base.sharded_methods :reader, :writer, :create, :create!, :build
5
+ end
6
+ end
7
+ end
8
+
9
+ ActiveRecord::Associations::SingularAssociation.send(:include, Octopus::SingularAssociation)
@@ -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
@@ -0,0 +1,3 @@
1
+ module Octopus
2
+ VERSION = '0.11.2'
3
+ 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
@@ -0,0 +1,4 @@
1
+ .bundle
2
+ db/*.sqlite3
3
+ log/*.log
4
+ tmp/**/*
data/sample_app/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -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