active_record_shards 3.22.0 → 4.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 23d9e5008110efdba7c5d366557f94ec83bbba4c655157ae6b071494fc745097
4
- data.tar.gz: 30bd423589d89b1d268fade3127321c7a42f157571c2bf07e31137e4b96e8316
2
+ SHA1:
3
+ metadata.gz: 88819bbec3c2e39bc44c5f6172aa5bf9a3a7e097
4
+ data.tar.gz: bc407bce7e0f3ae6bc7bcf9041ac49cd6c8077a8
5
5
  SHA512:
6
- metadata.gz: 9454ca6c66e3a1c4073d18b81a550c1fa9f29243198f98645d711c01db8d09dec1cbbcb6228fe0e5f1f3971fc26b040624de97c2d6f7c3d97dd7011f6a622cff
7
- data.tar.gz: 0ad014733e6f2728303618a0845b626ff181b6768fc41bcc87ec9b31a52e919ec3c6874b755e69073b9c933af2a0a0c42b3e00a6ec8a4fe135f94a0a71b2f0aa
6
+ metadata.gz: e5f4fb85b1321bdb55edf8118ea944fb7fd1188a56350c87ebf2a560e5ad63e145da065eb9859532bc4bdb545b89ac22d8d65ec2810028fa995e84c88030ab6f
7
+ data.tar.gz: b8bc4ac010819230082d490c2e2850b80ce5ea5018d1f5323536894e58005d17cfb018b6757a339444864d71e76484c22d9517ca197211bf90db1229306e6777
data/README.md CHANGED
@@ -1,20 +1,11 @@
1
- [![Build Status](https://github.com/zendesk/active_record_shards/workflows/CI/badge.svg)](https://github.com/zendesk/active_record_shards/actions?query=workflow%3ACI)
1
+ [![Build Status](https://secure.travis-ci.org/zendesk/active_record_shards.png)](http://travis-ci.org/zendesk/active_record_shards)
2
2
 
3
3
  # ActiveRecord Shards
4
4
 
5
- ActiveRecord Shards is an extension for ActiveRecord that provides support for sharded database and replicas. Basically it is just a nice way to
5
+ ActiveRecord Shards is an extension for ActiveRecord that provides support for sharded database and slaves. Basically it is just a nice way to
6
6
  switch between database connections. We've made the implementation very small, and have tried not to reinvent any wheels already present in ActiveRecord.
7
7
 
8
- ActiveRecord Shards has been used and tested on Rails 4.2, 5.x and 6.0, and has in some form or another been used in production on large Rails apps for several years.
9
-
10
- - [Installation](#installation)
11
- - [Configuration](#configuration)
12
- - [Migrations](#migrations)
13
- - [Example](#example)
14
- - [Shared Model](#create-a-table-for-the-shared-not-sharded-model)
15
- - [Sharded Model](#create-a-table-for-the-sharded-model)
16
- - [Usage](#usage)
17
- - [Debugging](#debugging)
8
+ ActiveRecord Shards has been used and tested on Rails 5.0 and has in some form or another been used in production on a large Rails app for several years.
18
9
 
19
10
  ## Installation
20
11
 
@@ -24,143 +15,83 @@ and make sure to require 'active\_record\_shards' in some way.
24
15
 
25
16
  ## Configuration
26
17
 
27
- Add the replica and shard configuration to config/database.yml:
28
-
29
- ```yaml
30
- production:
31
- adapter: mysql
32
- encoding: utf8
33
- database: my_app_main
34
- pool: 5
35
- host: db1
36
- username: root
37
- password:
38
- replica:
39
- host: db1_replica
40
- shards:
41
- 1:
42
- host: db_shard1
43
- database: my_app_shard
44
- replica:
45
- host: db_shard1_replica
46
- 2:
47
- host: db_shard2
48
- database: my_app_shard
49
- replica:
50
- host: db_shard2_replica
51
- ```
18
+ Add the slave and shard configuration to config/database.yml:
19
+
20
+ production:
21
+ adapter: mysql
22
+ encoding: utf8
23
+ database: my_app_main
24
+ pool: 5
25
+ host: db1
26
+ username: root
27
+ password:
28
+ slave:
29
+ host: db1_slave
30
+ shards:
31
+ 1:
32
+ host: db_shard1
33
+ database: my_app_shard
34
+ slave:
35
+ host: db_shard1_slave
36
+ 2:
37
+ host: db_shard2
38
+ database: my_app_shard
39
+ slave:
40
+ host: db_shard2_slave
52
41
 
53
42
  basically connections inherit configuration from the parent configuration file.
54
43
 
55
- ## Migrations
56
-
57
- ActiveRecord Shards also patches migrations to support running migrations on a shared (not sharded) or a sharded database.
58
- Each migration class has to specify a shard spec indicating where to run the migration.
59
-
60
- Valid shard specs:
61
-
62
- * `:none` - Run this migration on the shared database, not any shards
63
- * `:all` - Run this migration on all of the shards, not the shared database
64
-
65
- #### Example
66
-
67
- ###### Create a table for the shared (not sharded) model
68
-
69
- ```ruby
70
- class CreateAccounts < ActiveRecord::Migration
71
- shard :none
72
-
73
- def change
74
- create_table :accounts do |t|
75
- # This is NOT necessary for the gem to work, we just use it in the examples below demonstrating one way to switch shards
76
- t.integer :shard_id, null: false
77
-
78
- t.string :name
79
- end
80
- end
81
- end
82
- ```
83
-
84
- ###### Create a table for the sharded model
85
-
86
- ```ruby
87
- class CreateProjects < ActiveRecord::Migration
88
- shard :all
89
-
90
- def change
91
- create_table :projects do |t|
92
- t.references :account
93
- t.string :name
94
- end
95
- end
96
- end
97
- ```
98
-
99
44
  ## Usage
100
45
 
101
46
  Normally you have some models that live on a shared database, and you might need to query this data in order to know what shard to switch to.
102
- All the models that live on the shared database must be marked as not\_sharded:
47
+ All the models that live on the sharded database must inherit from ActiveRecordShards::ShardedModel:
103
48
 
104
- ```ruby
105
- class Account < ActiveRecord::Base
106
- not_sharded
107
-
108
- has_many :projects
109
- end
49
+ class Account < ActiveRecord::Base
50
+ has_many :projects
51
+ end
110
52
 
111
- class Project < ActiveRecord::Base
112
- belongs_to :account
113
- end
114
- ```
53
+ class Project < ActiveRecordShards::ShardedModel
54
+ belongs_to :account
55
+ end
115
56
 
116
57
  So in this setup the accounts live on the shared database, but the projects are sharded. If accounts have a shard\_id column, you could lookup the account
117
58
  in a rack middleware and switch to the right shard:
118
59
 
119
- ```ruby
120
- class AccountMiddleware
121
- def initialize(app)
122
- @app = app
123
- end
124
-
125
- def call(env)
126
- account = lookup_account(env)
127
-
128
- if account
129
- ActiveRecord::Base.on_shard(account.shard_id) do
130
- @app.call(env)
60
+ class AccountMiddleware
61
+ def initialize(app)
62
+ @app = app
131
63
  end
132
- else
133
- @app.call(env)
134
- end
135
- end
136
64
 
137
- def lookup_account(env)
138
- # ...
139
- end
140
- end
141
- ```
65
+ def call(env)
66
+ account = lookup_account(env)
142
67
 
143
- You can switch to the replica databases at any point by wrapping your code in an on\_replica block:
68
+ if account
69
+ ActiveRecord::Base.on_shard(account.shard_id) do
70
+ @app.call(env)
71
+ end
72
+ else
73
+ @app.call(env)
74
+ end
75
+ end
144
76
 
145
- ```ruby
146
- ActiveRecord::Base.on_replica do
147
- Account.find_by_big_expensive_query
148
- end
149
- ```
77
+ def lookup_account(env)
78
+ ...
79
+ end
80
+ end
150
81
 
151
- This will perform the query on the replica, and mark the returned instances as read-only. There is also a shortcut for this:
82
+ You can switch to the slave databases at any point by wrapping your code in an on\_slave block:
152
83
 
153
- ```ruby
154
- Account.on_replica.find_by_big_expensive_query
155
- ```
84
+ ActiveRecord::Base.on_slave do
85
+ Account.find_by_big_expensive_query
86
+ end
156
87
 
157
- If you do not want instances returned from replicas to be marked as read-only, this can be disabled globally:
88
+ This will perform the query on the slave, and mark the returned instances as read only. There is also a shortcut for this:
158
89
 
159
- `ActiveRecordShards.disable_replica_readonly_records = true`
90
+ Account.on_slave.find_by_big_expensive_query
160
91
 
161
92
  ## Debugging
162
93
 
163
- Show if a query went to primary or replica in the logs:
94
+ Show if a query went to master or slave in the logs:
164
95
 
165
96
  ```Ruby
166
97
  require 'active_record_shards/sql_comments'
@@ -1,49 +1,40 @@
1
1
  # frozen_string_literal: true
2
-
3
2
  module ActiveRecordShards
4
3
  module AssociationCollectionConnectionSelection
5
- def on_replica_if(condition)
6
- condition ? on_replica : self
4
+ def on_slave_if(condition)
5
+ condition ? on_slave : self
7
6
  end
8
- alias_method :on_slave_if, :on_replica_if
9
7
 
10
- def on_replica_unless(condition)
11
- on_replica_if(!condition)
8
+ def on_slave_unless(condition)
9
+ on_slave_if(!condition)
12
10
  end
13
- alias_method :on_slave_unless, :on_replica_unless
14
11
 
15
- def on_primary_if(condition)
16
- condition ? on_primary : self
12
+ def on_master_if(condition)
13
+ condition ? on_master : self
17
14
  end
18
- alias_method :on_master_if, :on_primary_if
19
15
 
20
- def on_primary_unless(condition)
21
- on_primary_if(!condition)
16
+ def on_master_unless(condition)
17
+ on_master_if(!condition)
22
18
  end
23
- alias_method :on_master_unless, :on_primary_unless
24
19
 
25
- def on_replica
26
- PrimaryReplicaProxy.new(self, :replica)
20
+ def on_slave
21
+ MasterSlaveProxy.new(self, :slave)
27
22
  end
28
- alias_method :on_slave, :on_replica
29
23
 
30
- def on_primary
31
- PrimaryReplicaProxy.new(self, :primary)
24
+ def on_master
25
+ MasterSlaveProxy.new(self, :master)
32
26
  end
33
- alias_method :on_master, :on_primary
34
27
 
35
- class PrimaryReplicaProxy
28
+ class MasterSlaveProxy
36
29
  def initialize(association_collection, which)
37
30
  @association_collection = association_collection
38
31
  @which = which
39
32
  end
40
33
 
41
- def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
34
+ def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissing
42
35
  reflection = @association_collection.proxy_association.reflection
43
36
  reflection.klass.on_cx_switch_block(@which) { @association_collection.send(method, *args, &block) }
44
37
  end
45
38
  end
46
-
47
- MasterSlaveProxy = PrimaryReplicaProxy
48
39
  end
49
40
  end
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
-
3
2
  require 'active_support/core_ext'
4
3
 
5
4
  module ActiveRecordShards
@@ -7,7 +6,7 @@ module ActiveRecordShards
7
6
  module_function
8
7
 
9
8
  def explode(conf)
10
- conf = conf.to_h.deep_dup
9
+ conf = conf.deep_dup
11
10
 
12
11
  conf.to_a.each do |env_name, env_config|
13
12
  next unless shards = env_config.delete('shards')
@@ -24,31 +23,10 @@ module ActiveRecordShards
24
23
  end
25
24
 
26
25
  conf.to_a.each do |env_name, env_config|
27
- if replica_conf = env_config.delete('replica')
28
- expand_child!(env_config, replica_conf)
29
- conf["#{env_name}_replica"] = replica_conf
30
- end
31
-
32
- # rubocop:disable Style/Next
33
- if legacy_replica_conf = env_config.delete('slave')
34
- ActiveRecordShards::Deprecation.warn('`slave` configuration keys should be replaced with `replica` keys!')
35
- expand_child!(env_config, legacy_replica_conf)
36
- conf["#{env_name}_replica"] = legacy_replica_conf
26
+ if slave_conf = env_config.delete('slave')
27
+ expand_child!(env_config, slave_conf)
28
+ conf["#{env_name}_slave"] = slave_conf
37
29
  end
38
- # rubocop:enable Style/Next
39
- end
40
-
41
- conf
42
- end
43
-
44
- def replace_slave_keys(conf)
45
- conf.to_a.each do |env_name, env_config|
46
- next unless env_name.end_with?("_slave")
47
-
48
- replica_key = env_name.sub(/_slave$/, "_replica")
49
- next if conf.key?(replica_key)
50
-
51
- conf[replica_key] = env_config.deep_dup
52
30
  end
53
31
 
54
32
  conf
@@ -56,16 +34,14 @@ module ActiveRecordShards
56
34
 
57
35
  def expand_child!(parent, child)
58
36
  parent.each do |key, value|
59
- unless ['slave', 'replica', 'shards'].include?(key) || value.is_a?(Hash)
37
+ unless ['slave', 'shards'].include?(key) || value.is_a?(Hash)
60
38
  child[key] ||= value
61
39
  end
62
40
  end
63
41
  end
64
42
 
65
43
  def configurations_with_shard_explosion=(conf)
66
- exploded_configuration = explode(conf)
67
- configuration_with_slave_keys_replaced = replace_slave_keys(exploded_configuration)
68
- self.configurations_without_shard_explosion = configuration_with_slave_keys_replaced
44
+ self.configurations_without_shard_explosion = explode(conf)
69
45
  end
70
46
 
71
47
  def self.extended(base)
@@ -1,15 +1,5 @@
1
1
  module ActiveRecordShards
2
2
  module ConnectionSwitcher
3
- def connection_specification_name
4
- name = current_shard_selection.resolve_connection_name(sharded: is_sharded?, configurations: configurations)
5
-
6
- unless configurations[name] || name == "primary"
7
- raise ActiveRecord::AdapterNotSpecified, "No database defined by #{name} in your database config. (configurations: #{configurations.to_h.keys.inspect})"
8
- end
9
-
10
- name
11
- end
12
-
13
3
  private
14
4
 
15
5
  def ensure_shard_connection
@@ -1,15 +1,5 @@
1
1
  module ActiveRecordShards
2
2
  module ConnectionSwitcher
3
- def connection_specification_name
4
- name = current_shard_selection.resolve_connection_name(sharded: is_sharded?, configurations: configurations)
5
-
6
- unless configurations[name] || name == "primary"
7
- raise ActiveRecord::AdapterNotSpecified, "No database defined by #{name} in your database config. (configurations: #{configurations.to_h.keys.inspect})"
8
- end
9
-
10
- name
11
- end
12
-
13
3
  private
14
4
 
15
5
  def ensure_shard_connection