rails-sharding 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +28 -2
- data/Rakefile +3 -7
- data/lib/generators/templates/shards.yml.example +1 -1
- data/lib/rails/sharding/config.rb +10 -1
- data/lib/rails/sharding/connection_handler.rb +41 -2
- data/lib/rails/sharding/core.rb +11 -10
- data/lib/rails/sharding/shard_thread_registry.rb +45 -19
- data/lib/rails/sharding/shardable_model.rb +4 -2
- data/lib/rails/sharding/version.rb +1 -1
- data/rails-sharding.gemspec +2 -2
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 58f0c90d4f03a5f3aa3d4ff363cdac165c948f8f
|
4
|
+
data.tar.gz: d927face94cf2c001534c1353b322294ab1984b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa192b584a7ef8e58be570798c8d35ae04f17134791ed325a272f32dc5c32d3d847e60d06894da35093736d7c29f52bba32414b4d27b6d5107e2390e86c43221
|
7
|
+
data.tar.gz: cd329c1e3473f0f8a11f93bfc15bdd5278ebb041b8fccf6d5e2b7b496fa229919914b4080e4d97913e61ad090c6b67dfdf2a9c22e729cace4129e4f63e92bf95
|
data/README.md
CHANGED
@@ -4,8 +4,9 @@
|
|
4
4
|
[![Code Climate](https://codeclimate.com/github/hsgubert/rails-sharding/badges/gpa.svg)](https://codeclimate.com/github/hsgubert/rails-sharding)
|
5
5
|
[![Test Coverage](https://codeclimate.com/github/hsgubert/rails-sharding/badges/coverage.svg)](https://codeclimate.com/github/hsgubert/rails-sharding/coverage)
|
6
6
|
[![Gem Version](https://badge.fury.io/rb/rails-sharding.svg)](https://badge.fury.io/rb/rails-sharding)
|
7
|
+
[![Dependency Status](https://gemnasium.com/badges/github.com/hsgubert/rails-sharding.svg)](https://gemnasium.com/github.com/hsgubert/rails-sharding)
|
7
8
|
|
8
|
-
Simple and robust sharding for Rails, including Migrations and ActiveRecord extensions
|
9
|
+
Simple and robust sharding gem for Rails, including Migrations and ActiveRecord extensions
|
9
10
|
|
10
11
|
This gems allows you to easily create extra databases to your rails application, and freely allocate ActiveRecord instances to any of the databases. It also provides rake tasks and migrations to help you manage the schema by shard groups.
|
11
12
|
|
@@ -27,8 +28,10 @@ You can also use the block syntax, where all your queries inside will be directe
|
|
27
28
|
You can also pick and choose which models will be shardable, so that all the models that are not shardable will still be retrieved from the master database, even if inside a using_shard block.
|
28
29
|
|
29
30
|
## Compatibility
|
31
|
+
Gem version 1.x.x -> compatible with Rails 5.0
|
32
|
+
|
30
33
|
Gem version 0.1.1 -> compatible with Rails 4.2
|
31
|
-
|
34
|
+
|
32
35
|
|
33
36
|
## Installation
|
34
37
|
|
@@ -141,6 +144,29 @@ rake shards:migrate SHARD_GROUP=shard_group_1
|
|
141
144
|
rake shards:migrate SHARD_GROUP=shard_group_1 SHARD=shard1
|
142
145
|
```
|
143
146
|
|
147
|
+
## Gem Options
|
148
|
+
Running the `rails g rails_sharding:scaffold` will create an initializer at `config/initializers/rails-sharding.rb`. You can pass additional configurations on this initializer to control the gem behavior. You can see below all available options and their default values:
|
149
|
+
```ruby
|
150
|
+
# config/initializers/rails-sharding.rb
|
151
|
+
|
152
|
+
Rails::Sharding.setup do |config|
|
153
|
+
# If true one connection will be established per shard (in every shard group) on startup.
|
154
|
+
# If false the user must call Shards::ConnectionHandler.establish_connection(shard_group, shard_name) manually at least once before using each shard.
|
155
|
+
config.establish_all_connections_on_setup = true
|
156
|
+
|
157
|
+
# If true the method #using_shard will be mixed in ActiveRecord scopes. Put this to false if you don't want the gem to modify ActiveRecord
|
158
|
+
config.extend_active_record_scope = true,
|
159
|
+
|
160
|
+
# Specifies where to find the definition of the shards configurations
|
161
|
+
config.shards_config_file = 'config/shards.yml',
|
162
|
+
|
163
|
+
# Specifies where to find the migrations for each shard group
|
164
|
+
config.shards_migrations_dir = 'db/shards_migrations',
|
165
|
+
|
166
|
+
# Specifies where to find the schemas for each shard group
|
167
|
+
config.shards_schemas_dir = 'db/shards_schemas'
|
168
|
+
end
|
169
|
+
```
|
144
170
|
|
145
171
|
## Development and Contributing
|
146
172
|
|
data/Rakefile
CHANGED
@@ -6,7 +6,8 @@ RSpec::Core::RakeTask.new(:spec)
|
|
6
6
|
task :default => :spec
|
7
7
|
|
8
8
|
# defines an environment task so we can run rake tasks from lib/tasks/rails-sharding.rake.
|
9
|
-
#
|
9
|
+
# The tasks on rails-sharding.rake depend on the :environment task, which is usuallu defined
|
10
|
+
# by rails. In our case, we just stub it so the rake tasks run
|
10
11
|
task :environment do
|
11
12
|
# do nothing
|
12
13
|
end
|
@@ -16,12 +17,7 @@ namespace :db do
|
|
16
17
|
|
17
18
|
desc 'Loads gem test environment and rake tasks from gem'
|
18
19
|
task :load_env do
|
19
|
-
require '
|
20
|
-
|
21
|
-
# requires spec helper but ensures no test coverage is reported to codeclimate
|
22
|
-
ENV['CODECLIMATE_REPO_TOKEN'] = nil
|
23
|
-
require './spec/spec_helper'
|
24
|
-
|
20
|
+
require './spec/load_gem_test_env'
|
25
21
|
load 'lib/tasks/rails-sharding.rake'
|
26
22
|
end
|
27
23
|
|
@@ -13,6 +13,16 @@ module Rails::Sharding
|
|
13
13
|
# this to false if you don't want the gem to modify ActiveRecord
|
14
14
|
extend_active_record_scope: true,
|
15
15
|
|
16
|
+
# If true the query logs of ActiveRecord will be tagged with the corresponding
|
17
|
+
# shard you're querying
|
18
|
+
add_shard_tag_to_query_logs: true,
|
19
|
+
|
20
|
+
# If true a warning will be printed everytime a using_shard block ends without
|
21
|
+
# the shard connection being retrieved at least once inside the block. This warning
|
22
|
+
# is helpful to remember the developer to include ShardableModel module on the
|
23
|
+
# sharded models, otherwise they will always connect to the master database.
|
24
|
+
no_connection_retrieved_warning: true,
|
25
|
+
|
16
26
|
# Specifies where to find the definition of the shards configurations
|
17
27
|
shards_config_file: 'config/shards.yml',
|
18
28
|
|
@@ -27,6 +37,5 @@ module Rails::Sharding
|
|
27
37
|
self.cattr_accessor config_name
|
28
38
|
self.send(config_name.to_s + '=', default_value)
|
29
39
|
end
|
30
|
-
|
31
40
|
end
|
32
41
|
end
|
@@ -49,7 +49,14 @@ module Rails::Sharding
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def self.retrieve_connection(shard_group, shard_name)
|
52
|
-
|
52
|
+
connection_name = connection_name(shard_group, shard_name)
|
53
|
+
connection = connection_handler.retrieve_connection(connection_name)
|
54
|
+
|
55
|
+
if connection && Config.add_shard_tag_to_query_logs
|
56
|
+
add_shard_tag_to_connection_log(connection, connection_name)
|
57
|
+
else
|
58
|
+
connection
|
59
|
+
end
|
53
60
|
end
|
54
61
|
|
55
62
|
def self.connected?(shard_group, shard_name)
|
@@ -57,7 +64,13 @@ module Rails::Sharding
|
|
57
64
|
end
|
58
65
|
|
59
66
|
def self.with_connection(shard_group, shard_name, &block)
|
60
|
-
connection_pool(shard_group, shard_name).with_connection
|
67
|
+
connection_pool(shard_group, shard_name).with_connection do |connection|
|
68
|
+
if connection && Config.add_shard_tag_to_query_logs
|
69
|
+
connection_name = connection_name(shard_group, shard_name)
|
70
|
+
add_shard_tag_to_connection_log(connection, connection_name)
|
71
|
+
end
|
72
|
+
block.call(connection)
|
73
|
+
end
|
61
74
|
end
|
62
75
|
|
63
76
|
def self.remove_connection(shard_group, shard_name)
|
@@ -79,5 +92,31 @@ module Rails::Sharding
|
|
79
92
|
def self.connection_name(shard_group, shard_name)
|
80
93
|
shard_group.to_s + ':' + shard_name.to_s
|
81
94
|
end
|
95
|
+
|
96
|
+
# Adds a shard tag to the log of all queries executed through this connection
|
97
|
+
def self.add_shard_tag_to_connection_log(connection, shard_tag)
|
98
|
+
# avoids modifing connection twice
|
99
|
+
if connection.respond_to? :shard_tag
|
100
|
+
connection.shard_tag = shard_tag
|
101
|
+
return connection
|
102
|
+
end
|
103
|
+
|
104
|
+
# creates #shard_tag attribute in connection
|
105
|
+
connection.singleton_class.send(:attr_accessor, :shard_tag)
|
106
|
+
connection.shard_tag = shard_tag
|
107
|
+
|
108
|
+
# create an alias #original_execute, as a copy of the #execute for this connection
|
109
|
+
connection.singleton_class.send(:alias_method, :original_execute, :execute)
|
110
|
+
|
111
|
+
# defines a new #execute that adds a tag to the log
|
112
|
+
class << connection
|
113
|
+
def execute(sql, name=nil)
|
114
|
+
name = (name.to_s + " (#{shard_tag})").strip
|
115
|
+
self.original_execute(sql, name)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
connection
|
120
|
+
end
|
82
121
|
end
|
83
122
|
end
|
data/lib/rails/sharding/core.rb
CHANGED
@@ -10,28 +10,29 @@ module Rails::Sharding
|
|
10
10
|
|
11
11
|
# Opens a block where all queries will be directed to the selected shard
|
12
12
|
def self.using_shard(shard_group, shard_name)
|
13
|
-
|
14
|
-
|
15
|
-
ShardThreadRegistry.current_shard_group = shard_group
|
16
|
-
ShardThreadRegistry.current_shard_name = shard_name
|
17
|
-
ShardThreadRegistry.shard_connection_used = false
|
13
|
+
ShardThreadRegistry.push_current_shard(shard_group, shard_name)
|
18
14
|
yield
|
19
15
|
ensure
|
16
|
+
shard_group, shard_name, connection_used = ShardThreadRegistry.pop_current_shard
|
17
|
+
|
20
18
|
# shows warning to user
|
21
|
-
if !
|
22
|
-
puts "Warning: no connection to shard '#{
|
19
|
+
if Config.no_connection_retrieved_warning && !connection_used
|
20
|
+
puts "Warning: no connection to shard '#{shard_group}:#{shard_name}' was retrieved inside the using_shard block. Make sure you don't forget to include Rails::Sharding::ShardableModel to the models you want to be sharded. Disable this warning with Rails::Sharding::Config.no_connection_retrieved_warning = false."
|
23
21
|
end
|
24
22
|
|
25
23
|
# Releases connections in case user left some connection in the reserved state
|
26
24
|
# (by calling retrieve_connection instead of with_connection). Also, using
|
27
25
|
# normal activerecord queries leaves a connection in the reserved state
|
28
|
-
|
29
|
-
|
26
|
+
# Obs: don't do this with a master database connection
|
27
|
+
ConnectionHandler.connection_pool(shard_group, shard_name).release_connection if shard_group && shard_name
|
30
28
|
end
|
31
29
|
|
32
30
|
def self.configurations(environment=Rails.env)
|
33
31
|
@@db_configs ||= YAML.load_file(Config.shards_config_file)
|
34
|
-
@@db_configs[environment]
|
32
|
+
environment_config = @@db_configs[environment]
|
33
|
+
return environment_config if environment_config
|
34
|
+
|
35
|
+
raise Errors::ConfigNotFoundError, 'Found no shard configurations for enviroment "' + environment + '" in ' + Config.shards_config_file.to_s + ' file was not found'
|
35
36
|
rescue Errno::ENOENT
|
36
37
|
raise Errors::ConfigNotFoundError, Config.shards_config_file.to_s + ' file was not found'
|
37
38
|
end
|
@@ -1,13 +1,21 @@
|
|
1
1
|
|
2
2
|
module Rails::Sharding
|
3
3
|
class ShardThreadRegistry
|
4
|
-
#
|
5
|
-
|
6
|
-
|
4
|
+
# Creates two thread-specific stacks to store the shard of connection
|
5
|
+
# The top of the stack indicates the current connection
|
6
|
+
# This allows us to have nested blocks of #using_shard and keep track of the
|
7
|
+
# connections as we open/close those blocks
|
8
|
+
thread_mattr_accessor :_shard_group_stack
|
9
|
+
thread_mattr_accessor :_shard_name_stack
|
7
10
|
|
8
|
-
# auxiliary
|
9
|
-
# using_shard block (so we can print an alert if not)
|
10
|
-
thread_mattr_accessor :
|
11
|
+
# auxiliary stack that keeps track of wether each shard connection was used
|
12
|
+
# inside its respective using_shard block (so we can print an alert if not)
|
13
|
+
thread_mattr_accessor :_shard_connection_used_stack
|
14
|
+
|
15
|
+
# accessors that initialize stacks if necessary
|
16
|
+
def self.shard_group_stack; self._shard_group_stack ||= [] end;
|
17
|
+
def self.shard_name_stack; self._shard_name_stack ||= [] end;
|
18
|
+
def self.shard_connection_used_stack; self._shard_connection_used_stack ||= [] end;
|
11
19
|
|
12
20
|
def self.connecting_to_master?
|
13
21
|
current_shard_group.nil? || current_shard_name.nil?
|
@@ -17,30 +25,48 @@ module Rails::Sharding
|
|
17
25
|
!connecting_to_master?
|
18
26
|
end
|
19
27
|
|
28
|
+
# Clears the connection stack and goes back to connecting to master
|
20
29
|
def self.connect_back_to_master!
|
21
|
-
|
22
|
-
|
23
|
-
|
30
|
+
shard_group_stack.clear
|
31
|
+
shard_name_stack.clear
|
32
|
+
shard_connection_used_stack.clear
|
24
33
|
end
|
25
34
|
|
26
35
|
# Returns the current shard group (for the current Thread)
|
27
36
|
def self.current_shard_group
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
# Sets the current shard group (for the current Thread)
|
32
|
-
def self.current_shard_group=(group)
|
33
|
-
self._current_shard_group = group.blank? ? nil : group.to_sym
|
37
|
+
shard_group_stack.last
|
34
38
|
end
|
35
39
|
|
36
40
|
# Returns the current shard name (for the current Thread)
|
37
41
|
def self.current_shard_name
|
38
|
-
|
42
|
+
shard_name_stack.last
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.current_connection_used?
|
46
|
+
shard_connection_used_stack.last
|
47
|
+
end
|
48
|
+
|
49
|
+
# adds shard connection to the stack
|
50
|
+
def self.push_current_shard(group, name)
|
51
|
+
# this line supresses the unused connection warning when there are nested
|
52
|
+
# using_shard blocks. We suppress the warning because we view nested using_shard
|
53
|
+
# blocks as a override
|
54
|
+
notify_connection_retrieved
|
55
|
+
|
56
|
+
shard_group_stack.push(group.blank? ? nil : group.to_sym)
|
57
|
+
shard_name_stack.push(name.blank? ? nil : name.to_sym)
|
58
|
+
shard_connection_used_stack.push(false)
|
59
|
+
end
|
60
|
+
|
61
|
+
# notifies the current connection was used (wee keep track of this to warn
|
62
|
+
# the user in case the connection is not used)
|
63
|
+
def self.notify_connection_retrieved
|
64
|
+
shard_connection_used_stack[-1] = true if shard_connection_used_stack.present?
|
39
65
|
end
|
40
66
|
|
41
|
-
#
|
42
|
-
def self.
|
43
|
-
|
67
|
+
# removes shard connection to the stack
|
68
|
+
def self.pop_current_shard
|
69
|
+
[shard_group_stack.pop, shard_name_stack.pop, shard_connection_used_stack.pop]
|
44
70
|
end
|
45
71
|
|
46
72
|
def self.current_shard_group_and_name
|
@@ -32,20 +32,22 @@ module Rails::Sharding
|
|
32
32
|
|
33
33
|
# @overrides ActiveRecord::ConnectionHandling#connection_pool
|
34
34
|
def sharded_connection_pool
|
35
|
+
ShardThreadRegistry.notify_connection_retrieved
|
36
|
+
|
35
37
|
if ShardThreadRegistry.connecting_to_master?
|
36
38
|
return original_connection_pool
|
37
39
|
else
|
38
|
-
ShardThreadRegistry.shard_connection_used = true # records that shard connection was used at least once
|
39
40
|
return ConnectionHandler.connection_pool(*ShardThreadRegistry.current_shard_group_and_name)
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
43
44
|
# @overrides ActiveRecord::ConnectionHandling#retrieve_connection
|
44
45
|
def sharded_retrieve_connection
|
46
|
+
ShardThreadRegistry.notify_connection_retrieved
|
47
|
+
|
45
48
|
if ShardThreadRegistry.connecting_to_master?
|
46
49
|
return original_retrieve_connection
|
47
50
|
else
|
48
|
-
ShardThreadRegistry.shard_connection_used = true # records that shard connection was used at least once
|
49
51
|
return ConnectionHandler.retrieve_connection(*ShardThreadRegistry.current_shard_group_and_name)
|
50
52
|
end
|
51
53
|
end
|
data/rails-sharding.gemspec
CHANGED
@@ -26,9 +26,9 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_runtime_dependency 'rails', '~> 5.0'
|
27
27
|
|
28
28
|
spec.add_development_dependency "bundler", "~> 1.12"
|
29
|
-
spec.add_development_dependency "rake", "~>
|
29
|
+
spec.add_development_dependency "rake", "~> 11.0"
|
30
30
|
spec.add_development_dependency "rspec", "~> 3.0"
|
31
|
-
spec.add_development_dependency "byebug", '~>
|
31
|
+
spec.add_development_dependency "byebug", '~> 9'
|
32
32
|
spec.add_development_dependency "mysql2", '~> 0'
|
33
33
|
spec.add_development_dependency "codeclimate-test-reporter", '~> 0'
|
34
34
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails-sharding
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Henrique Gubert
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '11.0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '11.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,14 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '9'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '9'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: mysql2
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|