ar-octopus 0.8.5 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.travis.yml +6 -9
  4. data/Appraisals +8 -8
  5. data/README.mkdn +47 -15
  6. data/Rakefile +1 -0
  7. data/ar-octopus.gemspec +9 -12
  8. data/gemfiles/rails42.gemfile +2 -2
  9. data/gemfiles/{rails32.gemfile → rails5.gemfile} +2 -2
  10. data/gemfiles/{rails4.gemfile → rails51.gemfile} +2 -2
  11. data/gemfiles/{rails41.gemfile → rails52.gemfile} +2 -2
  12. data/lib/octopus/abstract_adapter.rb +4 -10
  13. data/lib/octopus/association.rb +1 -0
  14. data/lib/octopus/association_shard_tracking.rb +41 -71
  15. data/lib/octopus/collection_association.rb +9 -3
  16. data/lib/octopus/exception.rb +4 -0
  17. data/lib/octopus/finder_methods.rb +8 -0
  18. data/lib/octopus/load_balancing/round_robin.rb +2 -1
  19. data/lib/octopus/log_subscriber.rb +6 -2
  20. data/lib/octopus/migration.rb +123 -55
  21. data/lib/octopus/model.rb +42 -27
  22. data/lib/octopus/persistence.rb +33 -27
  23. data/lib/octopus/proxy.rb +147 -272
  24. data/lib/octopus/proxy_config.rb +251 -0
  25. data/lib/octopus/query_cache_for_shards.rb +24 -0
  26. data/lib/octopus/railtie.rb +0 -2
  27. data/lib/octopus/relation_proxy.rb +36 -1
  28. data/lib/octopus/result_patch.rb +19 -0
  29. data/lib/octopus/scope_proxy.rb +12 -5
  30. data/lib/octopus/shard_tracking.rb +8 -3
  31. data/lib/octopus/slave_group.rb +3 -3
  32. data/lib/octopus/version.rb +1 -1
  33. data/lib/octopus.rb +71 -18
  34. data/spec/config/shards.yml +12 -0
  35. data/spec/migrations/10_create_users_using_replication.rb +1 -1
  36. data/spec/migrations/11_add_field_in_all_slaves.rb +1 -1
  37. data/spec/migrations/12_create_users_using_block.rb +1 -1
  38. data/spec/migrations/13_create_users_using_block_and_using.rb +1 -1
  39. data/spec/migrations/14_create_users_on_shards_of_a_group_with_versions.rb +1 -1
  40. data/spec/migrations/15_create_user_on_shards_of_default_group_with_versions.rb +1 -1
  41. data/spec/migrations/1_create_users_on_master.rb +1 -1
  42. data/spec/migrations/2_create_users_on_canada.rb +1 -1
  43. data/spec/migrations/3_create_users_on_both_shards.rb +1 -1
  44. data/spec/migrations/4_create_users_on_shards_of_a_group.rb +1 -1
  45. data/spec/migrations/5_create_users_on_multiples_groups.rb +1 -1
  46. data/spec/migrations/6_raise_exception_with_invalid_shard_name.rb +1 -1
  47. data/spec/migrations/7_raise_exception_with_invalid_multiple_shard_names.rb +1 -1
  48. data/spec/migrations/8_raise_exception_with_invalid_group_name.rb +1 -1
  49. data/spec/migrations/9_raise_exception_with_multiple_invalid_group_names.rb +1 -1
  50. data/spec/octopus/association_shard_tracking_spec.rb +344 -16
  51. data/spec/octopus/load_balancing/round_robin_spec.rb +15 -0
  52. data/spec/octopus/log_subscriber_spec.rb +1 -1
  53. data/spec/octopus/migration_spec.rb +45 -11
  54. data/spec/octopus/model_spec.rb +204 -16
  55. data/spec/octopus/octopus_spec.rb +2 -2
  56. data/spec/octopus/proxy_spec.rb +44 -40
  57. data/spec/octopus/query_cache_for_shards_spec.rb +40 -0
  58. data/spec/octopus/relation_proxy_spec.rb +71 -23
  59. data/spec/octopus/replicated_slave_grouped_spec.rb +27 -0
  60. data/spec/octopus/replication_spec.rb +72 -2
  61. data/spec/octopus/scope_proxy_spec.rb +41 -7
  62. data/spec/spec_helper.rb +2 -0
  63. data/spec/support/database_connection.rb +1 -1
  64. data/spec/support/database_models.rb +1 -1
  65. data/spec/support/octopus_helper.rb +14 -6
  66. data/spec/tasks/octopus.rake_spec.rb +1 -1
  67. metadata +40 -30
  68. data/.ruby-version +0 -1
  69. data/init.rb +0 -1
  70. data/lib/octopus/has_and_belongs_to_many_association.rb +0 -9
  71. data/rails/init.rb +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a2e171072013526c5597b2d233d983f129a84cec
