simple-sharding 0.1.0
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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +33 -0
- data/lib/simple/sharding.rb +11 -0
- data/lib/simple/sharding/active_record_extensions.rb +68 -0
- data/lib/simple/sharding/config.rb +18 -0
- data/lib/simple/sharding/connection_handler.rb +72 -0
- data/lib/simple/sharding/core.rb +43 -0
- data/lib/simple/sharding/shard_thread_registry.rb +61 -0
- data/lib/simple/sharding/version.rb +5 -0
- data/lib/tasks/simple/sharding_tasks.rake +4 -0
- metadata +83 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 439cde292a9f44fc9ddbfcd2a24e264e421cf2e3
|
|
4
|
+
data.tar.gz: 3a9cc11ad9a77f0f45a5d2d7b755f2114d9a4b25
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 7383af6ebf3e7601e667bad83e071e6338953c89aefeda42dd19500511faba12fcfb2df824df9b86b53130635508669de35dd05b4c4baedd4730b311483fabc1
|
|
7
|
+
data.tar.gz: 230d741a452b438f419e3397bd0ea34324d60133e4d946c5216ccf2cb1c5f134db45f32c93e12a0b45d0fd906b3005f41bb934144379319883869b317f65fe2d
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2017 qiqidone
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Simple::Sharding
|
|
2
|
+
Short description and motivation.
|
|
3
|
+
|
|
4
|
+
## Usage
|
|
5
|
+
How to use my plugin.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
Add this line to your application's Gemfile:
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
gem 'simple-sharding'
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
And then execute:
|
|
15
|
+
```bash
|
|
16
|
+
$ bundle
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
```bash
|
|
21
|
+
$ gem install simple-sharding
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Contributing
|
|
25
|
+
Contribution directions go here.
|
|
26
|
+
|
|
27
|
+
## License
|
|
28
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'bundler/setup'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
require 'rdoc/task'
|
|
8
|
+
|
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
11
|
+
rdoc.title = 'Simple::Sharding'
|
|
12
|
+
rdoc.options << '--line-numbers'
|
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
require 'bundler/gem_tasks'
|
|
23
|
+
|
|
24
|
+
require 'rake/testtask'
|
|
25
|
+
|
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
|
27
|
+
t.libs << 'test'
|
|
28
|
+
t.pattern = 'test/**/*_test.rb'
|
|
29
|
+
t.verbose = false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
task default: :test
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Simple::Sharding
|
|
2
|
+
module ActiveRecordExtensions
|
|
3
|
+
def self.extend_active_record_scope
|
|
4
|
+
# return if exist
|
|
5
|
+
return if ActiveRecord::Base.respond_to?(:sharding)
|
|
6
|
+
|
|
7
|
+
# include sharding in class and instance
|
|
8
|
+
ActiveRecord::Base.extend ScopeMethods
|
|
9
|
+
ActiveRecord::Base.include ScopeMethods
|
|
10
|
+
ActiveRecord::extend CaseFixer
|
|
11
|
+
|
|
12
|
+
# Includes scope method in scopes
|
|
13
|
+
ActiveRecord::Relation.include ScopeMethods
|
|
14
|
+
ActiveRecord::Relation.extend CaseFixer
|
|
15
|
+
|
|
16
|
+
# Includes scope method in has_many and habtm relations
|
|
17
|
+
ActiveRecord::Scoping.include ScopeMethods
|
|
18
|
+
ActiveRecord::Scoping.extend CaseFixer
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
module ScopeMethods
|
|
23
|
+
def sharding(shard_id)
|
|
24
|
+
raise 'Error for sharding block.' if block_given?
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Return value of the #sharding scope method. Allows us to chain the shard
|
|
31
|
+
# choice with other ActiveRecord scopes
|
|
32
|
+
class ScopeProxy
|
|
33
|
+
attr_accessor :original_scope
|
|
34
|
+
|
|
35
|
+
def initialize(shard_id, original_scope)
|
|
36
|
+
@shard_id = shard_id
|
|
37
|
+
@original_scope = original_scope
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def sharding(shard_id)
|
|
41
|
+
@shard_id = shard_id
|
|
42
|
+
self
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def method_missing(method, *args, &block)
|
|
46
|
+
res = Core.sharding(@shard_id) do
|
|
47
|
+
@original_scope.send(method, *args, &block)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# if result is still a scope (responds to to_sql), update original scope
|
|
51
|
+
# and return proxy to continue chaining
|
|
52
|
+
if res.respond_to?(:to_sql)
|
|
53
|
+
@original_scope = res
|
|
54
|
+
return self
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# return res
|
|
58
|
+
res
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Delegates == to method_missing so that User.sharding(:id).where(:name => "Mike")
|
|
62
|
+
# gets run in the correct shard context when #== is evaluated.
|
|
63
|
+
def ==(other)
|
|
64
|
+
method_missing(:==, other)
|
|
65
|
+
end
|
|
66
|
+
alias_method :eql?, :==
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Simple::Sharding
|
|
2
|
+
class Config
|
|
3
|
+
DEFAULT_CONFIG = {
|
|
4
|
+
shard_config_file: 'config/shard.yml'
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
# initialize config
|
|
8
|
+
DEFAULT_CONFIG.each do |config_name, default_value|
|
|
9
|
+
self.cattr_accessor config_name, instance_accessor: false
|
|
10
|
+
self.send(config_name.to_s + '=', default_value)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# methods
|
|
14
|
+
def self.config(name)
|
|
15
|
+
DEFAULT_CONFIG[name]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module Simple::Sharding
|
|
2
|
+
class ConnectionHandler
|
|
3
|
+
# check
|
|
4
|
+
def check(shard_id)
|
|
5
|
+
shard_id.present?
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# establish all connection
|
|
9
|
+
def connect_all
|
|
10
|
+
Core.config.keys.each do |shard_id|
|
|
11
|
+
connect(shard_id)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def connect(shard_id)
|
|
16
|
+
resolver = ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(Config.config(shard_id))
|
|
17
|
+
connect_spec = resolver.spec(shard_id.to_sym)
|
|
18
|
+
connect_spec
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.setup
|
|
22
|
+
@@connection_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
|
23
|
+
@@connection_pool_owners = {}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.connection_handler
|
|
27
|
+
raise 'Shards::ConnectionHandler was not setup' unless defined? @@connection_handler
|
|
28
|
+
@@connection_handler
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.connection_pool(shard_id)
|
|
32
|
+
connection_handler.retrieve_connection_pool(connection_pool_owner(shard_id))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.retrie_connection(shard_id)
|
|
36
|
+
connection_handler.retrieve_connection(connection_pool_owner(shard_id))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.connected?(shard_id)
|
|
40
|
+
connection_handler.connected?(connection_pool_owner(shard_id))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.remove_connection(shard_id)
|
|
44
|
+
connection_handler.remove_connection(connection_pool_owner(shard_id))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.connection_pool_owner(shard_id)
|
|
48
|
+
@@connection_pool_owners[shard_id] ||= ConnectionPoolOwner.new(shard_id)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Connection pool need a class to own each connection
|
|
52
|
+
class ConnectionPoolOwner
|
|
53
|
+
attr_reader :name
|
|
54
|
+
|
|
55
|
+
def initialize(name)
|
|
56
|
+
@name = name
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Safeguard in case pool cannot be retrieved for owner. This makes the error clear
|
|
60
|
+
def superclass
|
|
61
|
+
raise Exception, "ConnectionPool could not be retrieved for #{self}. "
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# in case owner ends up printed by rails in an error message when retrieving connection
|
|
65
|
+
def to_s
|
|
66
|
+
"ConnectionPoolOwner with name #{self.name}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'simple/sharding/active_record_extensions'
|
|
2
|
+
require 'simple/sharding/config'
|
|
3
|
+
require 'simple/sharding/connection_handler'
|
|
4
|
+
require 'simple/sharding/shard_thread_registry'
|
|
5
|
+
|
|
6
|
+
module Simple::Sharding
|
|
7
|
+
class Core
|
|
8
|
+
# block support
|
|
9
|
+
def self.sharding(shard_id)
|
|
10
|
+
begin
|
|
11
|
+
raise "no shard group #{shard_id} found in pool." if ConnectionHandler.check(shard_id)
|
|
12
|
+
ShardThreadRegistry.push_current_shard(shard_id)
|
|
13
|
+
yield
|
|
14
|
+
ensure
|
|
15
|
+
# Releases connections in case user left some connection in the reserved state
|
|
16
|
+
# (by calling retrieve_connection instead of with_connection). Also, using
|
|
17
|
+
# normal activerecord queries leaves a connection in the reserved state
|
|
18
|
+
# Obs: don't do this with a master database connection
|
|
19
|
+
ConnectionHandler.connection_pool(shard_id).release_connection if !shard_id.blank?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# configuration
|
|
25
|
+
def self.config(env=Rails.env)
|
|
26
|
+
@@db_config ||= YAML.load_file(Config.config(:shard_config_file))
|
|
27
|
+
env_config = @@db_config[env]
|
|
28
|
+
return env_config if env_config
|
|
29
|
+
|
|
30
|
+
# error
|
|
31
|
+
raise "No correct config for env: #{env}."
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def setup
|
|
35
|
+
if block_given?
|
|
36
|
+
yield Config
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
ConnectionHandler.connect_all
|
|
40
|
+
ActiveRecordExtensions.extend_active_record_scope
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'active_support/per_thread_registry'
|
|
2
|
+
|
|
3
|
+
module Simple::Sharding
|
|
4
|
+
class ShardThreadRegistry
|
|
5
|
+
extend ActiveSupport::PerThreadRegistry
|
|
6
|
+
attr_accessor :_current_shard_id
|
|
7
|
+
|
|
8
|
+
# Creates two thread-specific stacks to store the shard of connection
|
|
9
|
+
# The top of the stack indicates the current connection
|
|
10
|
+
# This allows us to have nested blocks of #using_shard and keep track of the
|
|
11
|
+
# connections as we open/close those blocks
|
|
12
|
+
thread_mattr_accessor :_shard_id_stack
|
|
13
|
+
# auxiliary stack that keeps track of wether each shard connection was used
|
|
14
|
+
# inside its respective using_shard block (so we can print an alert if not)
|
|
15
|
+
thread_mattr_accessor :_shard_connection_used_stack
|
|
16
|
+
|
|
17
|
+
def self.shard_id_stack; self._shard_id_stack ||= [] end;
|
|
18
|
+
def self.shard_connection_used_stack; self._shard_connection_used_stack ||= [] end;
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Returns the current shard id (for the current Thread)
|
|
22
|
+
def self.current_shard_id
|
|
23
|
+
shard_id_stack.last
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def self.connect_to_master?
|
|
28
|
+
_current_shard_id.nil?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.current_shard_id
|
|
32
|
+
_current_shard_id
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.current_shard_id=(shard_id)
|
|
36
|
+
self._current_shard_id = shard_id.blank? ? nil : shard_id.to_sym
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# adds shard connection to the stack
|
|
40
|
+
def self.push_current_shard(shard_id)
|
|
41
|
+
# this line supresses the unused connection warning when there are nested
|
|
42
|
+
# using_shard blocks. We suppress the warning because we view nested using_shard
|
|
43
|
+
# blocks as a override
|
|
44
|
+
notify_connection_retrieved
|
|
45
|
+
shard_id_stack.push(shard_id.blank? ? nil : shard_id.to_sym)
|
|
46
|
+
shard_connection_used_stack.push(false)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# notifies the current connection was used (wee keep track of this to warn
|
|
51
|
+
# the user in case the connection is not used)
|
|
52
|
+
def self.notify_connection_retrieved
|
|
53
|
+
shard_connection_used_stack[-1] = true if shard_connection_used_stack.present?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# removes shard connection to the stack
|
|
57
|
+
def self.pop_current_shard
|
|
58
|
+
[shard_id_stack.pop, shard_connection_used_stack.pop]
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: simple-sharding
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- qiqidone
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2017-11-14 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rails
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 5.1.3
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 5.1.3
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: sqlite3
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
description: Description of Simple::Sharding.
|
|
42
|
+
email:
|
|
43
|
+
- qiqidone@gmail.com
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- MIT-LICENSE
|
|
49
|
+
- README.md
|
|
50
|
+
- Rakefile
|
|
51
|
+
- lib/simple/sharding.rb
|
|
52
|
+
- lib/simple/sharding/active_record_extensions.rb
|
|
53
|
+
- lib/simple/sharding/config.rb
|
|
54
|
+
- lib/simple/sharding/connection_handler.rb
|
|
55
|
+
- lib/simple/sharding/core.rb
|
|
56
|
+
- lib/simple/sharding/shard_thread_registry.rb
|
|
57
|
+
- lib/simple/sharding/version.rb
|
|
58
|
+
- lib/tasks/simple/sharding_tasks.rake
|
|
59
|
+
homepage: http://baidu.com
|
|
60
|
+
licenses:
|
|
61
|
+
- MIT
|
|
62
|
+
metadata: {}
|
|
63
|
+
post_install_message:
|
|
64
|
+
rdoc_options: []
|
|
65
|
+
require_paths:
|
|
66
|
+
- lib
|
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
68
|
+
requirements:
|
|
69
|
+
- - ">="
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
version: '0'
|
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
|
+
requirements:
|
|
74
|
+
- - ">="
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '0'
|
|
77
|
+
requirements: []
|
|
78
|
+
rubyforge_project:
|
|
79
|
+
rubygems_version: 2.6.10
|
|
80
|
+
signing_key:
|
|
81
|
+
specification_version: 4
|
|
82
|
+
summary: Simple::Sharding to shard data.
|
|
83
|
+
test_files: []
|