octoball 0.1.6.1 → 0.1.8
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 +4 -4
- data/.github/Gemfile +0 -5
- data/.github/gemfiles/rails_7.2.gemfile +6 -0
- data/.github/gemfiles/rails_8.0.gemfile +6 -0
- data/.github/gemfiles/rails_8.1.gemfile +6 -0
- data/.github/workflows/rspec.yml +4 -3
- data/README.md +17 -4
- data/Rakefile +6 -3
- data/lib/octoball/connection_adapters.rb +32 -0
- data/lib/octoball/log_subscriber.rb +1 -5
- data/lib/octoball/version.rb +1 -1
- data/lib/octoball.rb +0 -1
- data/octoball.gemspec +5 -5
- data/spec/spec_helper.rb +3 -2
- data/spec/support/database_connection.rb +3 -2
- data/spec/support/database_models.rb +1 -1
- metadata +13 -14
- data/lib/octoball/connection_handling.rb +0 -20
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 825cfa3ec263110a928737f918d0576459a50aa67b1f1553ab194248e0c0d41c
|
|
4
|
+
data.tar.gz: c20b43133b0afd7c324b4cd57a54204d7c716f85acb7aa55a950be80e2bcec02
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0002bc390d5982cb4a968ecdd91d12234a4d495553e906c0765efc09c29111b72fd136b15bd8fbd8a3b30b8b0f6d25fb9e6b41c5138ee777e791773a5117fcc6
|
|
7
|
+
data.tar.gz: 37efcbe5015be915ae7263a03f6da3443f03b6de711389ec59c76ada5fb0ea3a786c072c358a9f27573df1741ae978ef723930bb8ea4503fa80563669b13880e
|
data/.github/Gemfile
CHANGED
data/.github/workflows/rspec.yml
CHANGED
|
@@ -17,14 +17,15 @@ jobs:
|
|
|
17
17
|
strategy:
|
|
18
18
|
fail-fast: true
|
|
19
19
|
matrix:
|
|
20
|
-
ruby: ["2
|
|
20
|
+
ruby: ["3.2", "3.3", "3.4"]
|
|
21
|
+
rails: ["7.2", "8.0", "8.1"]
|
|
21
22
|
env:
|
|
22
|
-
BUNDLE_GEMFILE: .github/
|
|
23
|
+
BUNDLE_GEMFILE: .github/gemfiles/rails_${{ matrix.rails }}.gemfile
|
|
23
24
|
MYSQL_HOST: 127.0.0.1
|
|
24
25
|
RAILS_ENV: test
|
|
25
26
|
steps:
|
|
26
27
|
- name: Checkout
|
|
27
|
-
uses: actions/checkout@
|
|
28
|
+
uses: actions/checkout@v4
|
|
28
29
|
- name: Setup ruby
|
|
29
30
|
uses: ruby/setup-ruby@v1
|
|
30
31
|
with:
|
data/README.md
CHANGED
|
@@ -11,12 +11,16 @@ Currently, its implementation is focusing on horizontal database sharding. Howev
|
|
|
11
11
|
## Scope of this gem
|
|
12
12
|
|
|
13
13
|
### What is included in Octoball
|
|
14
|
+
|
|
14
15
|
- Octopus-like shard swithcing by `using` class method, e.g.:
|
|
16
|
+
|
|
15
17
|
```ruby
|
|
16
18
|
Octoball.using(:shard1) { User.find_by_name("Alice") }
|
|
17
19
|
User.using(:shard1).first
|
|
18
20
|
```
|
|
21
|
+
|
|
19
22
|
- Each model instance knows which shard it came from so shard will be switched automatically:
|
|
23
|
+
|
|
20
24
|
```ruby
|
|
21
25
|
user1 = User.using(:shard1).find_by_name("Bob")
|
|
22
26
|
user2 = User.using(:shard2).find_by_name("Charlie")
|
|
@@ -25,15 +29,18 @@ Currently, its implementation is focusing on horizontal database sharding. Howev
|
|
|
25
29
|
user1.save! # Save the user1 in the correct shard `:shard1`
|
|
26
30
|
user2.save! # Save the user2 in the correct shard `:shard2`
|
|
27
31
|
```
|
|
32
|
+
|
|
28
33
|
- Relations such as `has_many` are also resolved from the model instance's shard:
|
|
34
|
+
|
|
29
35
|
```ruby
|
|
30
36
|
user = User.using(:shard1).find_by_name("Alice")
|
|
31
37
|
user.blogs.where(title: "blog") # user's blogs are fetched from `:shard1`
|
|
32
38
|
```
|
|
33
39
|
|
|
34
40
|
### What is NOT included in Octoball
|
|
41
|
+
|
|
35
42
|
- Connection handling and configuration -- managed by the native `ActiveRecord::Base.connects_to` methods introduced in ActiveRecord 6.1.
|
|
36
|
-
- You need to migrate from Octopus' `config/shards.yml` to [Rails native multiple DB configuration using `config/database.yml`](https://edgeguides.rubyonrails.org/active_record_multiple_databases.html). Please refer the [Setup](#
|
|
43
|
+
- You need to migrate from Octopus' `config/shards.yml` to [Rails native multiple DB configuration using `config/database.yml`](https://edgeguides.rubyonrails.org/active_record_multiple_databases.html). Please refer the [Setup](#setup) section for more details.
|
|
37
44
|
- Migration -- done by ActiveRecord 6.1+ natively.
|
|
38
45
|
- Instead of `using` method in Octopus, you can specify the `migrations_paths` parameter in the `config/database.yml` file.
|
|
39
46
|
- Replication handling -- done by ActiveRecord's `role`
|
|
@@ -46,12 +53,13 @@ gem "octoball"
|
|
|
46
53
|
```
|
|
47
54
|
|
|
48
55
|
Define the database connections in `config/database.yml`, e.g.:
|
|
56
|
+
|
|
49
57
|
```
|
|
50
58
|
default: &default
|
|
51
|
-
adapter:
|
|
59
|
+
adapter: trilogy
|
|
52
60
|
pool: 5
|
|
53
61
|
username: root
|
|
54
|
-
host:
|
|
62
|
+
host: 127.0.0.1
|
|
55
63
|
timeout: 5000
|
|
56
64
|
connnect_timeout: 5000
|
|
57
65
|
|
|
@@ -63,7 +71,9 @@ development:
|
|
|
63
71
|
<<: *default
|
|
64
72
|
database: db_shard1
|
|
65
73
|
```
|
|
74
|
+
|
|
66
75
|
And define shards and corresponding connections in abstract ActiveRecord model class, e.g.:
|
|
76
|
+
|
|
67
77
|
```ruby
|
|
68
78
|
class ApplicationRecord < ActiveRecord::Base
|
|
69
79
|
self.abstract_class = true
|
|
@@ -80,21 +90,24 @@ end
|
|
|
80
90
|
```
|
|
81
91
|
|
|
82
92
|
Optionally, to use the `:master` shard as a default connection like Octopus, add the following script to `config/initializers/default_shard.rb`:
|
|
93
|
+
|
|
83
94
|
```
|
|
84
95
|
ActiveRecord::Base.default_shard = :master
|
|
85
96
|
```
|
|
86
97
|
|
|
87
|
-
|
|
88
98
|
## Development of Octoball
|
|
99
|
+
|
|
89
100
|
Octoball has rspec tests delived from subsets of Octopus' rspec.
|
|
90
101
|
|
|
91
102
|
To run the rspec tests, follow these steps:
|
|
103
|
+
|
|
92
104
|
```
|
|
93
105
|
RAILS_ENV=test bundle exec rake db:prepare
|
|
94
106
|
RAILS_ENV=test bundle exec rake spec
|
|
95
107
|
```
|
|
96
108
|
|
|
97
109
|
## License
|
|
110
|
+
|
|
98
111
|
Octoball is released under the MIT license.
|
|
99
112
|
|
|
100
113
|
Original Octopus' copyright: Copyright (c) Thiago Pradi
|
data/Rakefile
CHANGED
|
@@ -9,9 +9,10 @@ RuboCop::RakeTask.new
|
|
|
9
9
|
|
|
10
10
|
namespace :db do
|
|
11
11
|
mysql_spec = {
|
|
12
|
-
adapter: '
|
|
13
|
-
host: (ENV['MYSQL_HOST'] || '
|
|
12
|
+
adapter: 'trilogy',
|
|
13
|
+
host: (ENV['MYSQL_HOST'] || '127.0.0.1'),
|
|
14
14
|
username: (ENV['MYSQL_USER'] || 'root'),
|
|
15
|
+
port: (ENV['MYSQL_PORT'] || 3306),
|
|
15
16
|
encoding: 'utf8mb4',
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -31,6 +32,8 @@ namespace :db do
|
|
|
31
32
|
|
|
32
33
|
desc 'Create tables on tests databases'
|
|
33
34
|
task :create_tables do
|
|
35
|
+
require 'active_record'
|
|
36
|
+
|
|
34
37
|
ActiveRecord::Base.configurations = {
|
|
35
38
|
"test" => {
|
|
36
39
|
shard1: mysql_spec.merge(database: 'octoball_shard_1'),
|
|
@@ -43,7 +46,7 @@ namespace :db do
|
|
|
43
46
|
require './spec/models/application_record'
|
|
44
47
|
ActiveRecord::Base.configurations.configs_for(env_name: "test").each do |config|
|
|
45
48
|
ActiveRecord::Base.establish_connection(config)
|
|
46
|
-
schema_migration = ActiveRecord::Base.connection.schema_migration
|
|
49
|
+
schema_migration = ActiveRecord::Base.connection.pool.schema_migration
|
|
47
50
|
ActiveRecord::MigrationContext.new("spec/migration", schema_migration)
|
|
48
51
|
.migrate(config.database == 'octoball_shard_5' ? 2 : 1)
|
|
49
52
|
end
|
|
@@ -13,6 +13,38 @@ class Octoball
|
|
|
13
13
|
attr_accessor :current_shard
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
module ConnectionPoolSetCurrentShard
|
|
17
|
+
def with_connection(prevent_permanent_checkout: false)
|
|
18
|
+
lease = connection_lease
|
|
19
|
+
if lease.connection
|
|
20
|
+
lease.connection.current_shard = lease.connection.shard
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
super
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def active_connection?
|
|
27
|
+
conn = connection_lease.connection
|
|
28
|
+
conn.current_shard = conn.shard if conn
|
|
29
|
+
conn
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def active_connection
|
|
33
|
+
conn = connection_lease.connection
|
|
34
|
+
conn.current_shard = conn.shard if conn
|
|
35
|
+
conn
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def lease_connection
|
|
39
|
+
lease = connection_lease
|
|
40
|
+
lease.sticky = true
|
|
41
|
+
lease.connection ||= checkout
|
|
42
|
+
lease.connection.current_shard = lease.connection.shard
|
|
43
|
+
lease.connection
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
::ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(ConnectionPoolSetCurrentShard)
|
|
16
48
|
::ActiveRecord::ConnectionAdapters::ConnectionHandler.prepend(ConnectionHandlerSetCurrentShard)
|
|
17
49
|
::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ConnectionHasCurrentShard)
|
|
18
50
|
end
|
|
@@ -12,11 +12,7 @@ class Octoball
|
|
|
12
12
|
private
|
|
13
13
|
|
|
14
14
|
def debug(progname = nil, &block)
|
|
15
|
-
|
|
16
|
-
conn = current_shard ? color("[Shard: #{current_shard}]", ActiveSupport::LogSubscriber::GREEN, bold: true) : ''
|
|
17
|
-
else
|
|
18
|
-
conn = current_shard ? color("[Shard: #{current_shard}]", ActiveSupport::LogSubscriber::GREEN, true) : ''
|
|
19
|
-
end
|
|
15
|
+
conn = current_shard ? color("[Shard: #{current_shard}]", ActiveSupport::LogSubscriber::GREEN, bold: true) : ''
|
|
20
16
|
super(conn + progname.to_s, &block)
|
|
21
17
|
end
|
|
22
18
|
end
|
data/lib/octoball/version.rb
CHANGED
data/lib/octoball.rb
CHANGED
|
@@ -6,7 +6,6 @@ require 'octoball/version'
|
|
|
6
6
|
ActiveSupport.on_load(:active_record) do
|
|
7
7
|
require 'octoball/relation_proxy'
|
|
8
8
|
require 'octoball/connection_adapters'
|
|
9
|
-
require 'octoball/connection_handling'
|
|
10
9
|
require 'octoball/current_shard_tracker'
|
|
11
10
|
require 'octoball/association_shard_check'
|
|
12
11
|
require 'octoball/persistence'
|
data/octoball.gemspec
CHANGED
|
@@ -15,14 +15,14 @@ Gem::Specification.new do |s|
|
|
|
15
15
|
s.homepage = 'https://github.com/aktsk/octoball'
|
|
16
16
|
s.require_paths = ['lib']
|
|
17
17
|
|
|
18
|
-
s.required_ruby_version = '>= 2.
|
|
18
|
+
s.required_ruby_version = '>= 3.2.0'
|
|
19
19
|
|
|
20
|
-
s.add_dependency 'activerecord', '>=
|
|
21
|
-
s.add_dependency 'activesupport', '>=
|
|
20
|
+
s.add_dependency 'activerecord', '>= 7.2'
|
|
21
|
+
s.add_dependency 'activesupport', '>= 7.2'
|
|
22
22
|
|
|
23
|
-
s.add_development_dependency '
|
|
23
|
+
s.add_development_dependency 'trilogy'
|
|
24
24
|
s.add_development_dependency 'rake'
|
|
25
25
|
s.add_development_dependency 'rspec', '>= 3'
|
|
26
26
|
s.add_development_dependency 'rubocop'
|
|
27
|
-
s.add_development_dependency '
|
|
27
|
+
s.add_development_dependency 'debug'
|
|
28
28
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -10,9 +10,10 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each { |f| require f }
|
|
|
10
10
|
|
|
11
11
|
RSpec.configure do |config|
|
|
12
12
|
mysql_spec = {
|
|
13
|
-
adapter: '
|
|
14
|
-
host: (ENV['MYSQL_HOST'] || '
|
|
13
|
+
adapter: 'trilogy',
|
|
14
|
+
host: (ENV['MYSQL_HOST'] || '127.0.0.1'),
|
|
15
15
|
username: (ENV['MYSQL_USER'] || 'root'),
|
|
16
|
+
port: (ENV['MYSQL_PORT'] || 3306),
|
|
16
17
|
encoding: 'utf8mb4',
|
|
17
18
|
}
|
|
18
19
|
ActiveRecord::Base.configurations = {
|
|
@@ -3,9 +3,10 @@ require 'logger'
|
|
|
3
3
|
ActiveRecord::Base.logger = Logger.new(File.open('database.log', 'a'))
|
|
4
4
|
|
|
5
5
|
mysql_spec = {
|
|
6
|
-
adapter: '
|
|
7
|
-
host: (ENV['MYSQL_HOST'] || '
|
|
6
|
+
adapter: 'trilogy',
|
|
7
|
+
host: (ENV['MYSQL_HOST'] || '127.0.0.1'),
|
|
8
8
|
username: (ENV['MYSQL_USER'] || 'root'),
|
|
9
|
+
port: (ENV['MYSQL_PORT'] || 3306),
|
|
9
10
|
encoding: 'utf8mb4',
|
|
10
11
|
}
|
|
11
12
|
|
|
@@ -20,7 +20,7 @@ end
|
|
|
20
20
|
# This class sets its own connection
|
|
21
21
|
class CustomConnectionBase < ActiveRecord::Base
|
|
22
22
|
self.abstract_class = true
|
|
23
|
-
establish_connection(:adapter => '
|
|
23
|
+
establish_connection(:adapter => 'trilogy', :host => (ENV['MYSQL_HOST'] || '127.0.0.1'), :database => 'octoball_shard_2', :username => "#{ENV['MYSQL_USER'] || 'root'}", :password => '', :port => (ENV['MYSQL_PORT'] || 3306))
|
|
24
24
|
connects_to shards: {
|
|
25
25
|
custom_shard: { writing: :shard3 }
|
|
26
26
|
}
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: octoball
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tomoki Sekiyama
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: activerecord
|
|
@@ -16,30 +15,30 @@ dependencies:
|
|
|
16
15
|
requirements:
|
|
17
16
|
- - ">="
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
18
|
+
version: '7.2'
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
23
|
- - ">="
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
25
|
+
version: '7.2'
|
|
27
26
|
- !ruby/object:Gem::Dependency
|
|
28
27
|
name: activesupport
|
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
|
30
29
|
requirements:
|
|
31
30
|
- - ">="
|
|
32
31
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
32
|
+
version: '7.2'
|
|
34
33
|
type: :runtime
|
|
35
34
|
prerelease: false
|
|
36
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
36
|
requirements:
|
|
38
37
|
- - ">="
|
|
39
38
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
39
|
+
version: '7.2'
|
|
41
40
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
41
|
+
name: trilogy
|
|
43
42
|
requirement: !ruby/object:Gem::Requirement
|
|
44
43
|
requirements:
|
|
45
44
|
- - ">="
|
|
@@ -95,7 +94,7 @@ dependencies:
|
|
|
95
94
|
- !ruby/object:Gem::Version
|
|
96
95
|
version: '0'
|
|
97
96
|
- !ruby/object:Gem::Dependency
|
|
98
|
-
name:
|
|
97
|
+
name: debug
|
|
99
98
|
requirement: !ruby/object:Gem::Requirement
|
|
100
99
|
requirements:
|
|
101
100
|
- - ">="
|
|
@@ -117,6 +116,9 @@ extensions: []
|
|
|
117
116
|
extra_rdoc_files: []
|
|
118
117
|
files:
|
|
119
118
|
- ".github/Gemfile"
|
|
119
|
+
- ".github/gemfiles/rails_7.2.gemfile"
|
|
120
|
+
- ".github/gemfiles/rails_8.0.gemfile"
|
|
121
|
+
- ".github/gemfiles/rails_8.1.gemfile"
|
|
120
122
|
- ".github/workflows/rspec.yml"
|
|
121
123
|
- ".gitignore"
|
|
122
124
|
- Gemfile
|
|
@@ -126,7 +128,6 @@ files:
|
|
|
126
128
|
- lib/octoball/association.rb
|
|
127
129
|
- lib/octoball/association_shard_check.rb
|
|
128
130
|
- lib/octoball/connection_adapters.rb
|
|
129
|
-
- lib/octoball/connection_handling.rb
|
|
130
131
|
- lib/octoball/current_shard_tracker.rb
|
|
131
132
|
- lib/octoball/log_subscriber.rb
|
|
132
133
|
- lib/octoball/persistence.rb
|
|
@@ -154,7 +155,6 @@ homepage: https://github.com/aktsk/octoball
|
|
|
154
155
|
licenses:
|
|
155
156
|
- MIT
|
|
156
157
|
metadata: {}
|
|
157
|
-
post_install_message:
|
|
158
158
|
rdoc_options: []
|
|
159
159
|
require_paths:
|
|
160
160
|
- lib
|
|
@@ -162,15 +162,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
162
162
|
requirements:
|
|
163
163
|
- - ">="
|
|
164
164
|
- !ruby/object:Gem::Version
|
|
165
|
-
version: 2.
|
|
165
|
+
version: 3.2.0
|
|
166
166
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
167
|
requirements:
|
|
168
168
|
- - ">="
|
|
169
169
|
- !ruby/object:Gem::Version
|
|
170
170
|
version: '0'
|
|
171
171
|
requirements: []
|
|
172
|
-
rubygems_version: 3.
|
|
173
|
-
signing_key:
|
|
172
|
+
rubygems_version: 3.7.0.dev
|
|
174
173
|
specification_version: 4
|
|
175
174
|
summary: Octopus-like Database Sharding Helper for ActiveRecord 6.1+
|
|
176
175
|
test_files: []
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class Octoball
|
|
4
|
-
module ConnectionHandlingAvoidAutoLoadProxy
|
|
5
|
-
private
|
|
6
|
-
|
|
7
|
-
if ActiveRecord.gem_version < Gem::Version.new('7.1.0')
|
|
8
|
-
def swap_connection_handler(handler, &blk)
|
|
9
|
-
old_handler, ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handler, handler
|
|
10
|
-
return_value = yield
|
|
11
|
-
return_value.load if !return_value.respond_to?(:ar_relation) && return_value.is_a?(ActiveRecord::Relation)
|
|
12
|
-
return_value
|
|
13
|
-
ensure
|
|
14
|
-
ActiveRecord::Base.connection_handler = old_handler
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
::ActiveRecord::Base.extend(ConnectionHandlingAvoidAutoLoadProxy)
|
|
20
|
-
end
|