4
- data.tar.gz: 0c05c12e05f5dfb64aaf6f15e0accb59c2174a53
2
+ SHA256:
3
+ metadata.gz: be74de9c9b6dfacee338dfa5d500da6ce758f501e3be7fa0f154afda07f31f40
4
+ data.tar.gz: 1892942a155383583a054e4d61cc588ebca2b33749ca191762d550bf9bf13022
5
5
  SHA512:
6
- metadata.gz: 19f50eb815a6273a87430354c5124096d90d561a8b27c1bfe0073a5a81f3a2242c71f099b88db0bab031018c56a8662ed587122412a0042ba543bc41c6b0d745
7
- data.tar.gz: 11828df91744a59445fe6a4add2c6081971f724ed9394b96eb12b36c6a09e69db2546d7dc1eadc56c19074ed5de1d7cd28f39a38c0bd7d3ad88030238a46a98e
6
+ metadata.gz: c0b21eb7e0fa5397b77c5df6c0bdce5fced41b7284ee7fc4869539ec6219bfcbe2f02a7e85239979b7534a298e2b7ad34e6833fdeb0fd81c67c536b17ea73ae9
7
+ data.tar.gz: 34bd15fbfb892fca1329f69abf85ba4421d563278d846a67014b710b473919d50cfc3425ed76de1580484a4f10554d4464a490e5ac49b3339553b810acdacfd5
data/.gitignore CHANGED
@@ -2,6 +2,7 @@
2
2
  *.sqlite3
3
3
  .bundle
4
4
  .rvmrc
5
+ .ruby-version
5
6
  Gemfile.lock
