Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/gemfiles/rails_7.1.gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
source 'https://rubygems.org'

gemspec :path => '../../'

gem 'activerecord', '~> 7.1.0'
gem 'activesupport', '~> 7.1.0'
6 changes: 6 additions & 0 deletions .github/gemfiles/rails_7.2.gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
source 'https://rubygems.org'

gemspec :path => '../../'

gem 'activerecord', '~> 7.2.0'
gem 'activesupport', '~> 7.2.0'
6 changes: 6 additions & 0 deletions .github/gemfiles/rails_8.0.gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
source 'https://rubygems.org'

gemspec :path => '../../'

gem 'activerecord', '~> 8.0.0'
gem 'activesupport', '~> 8.0.0'
5 changes: 3 additions & 2 deletions .github/workflows/rspec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ jobs:
strategy:
fail-fast: true
matrix:
ruby: ["3.1","3.2","3.3"]
ruby: ["3.2","3.3", "3.4"]
rails: ["7.1", "7.2", "8.0"]
env:
BUNDLE_GEMFILE: .github/Gemfile
BUNDLE_GEMFILE: .github/gemfiles/rails_${{ matrix.rails }}.gemfile
MYSQL_HOST: 127.0.0.1
RAILS_ENV: test
steps:
Expand Down
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ Currently, its implementation is focusing on horizontal database sharding. Howev
## Scope of this gem

### What is included in Octoball

- Octopus-like shard swithcing by `using` class method, e.g.:

```ruby
Octoball.using(:shard1) { User.find_by_name("Alice") }
User.using(:shard1).first
```

- Each model instance knows which shard it came from so shard will be switched automatically:

```ruby
user1 = User.using(:shard1).find_by_name("Bob")
user2 = User.using(:shard2).find_by_name("Charlie")
Expand All @@ -25,15 +29,18 @@ Currently, its implementation is focusing on horizontal database sharding. Howev
user1.save! # Save the user1 in the correct shard `:shard1`
user2.save! # Save the user2 in the correct shard `:shard2`
```

- Relations such as `has_many` are also resolved from the model instance's shard:

```ruby
user = User.using(:shard1).find_by_name("Alice")
user.blogs.where(title: "blog") # user's blogs are fetched from `:shard1`
```

### What is NOT included in Octoball

- Connection handling and configuration -- managed by the native `ActiveRecord::Base.connects_to` methods introduced in ActiveRecord 6.1.
- 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.
- 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.
- Migration -- done by ActiveRecord 6.1+ natively.
- Instead of `using` method in Octopus, you can specify the `migrations_paths` parameter in the `config/database.yml` file.
- Replication handling -- done by ActiveRecord's `role`
Expand All @@ -46,12 +53,13 @@ gem "octoball"
```

Define the database connections in `config/database.yml`, e.g.:

```
default: &default
adapter: mysql2
adapter: trilogy
pool: 5
username: root
host: localhost
host: 127.0.0.1
timeout: 5000
connnect_timeout: 5000

Expand All @@ -63,7 +71,9 @@ development:
<<: *default
database: db_shard1
```

And define shards and corresponding connections in abstract ActiveRecord model class, e.g.:

```ruby
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
Expand All @@ -80,21 +90,24 @@ end
```

Optionally, to use the `:master` shard as a default connection like Octopus, add the following script to `config/initializers/default_shard.rb`:

```
ActiveRecord::Base.default_shard = :master
```


## Development of Octoball

Octoball has rspec tests delived from subsets of Octopus' rspec.

To run the rspec tests, follow these steps:

```
RAILS_ENV=test bundle exec rake db:prepare
RAILS_ENV=test bundle exec rake spec
```

## License

Octoball is released under the MIT license.

Original Octopus' copyright: Copyright (c) Thiago Pradi
7 changes: 5 additions & 2 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ RuboCop::RakeTask.new

namespace :db do
mysql_spec = {
adapter: 'mysql2',
host: (ENV['MYSQL_HOST'] || 'localhost'),
adapter: 'trilogy',
host: (ENV['MYSQL_HOST'] || '127.0.0.1'),
username: (ENV['MYSQL_USER'] || 'root'),
port: (ENV['MYSQL_PORT'] || 3306),
encoding: 'utf8mb4',
}

Expand All @@ -31,6 +32,8 @@ namespace :db do

desc 'Create tables on tests databases'
task :create_tables do
require 'active_record'

ActiveRecord::Base.configurations = {
"test" => {
shard1: mysql_spec.merge(database: 'octoball_shard_1'),
Expand Down
4 changes: 2 additions & 2 deletions octoball.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ Gem::Specification.new do |s|
s.homepage = 'https://github.com/aktsk/octoball'
s.require_paths = ['lib']

s.required_ruby_version = '>= 3.1.0'
s.required_ruby_version = '>= 3.2.0'

s.add_dependency 'activerecord', '>= 7.0'
s.add_dependency 'activesupport', '>= 7.0'

s.add_development_dependency 'mysql2'
s.add_development_dependency 'trilogy'
s.add_development_dependency 'rake'
s.add_development_dependency 'rspec', '>= 3'
s.add_development_dependency 'rubocop'
Expand Down
5 changes: 3 additions & 2 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@

RSpec.configure do |config|
mysql_spec = {
adapter: 'mysql2',
host: (ENV['MYSQL_HOST'] || 'localhost'),
adapter: 'trilogy',
host: (ENV['MYSQL_HOST'] || '127.0.0.1'),
username: (ENV['MYSQL_USER'] || 'root'),
port: (ENV['MYSQL_PORT'] || 3306),
encoding: 'utf8mb4',
}
ActiveRecord::Base.configurations = {
Expand Down
5 changes: 3 additions & 2 deletions spec/support/database_connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
ActiveRecord::Base.logger = Logger.new(File.open('database.log', 'a'))

mysql_spec = {
adapter: 'mysql2',
host: (ENV['MYSQL_HOST'] || 'localhost'),
adapter: 'trilogy',
host: (ENV['MYSQL_HOST'] || '127.0.0.1'),
username: (ENV['MYSQL_USER'] || 'root'),
port: (ENV['MYSQL_PORT'] || 3306),
encoding: 'utf8mb4',
}

Expand Down
2 changes: 1 addition & 1 deletion spec/support/database_models.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Client < ApplicationRecord
# This class sets its own connection
class CustomConnectionBase < ActiveRecord::Base
self.abstract_class = true
establish_connection(:adapter => 'mysql2', :host => (ENV['MYSQL_HOST'] || 'localhost'), :database => 'octoball_shard_2', :username => "#{ENV['MYSQL_USER'] || 'root'}", :password => '')
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))
connects_to shards: {
custom_shard: { writing: :shard3 }
}
Expand Down
Loading