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.
@@ -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
@@ -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.
@@ -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).
@@ -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,11 @@
1
+ require 'rails'
2
+ require 'simple/sharding/version'
3
+ require 'simple/sharding/core'
4
+
5
+ module Simple
6
+ module Sharding
7
+ def self.method_missing(method_sym, *arguments, &block)
8
+ Core.send(method_sym, *arguments, &block)
9
+ end
10
+ end
11
+ end
@@ -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
@@ -0,0 +1,5 @@
1
+ module Simple
2
+ module Sharding
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :simple_sharding do
3
+ # # Task goes here
4
+ # 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: []