6
7
  gemfiles/*.lock
7
8
  pkg/*
data/.travis.yml CHANGED
@@ -4,18 +4,15 @@ env:
4
4
  before_script:
5
5
  - "bundle exec rake db:prepare"
6
6
  rvm:
7
- - 2.0.0
8
- - 2.1.5
9
- - 2.2.0
7
+ - 2.2.7
8
+ - 2.3.4
9
+ - 2.4.1
10
10
  gemfile:
11
- - gemfiles/rails4.gemfile
12
- - gemfiles/rails41.gemfile
13
11
  - gemfiles/rails42.gemfile
12
+ - gemfiles/rails5.gemfile
13
+ - gemfiles/rails51.gemfile
14
+ - gemfiles/rails52.gemfile
14
15
  notifications:
15
16
  recipients:
16
17
  - gabriel.sobrinho@gmail.com
17
18
  - thiago.pradi@gmail.com
18
- matrix:
19
- include:
20
- - rvm: 1.9.3
21
- gemfile: gemfiles/rails32.gemfile
data/Appraisals CHANGED
@@ -1,16 +1,16 @@
1
- appraise "rails32" do
2
- gem "activerecord", "~> 3.2.0"
1
+ appraise "rails42" do
2
+ gem "activerecord", "~> 4.2.0"
3
3
  end
4
4
 
5
- appraise "rails4" do
6
- gem "activerecord", "~> 4.0.0"
5
+ appraise "rails5" do
6
+ gem "activerecord", "~> 5.0.0"
7
7
  end
8
8
 
9
- appraise "rails41" do
10
- gem "activerecord", "~> 4.1.0"
9
+ appraise "rails51" do
10
+ gem "activerecord", "~> 5.1.0"
11
11
  end
12
12
 
13
- appraise "rails42" do
14
- gem "activerecord", "~> 4.2.0"
13
+ appraise "rails52" do
14
+ gem "activerecord", "~> 5.2.0"
15
15
  end
16
16
  # vim: ft=ruby
data/README.mkdn CHANGED
@@ -1,11 +1,16 @@
1
+ # Important Notice:
2
+
3
+ Octopus will enter into maintainance mode once Rails 6 is released - since Rails 6 will include all the features for Database Sharding / Replication into Rails Core (PR: https://github.com/rails/rails/pull/34052).
4
+ Once the first version of Rails 6 beta is released, there will be a migration guide to help users migrate from Octopus to Rails 6.
5
+
1
6
  # Octopus - Easy Database Sharding for ActiveRecord
2
7
 
3
- <a href='http://www.pledgie.com/campaigns/20950'><img alt='Click here to lend your support to: Octopus and make a donation at www.pledgie.com !' src='http://www.pledgie.com/campaigns/20950.png?skin_name=chrome' border='0' /></a> [![Build Status](https://travis-ci.org/tchandy/octopus.png)](https://travis-ci.org/tchandy/octopus) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/tchandy/octopus)
8
+ [![Build Status](https://travis-ci.org/thiagopradi/octopus.svg)](https://travis-ci.org/thiagopradi/octopus) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/thiagopradi/octopus)
4
9
 
5
10
  Octopus is a better way to do Database Sharding in ActiveRecord. Sharding allows multiple databases in the same rails application. While there are several projects that implement Sharding (e.g. DbCharmer, DataFabric, MultiDb), each project has its own limitations. The main goal of octopus project is to provide a better way of doing Database Sharding.
6
11
 
7
12
  ## Feature list:
8
- The api is designed to be simple as possible. Octopus focuses on the end user, giving the power of multiple databases but with reliable code and flexibility. Octopus is compatible with Rails 3.2 and Rails 4.
13
+ The api is designed to be simple as possible. Octopus focuses on the end user, giving the power of multiple databases but with reliable code and flexibility. Octopus is compatible with Rails 4 and Rails 5.
9
14
 
10
15
  Octopus supports:
11
16
 
@@ -15,15 +20,15 @@ Octopus supports:
15
20
  - Tools to manage database configurations. (soon)
16
21
 
17
22
  ### Replication
18
- When using replication, all writes queries will be sent to master, and read queries to slaves. More info could be found at: <a href="http://wiki.github.com/tchandy/octopus/replication"> Wiki</a>
23
+ When using replication, all writes queries will be sent to master, and read queries to slaves. More info could be found at: <a href="http://wiki.github.com/thiagopradi/octopus/replication"> Wiki</a>
19
24
 
20
25
  ### Sharding
21
- When using sharding, you need to specify which shard to send the query. Octopus supports selecting the shard inside a controller, or manually in each object. More could be found at <a href="http://wiki.github.com/tchandy/octopus/sharding"> Wiki</a>
26
+ When using sharding, you need to specify which shard to send the query. Octopus supports selecting the shard inside a controller, or manually in each object. More could be found at <a href="http://wiki.github.com/thiagopradi/octopus/sharding"> Wiki</a>
22
27
 
23
28
  ### Replication + Sharding
24
- When using replication and sharding concurrently, you must specify a shard, and can optionally specify a <a href="https://github.com/tchandy/octopus/wiki/Slave-Groups">slave group</a>.
29
+ When using replication and sharding concurrently, you must specify a shard, and can optionally specify a <a href="https://github.com/thiagopradi/octopus/wiki/Slave-Groups">slave group</a>.
25
30
  All write queries will be sent to each shard's master. If the slave group is specified read queries will be sent to slaves in it, or else to shard's master.
26
- More info could be found at <a href="https://github.com/tchandy/octopus/wiki/Slave-Groups"> Wiki</a>
31
+ More info could be found at <a href="https://github.com/thiagopradi/octopus/wiki/Slave-Groups"> Wiki</a>
27
32
 
28
33
  ## Install
29
34
 
@@ -55,21 +60,35 @@ going forward.
55
60
 
56
61
  ## How to use Octopus?
57
62
 
58
- First, you need to create a config file, shards.yml, inside your config/ directory. to see the syntax and how this file should look, please checkout <a href="http://wiki.github.com/tchandy/octopus/config-file">this page on wiki</a>.
63
+ First, you need to create a config file, shards.yml, inside your config/ directory. to see the syntax and how this file should look, please checkout <a href="http://wiki.github.com/thiagopradi/octopus/config-file">this page on wiki</a>.
59
64
 
60
65
  ### Syntax
61
66
 
62
67
  Octopus adds a method to each AR Class and object: the using method is used to select the shard like this:
63
68
 
64
69
  ```ruby
65
- User.where(:name => "Thiago").limit(3).using(:slave_one)
70
+ User.where(:name => "Boba").limit(3).using(:read_replica_one)
66
71
  ```
67
72
 
68
73
  Octopus also supports queries within a block. When you pass a block to the using method, all queries inside the block will be sent to the specified shard.
69
74
 
70
75
  ```ruby
71
- Octopus.using(:slave_two) do
72
- User.create(:name => "Mike")
76
+ Octopus.using(:read_replica_two) do
77
+ User.create(:name => "Thiago")
78
+ end
79
+ ```
80
+
81
+ If you want to use the same code for all shards or all shards in a specific group (for example in `db/seeds.rb`), you can use this syntax.
82
+
83
+ ```ruby
84
+ # This will return a list of the given block's results, per shard.
85
+ Octopus.using_all do
86
+ User.create_from_csv!
87
+ end
88
+
89
+ # This will return a list of the given block's results, per shard in history_shards group.
90
+ Octopus.using_group(:history_shards) do
91
+ HistoryCategory.create_from_csv!
73
92
  end
74
93
  ```
75
94
 
@@ -131,6 +150,19 @@ octopus:
131
150
 
132
151
  There is no need for a corresponding `default_migration_shard` - simply define that database to be your master. You might want this setting if all of your databases have identical schemas, but are not replicated.
133
152
 
153
+ You can configure a master shard for the rails application, to connect to when rails is going up. The safest would be to configure this to the shard specified in `database.yml` (some things still use it).
154
+
155
+ ```yaml
156
+ octopus:
157
+ master_shard: <%= ENV['SHARD'] || 'shard1' %>
158
+ ```
159
+
160
+ Then you can use the `SHARD` environment variable to override the `master_shard` specified in `config/shards.yml`, useful for running rake tasks.
161
+
162
+ ```bash
163
+ SHARD=shard1 rake db:setup && SHARD=shard2 rake db:setup
164
+ ```
165
+
134
166
  ### Rails Controllers
135
167
 
136
168
  If you want to send a specified action, or all actions from a controller, to a specific shard, use this syntax:
@@ -145,8 +177,8 @@ class ApplicationController < ActionController::Base
145
177
  end
146
178
  ```
147
179
 
148
- To see the complete list of features and syntax, please check out our <a href="http://wiki.github.com/tchandy/octopus/"> Wiki</a>
149
- Want to see sample rails applications using octopus features? please check it out: <a href="http://github.com/tchandy/octopus_sharding_example">Sharding Example</a> and <a href="http://github.com/tchandy/octopus_replication_example">Replication Example</a>. Also, we have an example that shows how to use Octopus without Rails: <a href="http://github.com/tchandy/octopus_sinatra"> Octopus + Sinatra Example</a>.
180
+ To see the complete list of features and syntax, please check out our <a href="http://wiki.github.com/thiagopradi/octopus/"> Wiki</a>
181
+ Want to see sample rails applications using octopus features? please check it out: <a href="http://github.com/thiagopradi/octopus_sharding_example">Sharding Example</a> and <a href="http://github.com/thiagopradi/octopus_replication_example">Replication Example</a>. Also, we have an example that shows how to use Octopus without Rails: <a href="http://github.com/thiagopradi/octopus_sinatra"> Octopus + Sinatra Example</a>.
150
182
 
151
183
  ## Mixing Octopus with the Rails multiple database model
152
184
  If you want to set a custom connection to a specific model, use the syntax `octopus_establish_connection` syntax:
@@ -174,13 +206,13 @@ CustomConnectedModel.using(:my_shard).first
174
206
  CustomConnectedModel.using(:some_other_shard).first
175
207
  ```
176
208
 
177
- This can be useful if you have a model that lives in a separate database and would like to add sharding or replication to it. For other use cases, you may be better off with <a href="https://github.com/tchandy/octopus/wiki/Slave-Groups">slave groups</a>.
209
+ This can be useful if you have a model that lives in a separate database and would like to add sharding or replication to it. For other use cases, you may be better off with <a href="https://github.com/thiagopradi/octopus/wiki/Slave-Groups">slave groups</a>.
178
210
 
179
211
  ## Contributing with Octopus
180
212
  Contributors are welcome! To run the test suite, you need mysql, postgresql and sqlite3 installed. This is what you need to setup your Octopus development environment:
181
213
 
182
214
  ```bash
183
- git clone http://github.com/tchandy/octopus.git
215
+ git clone http://github.com/thiagopradi/octopus.git
184
216
  cd octopus
185
217
  bundle install
186
218
  bundle exec rake db:prepare
@@ -200,7 +232,7 @@ cucumber
200
232
  If you are having issues running the octopus spec suite, verify your database users and passwords match those inside the config files and your permissions are correct.
201
233
 
202
234
  ## Contributors:
203
- - <a href="https://github.com/tchandy/octopus/contributors">All Contributors</a>
235
+ - <a href="https://github.com/thiagopradi/octopus/contributors">All Contributors</a>
204
236
 
205
237
  ## Mailing List:
206
238
  - <a href="http://groups.google.com/group/octopus-activerecord/">Octopus Mailing List</a>
data/Rakefile CHANGED
@@ -65,6 +65,7 @@ namespace :db do
65
65
  class BlankModel < ActiveRecord::Base; end
66
66
 
67
67
  BlankModel.using(shard_symbol).connection.initialize_schema_migrations_table
68
+ BlankModel.using(shard_symbol).connection.initialize_metadata_table if Octopus.atleast_rails50?
68
69
 
69
70
  BlankModel.using(shard_symbol).connection.create_table(:users) do |u|
70
71
  u.string :name
data/ar-octopus.gemspec CHANGED
@@ -21,22 +21,19 @@ Gem::Specification.new do |s|
21
21
  'Octopus now stores schema version information in each shard and migrations will not ' \
22
22
  'work properly unless this task is invoked.'
23
23
 
24
- s.add_dependency 'activerecord', '>= 3.2.0'
25
- s.add_dependency 'activesupport', '>= 3.2.0'
24
+ s.required_ruby_version = '>= 2.2.0'
25
+
26
+ s.add_dependency 'activerecord', '>= 4.2.0'
27
+ s.add_dependency 'activesupport', '>= 4.2.0'
26
28
 
27
29
  s.add_development_dependency 'appraisal', '>= 0.3.8'
28
- s.add_development_dependency 'mysql2', '> 0.3'
29
- s.add_development_dependency 'pg', '>= 0.11.0'
30
- s.add_development_dependency 'rake', '>= 0.8.7'
30
+ s.add_development_dependency 'mysql2', '>= 0.3.18', "< 0.5"
31
+ s.add_development_dependency 'pg', '~> 0.18'
32
+ s.add_development_dependency 'rake'
31
33
  s.add_development_dependency 'rspec', '>= 3'
32
34
  s.add_development_dependency 'rubocop'
33
- s.add_development_dependency 'sqlite3', '>= 1.3.4'
34
-
35
- if RUBY_VERSION < '2.0.0'
36
- s.add_development_dependency 'pry-debugger'
37
- else
38
- s.add_development_dependency 'pry-byebug'
39
- end
35
+ s.add_development_dependency 'sqlite3', '~> 1.3.6'
36
+ s.add_development_dependency 'pry-byebug'
40
37
 
41
38
  s.license = 'MIT'
42
39
  end
@@ -3,5 +3,5 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "activerecord", "~> 4.2.0"
6
-
7
- gemspec :path => "../"
6
+ gem "mysql2", "0.4.10"
7
+ gemspec path: "../"
@@ -2,6 +2,6 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "~> 3.2.0"
5
+ gem "activerecord", "~> 5.0.0"
6
6
 
7
- gemspec :path => "../"
7
+ gemspec path: "../"
@@ -2,6 +2,6 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "~> 4.0.0"
5
+ gem "activerecord", "~> 5.1.0"
6
6
 
7
- gemspec :path => "../"
7
+ gemspec path: "../"
@@ -2,6 +2,6 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "~> 4.1.0"
5
+ gem "activerecord", "~> 5.2.0"
6
6
 
7
- gemspec :path => "../"
7
+ gemspec path: "../"
@@ -2,9 +2,7 @@
2
2
  module Octopus
3
3
  module AbstractAdapter
4
4
  module OctopusShard
5
- parent = Octopus.rails3? ? ActiveSupport::BasicObject : ActiveSupport::ProxyObject
6
-
7
- class InstrumenterDecorator < parent
5
+ class InstrumenterDecorator < ActiveSupport::ProxyObject
8
6
  def initialize(adapter, instrumenter)
9
7
  @adapter = adapter
10
8
  @instrumenter = instrumenter
@@ -20,20 +18,16 @@ module Octopus
20
18
  end
21
19
  end
22
20
 
23
- def self.included(base)
24
- base.alias_method_chain :initialize, :octopus_shard
25
- end
26
-
27
21
  def octopus_shard
28
22
  @config[:octopus_shard]
29
23
  end
30
24
 
31
- def initialize_with_octopus_shard(*args)
32
- initialize_without_octopus_shard(*args)
25
+ def initialize(*args)
26
+ super
33
27
  @instrumenter = InstrumenterDecorator.new(self, @instrumenter)
34
28
  end
35
29
  end
36
30
  end
37
31
  end
38
32
 
39
- ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, Octopus::AbstractAdapter::OctopusShard)
33
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:prepend, Octopus::AbstractAdapter::OctopusShard)
@@ -2,6 +2,7 @@ module Octopus
2
2
  module Association
3
3
  def self.included(base)
4
4
  base.send(:include, Octopus::ShardTracking::Dynamic)
5
+ base.sharded_methods :target_scope
5
6
  end
6
7
 
7
8
  def current_shard
@@ -1,102 +1,72 @@
1
1
  module Octopus
2
2
  module AssociationShardTracking
3
- def self.extended(base)
4
- base.send(:include, InstanceMethods)
5
- end
3
+ class MismatchedShards < StandardError
4
+ attr_reader :record, :current_shard
6
5
 
7
- module QueryOnCurrentShard
8
- METHODS = %w(
9
- all
10
- average
11
- count
12
- empty?
13
- exists?
14
- find
15
- find_by_sql
16
- first
17
- last
18
- maximum
19
- minimum
20
- pluck
21
- scoping
22
- size
23
- sum
24
- to_a
25
- )
6
+ def initialize(record, current_shard)
7
+ @record = record
8
+ @current_shard = current_shard
9
+ end
26
10
 
27
- METHODS.each do |m|
28
- define_method m.to_sym do |*args, &block|
29
- if self.respond_to?(:proxy_association) && proxy_association
30
- proxy_association.owner.run_on_shard { super(*args, &block) }
31
- else
32
- super(*args, &block)
33
- end
34
- end
11
+ def message
12
+ [
13
+ "Association Error: Records are from different shards",
14
+ "Record: #{record.inspect}",
15
+ "Current Shard: #{current_shard.inspect}",
16
+ "Current Record Shard: #{record.current_shard.inspect}",
17
+ ].join(" ")
35
18
  end
36
19
  end
37
20
 
21
+ def self.extended(base)
22
+ base.send(:include, InstanceMethods)
23
+ end
24
+
38
25
  module InstanceMethods
39
26
  def connection_on_association=(record)
40
27
  return unless ::Octopus.enabled?
41
28
  return if !self.class.connection.respond_to?(:current_shard) || !self.respond_to?(:current_shard)
42
- if !record.current_shard.nil? && !current_shard.nil? && record.current_shard != current_shard
43
- fail 'Association Error: Records are from different shards'
29
+
30
+ if !record.current_shard.nil? && !current_shard.nil? && record.current_shard.to_s != current_shard.to_s
31
+ raise MismatchedShards.new(record, current_shard)
44
32
  end
45
33
 
46
34
  record.current_shard = self.class.connection.current_shard = current_shard if should_set_current_shard?
47
35
  end
48
36
  end
49
37
 
50
- if Octopus.rails4?
51
- def has_many(association_id, scope = nil, options = {}, &extension)
52
- if options == {} && scope.is_a?(Hash)
53
- default_octopus_opts(scope)
54
- else
55
- default_octopus_opts(options)
56
- end
57
- super
58
- end
59
- else
60
- def has_many(association_id, options = {}, &extension)
38
+ def has_many(name, scope = nil, **options, &extension)
39
+ if options == {} && scope.is_a?(Hash)
40
+ default_octopus_opts(scope)
41
+ else
61
42
  default_octopus_opts(options)
62
- super
63
43
  end
44
+ super
64
45
  end
65
46
 
66
- if Octopus.rails4?
67
- def has_and_belongs_to_many(association_id, scope = nil, options = {}, &extension)
68
- if options == {} && scope.is_a?(Hash)
69
- default_octopus_opts(scope)
70
- else
71
- default_octopus_opts(options)
72
- end
73
- super
74
- end
75
- else
76
- def has_and_belongs_to_many(association_id, options = {}, &extension)
77
- default_octopus_opts(options)
78
- super
79
- end
47
+ def has_and_belongs_to_many(association_id, scope = nil, options = {}, &extension)
48
+ assign_octopus_opts(scope, options)
49
+ super
80
50
  end
81
51
 
82
52
  def default_octopus_opts(options)
83
- if options[:before_add].is_a?(Array)
84
- options[:before_add] << :connection_on_association=
85
- elsif options[:before_add].is_a?(Symbol)
86
- options[:before_add] = [:connection_on_association=, options[:before_add]]
87
- else
88
- options[:before_add] = :connection_on_association=
89
- end
53
+ options[:before_add] = [ :connection_on_association=, options[:before_add] ].compact.flatten
54
+ options[:before_remove] = [ :connection_on_association=, options[:before_remove] ].compact.flatten
55
+ end
90
56
 
91
- if options[:before_remove].is_a?(Array)
92
- options[:before_remove] << :connection_on_association=
93
- elsif options[:before_remove].is_a?(Symbol)
94
- options[:before_remove] = [:connection_on_association=, options[:before_remove]]
57
+ def assign_octopus_opts(scope, options)
58
+ if options == {} && scope.is_a?(Hash)
59
+ default_octopus_opts(scope)
95
60
  else
96
- options[:before_remove] = :connection_on_association=
61
+ default_octopus_opts(options)
97
62
  end
63
+ end
98
64
 
99
- options[:extend] = [Octopus::AssociationShardTracking::QueryOnCurrentShard, options[:extend]].flatten.compact
65
+ if Octopus.atleast_rails51?
66
+ def has_and_belongs_to_many(association_id, scope = nil, **options, &extension)
67
+ assign_octopus_opts(scope, options)
68
+ super
69
+ end
100
70
  end
101
71
  end
102
72
  end
@@ -1,9 +1,15 @@
1
1
  module Octopus
2
2
  module CollectionAssociation
3
3
  def self.included(base)
4
- base.sharded_methods :reader, :writer, :ids_reader, :ids_writer, :create, :create!,
5
- :build, :any?, :count, :empty?, :first, :include?, :last, :length,
6
- :load_target, :many?, :reload, :size, :select, :uniq
4
+ if Octopus.rails51? || Octopus.rails52?
5
+ base.sharded_methods :reader, :writer, :ids_reader, :ids_writer, :create, :create!,
6
+ :build, :include?,
7
+ :load_target, :reload, :size, :select
8
+ else
9
+ base.sharded_methods :reader, :writer, :ids_reader, :ids_writer, :create, :create!,
10
+ :build, :any?, :count, :empty?, :first, :include?, :last, :length,
11
+ :load_target, :many?, :reload, :size, :select, :uniq
12
+ end
7
13
  end
8
14
  end
9
15
  end
@@ -0,0 +1,4 @@
1
+ module Octopus
2
+ class Exception < ::Exception
3
+ end
4
+ end
@@ -0,0 +1,8 @@
1
+ # find_nth / find_nth! must be public here to allow Octopus to call
2
+ # them on the scope proxy object
3
+ if Octopus.rails42? || Octopus.rails50?
4
+ module ActiveRecord::FinderMethods
5
+ public :find_nth
6
+ public :find_nth!
7
+ end
8
+ end
@@ -6,12 +6,13 @@ module Octopus
6
6
  module LoadBalancing
7
7
  class RoundRobin
8
8
  def initialize(slaves_list)
9
+ raise Octopus::Exception.new("No slaves available") if slaves_list.empty?
9
10
  @slaves_list = slaves_list
10
11
  @slave_index = 0
11
12
  end
12
13
 
13
14
  # Returns the next available slave in the pool
14
- def next
15
+ def next(options)
15
16
  @slaves_list[@slave_index = (@slave_index + 1) % @slaves_list.length]
16
17
  end
17
18
  end
@@ -3,8 +3,12 @@ module Octopus
3
3
  module LogSubscriber
4
4
  def self.included(base)
5
5
  base.send(:attr_accessor, :octopus_shard)
6
- base.alias_method_chain :sql, :octopus_shard
7
- base.alias_method_chain :debug, :octopus_shard
6
+
7
+ base.send :alias_method, :sql_without_octopus_shard, :sql
8
+ base.send :alias_method, :sql, :sql_with_octopus_shard
9
+
10
+ base.send :alias_method, :debug_without_octopus_shard, :debug
11
+ base.send :alias_method, :debug, :debug_with_octopus_shard
8
12
  end
9
13
 
10
14
  def sql_with_octopus_shard(event)