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.
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